Prepare the environment
Installation package:
pyVim==0.0.21
pyvmomi==6.7.1
命令:pip install pyVim==0.0.21 pyvmomi==6.7.1
Code
# -*- 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
)
Cloning a virtual machine depends on the virtual machine template. When the virtual machine template is normal, the above code can complete the operation of cloning the virtual machine and specifying the IP, so let's talk about the pit of cloning the virtual machine first.
Phenomenon 1: The virtual machine is successfully cloned, and the virtual machine automatically starts normally, but the network IP is not specified.
现象产生的原因:
克隆使用的模板没有网络适配器
Phenomenon 2: The virtual machine was cloned successfully, but it did not start automatically, and the specified network IP failed, and the ident.hostName.name exception was reported on vSphere.
现象产生的原因:
①虚拟机主机名:不可以用下划线等特殊字符
②虚拟机主机名为空
Phenomenon 3: The virtual machine is successfully cloned, the virtual machine automatically starts normally, and the network assignment is successful, but the virtual machine network is blocked and the network cannot be used normally.
现象产生的原因:
指定的虚拟机IP和虚拟机所在的网络适配器的网络段不匹配,导致网络不通。
After the virtual machine is cloned successfully, if the virtual machine IP does not match the network segment of the network adapter, you can modify the network segment, that is, modify the vlan network segment.
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
Modifications here are divided into vds and vss type network segments. Vss refers to standard switches, while vds refers to distributed switches. The two methods of modifying network segments are different.
Supplement: When cloning a virtual machine, there is no cluster, level: data center -> folder -> host
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