准备环境
安装包:
pyVim==0.0.21
pyvmomi==6.7.1
命令:pip install pyVim==0.0.21 pyvmomi==6.7.1
代码
# -*- coding: utf-8 -*-
import traceback
from pyVim.connect import SmartConnectNoSSL, Disconnect
from pyVmomi import vim, vmodl
class VmManage(object):
def __init__(self, host, user, password, port, ssl):
self.config = None
self.host = host
self.user = user
self.pwd = password
self.port = port
self.sslContext = ssl
try:
self.client = SmartConnectNoSSL(host=host,
user=user,
pwd=password,
port=443
)
self.content = self.client.RetrieveContent()
self.result = True
except Exception as e:
self.result = e
def _get_all_objs(self, obj_type, folder=None):
"""
根据对象类型获取这一类型的所有对象
"""
if folder is None:
container = self.content.viewManager.CreateContainerView(self.content.rootFolder, obj_type, True)
else:
container = self.content.viewManager.CreateContainerView(folder, obj_type, True)
return container.view
def _get_obj(self, obj_type, name):
"""
根据对象类型和名称来获取具体对象
"""
obj = None
content = self.client.RetrieveContent()
container = content.viewManager.CreateContainerView(content.rootFolder, obj_type, True)
for c in container.view:
if c.name == name:
obj = c
break
return obj
def get_datacenters(self):
"""
获取数据中心列表
"""
return self._get_all_objs([vim.Datacenter])
# vcenter执行动作等待
def wait_for_task(self, task):
""" wait for a vCenter task to finish """
task_done = False
while not task_done:
print "task.....%s " % task.info.state
if task.info.state == 'success':
return {'message': u'执行成功', 'status': True}
if task.info.state == 'error':
print "there was an error"
return {'message': task.info.error.msg, 'status': True}
def handleTask(self, tasks=None):
if tasks is None:
return False
else:
from pyVim.task import WaitForTasks
try:
WaitForTasks(tasks=tasks, si=self.client)
except Exception as e:
traceback.print_exc()
print str(e)
return False
def get_cluster_by_name(self, name=None, datacenter=None):
if datacenter:
folder = datacenter.hostFolder
else:
folder = self.content.rootFolder
container = self.content.viewManager.CreateContainerView(folder, [vim.ClusterComputeResource], True)
clusters = container.view
for cluster in clusters:
if cluster.name == name:
return cluster
return None
def get_datastore_by_name(self, name, datacenter):
datastores = datacenter.datastore
for datastore in datastores:
if datastore.name == name:
return datastore
return None
def get_host_by_name(self, name, datastore):
hosts = datastore.host
for host in hosts:
if host.key.summary.config.name == name:
return host.key
return None
def get_vms_by_cluster(self, vmFolder):
content = self.client.content
objView = content.viewManager.CreateContainerView(vmFolder, [vim.VirtualMachine], True)
vmList = objView.view
objView.Destroy()
return vmList
def get_customspec(self, vm_ip=None, vm_subnetmask=None, vm_gateway=None, vm_dns=None,
vm_domain=None, vm_hostname=None):
# guest NIC settings 有关dns和域名的配置错误 更改了
adaptermaps = []
guest_map = vim.vm.customization.AdapterMapping()
guest_map.adapter = vim.vm.customization.IPSettings()
guest_map.adapter.ip = vim.vm.customization.FixedIp()
guest_map.adapter.ip.ipAddress = vm_ip
guest_map.adapter.subnetMask = vm_subnetmask
guest_map.adapter.gateway = vm_gateway
if vm_domain:
guest_map.adapter.dnsDomain = vm_domain
adaptermaps.append(guest_map)
# DNS settings
globalip = vim.vm.customization.GlobalIPSettings()
if vm_dns:
globalip.dnsServerList = [vm_dns]
globalip.dnsSuffixList = vm_domain
# Hostname settings
ident = vim.vm.customization.LinuxPrep()
if vm_domain:
ident.domain = vm_domain
ident.hostName = vim.vm.customization.FixedName()
if vm_hostname:
ident.hostName.name = vm_hostname
customspec = vim.vm.customization.Specification()
customspec.nicSettingMap = adaptermaps
customspec.globalIPSettings = globalip
customspec.identity = ident
return customspec
def clone(self, template_name, vm_name, datacenter_name,
datastore_name, cluster_name, host_name=None,
cup_num=None, memory=None, vm_ip=None, vm_subnetmask=None,
vm_gateway=None, vm_dns, vm_domain=None,
vm_hostname=None):
# 获取模版
template = self._get_obj([vim.VirtualMachine], template_name)
# 模版不存在
if template is None:
return {'message': u'克隆失败: 模版不存在', 'result': False}
# 选择克隆的虚拟机存放位置,通过数据中心获取对象
datacenter = self._get_obj([vim.Datacenter], datacenter_name)
# 数据中心不存在
if datacenter is None:
return {'message': u'克隆失败: 数据中心不存在', 'result': False}
vmfolder = datacenter.vmFolder
# 获取存储
if datastore_name:
datastore = self.get_datastore_by_name(datastore_name, datacenter)
if datastore is None:
return {'message': u'克隆失败: 该数据中心下%s存储不存在' % datastore_name, 'result': False}
else:
datastore = self.get_datastore_by_name(template.datastore[0].info.name, datacenter)
if datastore is None:
return {'message': u'克隆失败: 该数据中心下%s模版不存在' % template_name, 'result': False}
# 获取集群
cluster = self.get_cluster_by_name(cluster_name, datacenter)
if cluster is None:
return {'message': u'克隆失败: 该数据中心下%s集群不存在' % cluster_name, 'result': False}
vms = self.get_vms_by_cluster(cluster)
vms_name = [i.name for i in vms]
if vm_name in vms_name:
return {'message': u'克隆失败: 虚拟机%s已经存在' % vm_name, 'result': False}
resource_pool = cluster.resourcePool
relospec = vim.vm.RelocateSpec()
relospec.datastore = datastore
relospec.pool = resource_pool
# 获取主机
if host_name:
host = self.get_host_by_name(host_name, datastore)
if host is None:
return {'message': u'克隆失败: 该存储下%s主机不存在' % host_name, 'result': False}
else:
relospec.host = host
clonespec = vim.vm.CloneSpec()
clonespec.location = relospec
clonespec.powerOn = True
# 设置cpu和内存
if all([vm_ip, vm_subnetmask, vm_gateway, vm_domain]):
clonespec.customization = self.get_customspec(vm_ip, vm_subnetmask, vm_gateway, vm_domain, vm_dns, vm_hostname)
vmconf = vim.vm.ConfigSpec()
if cup_num:
vmconf.numCPUs = cup_num
if memory:
vmconf.memoryMB = memory
if vmconf is not None:
clonespec.config = vmconf
print "cloning VM..."
task = template.Clone(folder=vmfolder, name=vm_name, spec=clonespec)
result = self.wait_for_task(task)
if result['status']:
data = {'message': u'克隆成功', 'result': True}
else:
data = {'message': u'克隆失败: %s' % result['message'], 'result': False}
return data
if __name__ == '__main__':
ip = 'xxx.xxx.xxx.xxx'
user = 'xxxx'
password = 'xxx'
port = 443
template_name = u'模板-演示环境-CentOS74-1qaz!QAZ-100G'
vm_name = u'20-xuwei_testxxx2222'
data_center_name = u'测试数据中心'
datastore_name = None
cluster_name = u'测试集群'
host_name = None
cup_num = None
memory = None
resource_pool = None
power_on = True
vm_ip = '192.168.10.11'
vm_subnetmask = '255.255.255.0'
vm_gateway = '192.168.10.1'
vm_dns = 'xxx.xxx.xxx.xxx'
vm_domain = 'baidu.com'
vm_hostname = 'xuwei'
vm = VmManage(host=ip,
user=user,
password=password,
port=port, ssl=None)
data = vm.clone(
template_name=template_name,
vm_name=vm_name,
datacenter_name=data_center_name,
cluster_name=cluster_name,
datastore_name=datastore_name,
host_name=host_name,
cup_num=cup_num,
memory=memory,
vm_ip=vm_ip,
vm_subnetmask=vm_subnetmask,
vm_gateway=vm_gateway,
vm_dns=vm_dns,
vm_domain=vm_domain,
vm_hostname=vm_hostname
)
克隆虚拟机是依赖于虚拟机模板,在虚拟机模板正常的情况下,以上代码能够完成克隆虚拟机并指定IP的操作的,那么先来说一说克隆虚拟机的坑。
现象1:虚拟机克隆成功,虚拟机自动正常开机,但没有指定上网络IP。
现象产生的原因:
克隆使用的模板没有网络适配器
现象2:虚拟机克隆成功,但是没有自动开机,并且指定网络IP也失败了,在 vSphere上还报了ident.hostName.name异常。
现象产生的原因:
①虚拟机主机名:不可以用下划线等特殊字符
②虚拟机主机名为空
现象3:虚拟机克隆成功,虚拟机自动正常开机,网络指定成功,但虚拟机网络不通,无法正常使用网络。
现象产生的原因:
指定的虚拟机IP和虚拟机所在的网络适配器的网络段不匹配,导致网络不通。
虚拟机克隆成功后,如果由于虚拟机IP和网络适配器的网络段不匹配,可以对网络段进行修改,也就是修改vlan网段。
def edit_nic(self, vm, network_name, is_vss):
device_change = []
data = {'message': None, 'result': False, 'code': 1}
for device in vm.config.hardware.device:
if isinstance(device, vim.vm.device.VirtualEthernetCard):
nicspec = vim.vm.device.VirtualDeviceSpec()
nicspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit
nicspec.device = device
nicspec.device.wakeOnLanEnabled = True
if is_vss:
nicspec.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
nicspec.device.backing.network = self._get_obj([vim.Network], network_name)
nicspec.device.backing.deviceName = network_name
else:
network = self._get_obj([vim.dvs.DistributedVirtualPortgroup], network_name)
dvs_port_connection = vim.dvs.PortConnection()
dvs_port_connection.portgroupKey = network.key
dvs_port_connection.switchUuid = network.config.distributedVirtualSwitch.uuid
nicspec.device.backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo()
nicspec.device.backing.port = dvs_port_connection
nicspec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
nicspec.device.connectable.startConnected = True
nicspec.device.connectable.allowGuestControl = True
device_change.append(nicspec)
nicspec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
nicspec.device.connectable.startConnected = True
nicspec.device.connectable.allowGuestControl = True
device_change.append(nicspec)
break
if device_change:
config_spec = vim.vm.ConfigSpec(deviceChange=device_change)
task = vm.ReconfigVM_Task(config_spec)
result = self.wait_for_task(task)
if result['status']:
power_state = vm.runtime.powerState
if power_state == 'poweredOn':
stop_task = vm.PowerOff()
stop_result = self.wait_for_task(stop_task)
# if stop_result['status']:
# start_task = vm.PowerOn()
# self.wait_for_task(start_task)
# else:
start_task = vm.PowerOn()
self.wait_for_task(start_task)
data["message"] = u'更改网络成功'
data["result"] = True
data["code"] = 0
else:
data["message"] = u'更改网络失败'
else:
data["message"] = u'网络适配器不存在,无法修改网络'
return data
这里的修改分为vds和vss类型网段,vss指的是标准交换机,而vds指的是分布式交换机,两者的修改网段方式不同。
补充:当克隆虚拟机,没有集群,层级:数据中心–>文件夹–>主机
def clone(self, template_name, vm_name, datacenter_name,
datastore_name, vm_exi_ip, vm_folder=None,
cup_num=None, memory=None, vm_disk=None, vm_ip=None,
vm_subnetmask=None, vm_gateway=None, vm_dns=None,
vm_domain=None, vm_hostname=None):
# 获取模版
template = self._get_obj([vim.VirtualMachine], template_name)
# 模版不存在
if template is None:
return {'message': u'克隆失败: 模版不存在', 'result': False}
# 选择克隆的虚拟机存放位置,通过数据中心获取对象
datacenter = self._get_obj([vim.Datacenter], datacenter_name)
# 数据中心不存在
if datacenter is None:
return {'message': u'克隆失败: 数据中心不存在', 'result': False}
# vm创建路径
if vm_folder:
vmfolder = self._get_obj([vim.Folder], vm_folder)
else:
vmfolder = datacenter.vmFolder
# 获取存储
if datastore_name:
datastore = self.get_datastore_by_name(datastore_name, datacenter)
if datastore is None:
return {'message': u'克隆失败: 该数据中心下%s存储不存在' % datastore_name, 'result': False}
else:
datastore = self.get_datastore_by_name(template.datastore[0].info.name, datacenter)
if datastore is None:
return {'message': u'克隆失败: 该数据中心下%s模版不存在' % template_name, 'result': False}
# 获取宿主机
host = self.get_host_by_name(vm_exi_ip, datastore)
if host is None:
return {'message': u'克隆失败: 该存储下%s主机不存在' % vm_exi_ip, 'result': False}
# 获取宿主机下的vm
vms = host.vm
for vm in vms:
config = vm.summary.config
if vm_name == config.name:
return {'message': u'克隆失败: 虚拟机%s已经存在' % vm_name, 'result': False}
# 获取资源池
resourcepool = host.parent.resourcePool
relospec = vim.vm.RelocateSpec()
relospec.datastore = datastore
relospec.pool = resourcepool
relospec.host = host
# 配置Clone属性
clonespec = vim.vm.CloneSpec()
clonespec.location = relospec
clonespec.powerOn = True
device_change = []
# 设置网卡
if len(template.network) == 0:
logger.info(u'设置网卡')
nic_change = self.add_nic('VM Network')
device_change.extend(nic_change)
# 修改硬盘大小
if vm_disk:
logger.info(u'追加硬盘')
# disk_change = self.add_disk(template, vm_disk)
# if type(disk_change) is list:
# device_change.extend(disk_change)
# else:
# return {'message': disk_change, 'result': False}
disk_change = self.change_disk_size(template, vm_disk)
if type(disk_change) is list:
device_change.extend(disk_change)
else:
return {'message': disk_change, 'result': False}
# 更新配置
vmconf = vim.vm.ConfigSpec(deviceChange=device_change)
logger.info(u'更新网卡网卡的配置')
# 设置IP
if all([vm_ip, vm_subnetmask, vm_gateway]):
clonespec.customization = self.get_customspec(vm_ip, vm_subnetmask, vm_gateway, vm_dns, vm_domain,
vm_hostname)
logger.info(u'设置IP')
# 更改cpu和内存
if cup_num:
vmconf.numCPUs = cup_num
if memory:
vmconf.memoryMB = memory * 1024
if vmconf is not None:
clonespec.config = vmconf
# 开始克隆
task = template.Clone(folder=vmfolder, name=vm_name, spec=clonespec)
vm_task = {
'task': task,
'vm_name': vm_name,
'vm_ip': vm_ip,
'vm_exi_ip': vm_exi_ip
}
data = {'message': u'任务下发成功', 'result': True, 'data': vm_task}
return data