克隆虚拟机是依赖于虚拟机模板,在虚拟机模板失常的状况下,以上代码可能实现克隆虚拟机并指定IP的操作的,那么先来说一说克隆虚拟机的坑。景象1:虚拟机克隆胜利,虚拟机主动失常开机,但没有指定上网络IP。景象产生的起因:克隆应用的模板没有网络适配器景象2:虚拟机克隆胜利,然而没有自动开机,并且指定网络IP也失败了,在 vSphere上还报了ident.hostName.name异样。景象产生的起因:①虚拟机主机名:不能够用下划线等特殊字符②虚拟机主机名为空景象3:虚拟机克隆胜利,虚拟机主动失常开机,网络指定胜利,但虚拟机网络不通,无奈失常应用网络。景象产生的起因:指定的虚拟机IP和虚拟机所在的网络适配器的网络段不匹配,导致网络不通。补充:当克隆虚拟机,没有集群,层级:数据中心–>文件夹–>主机
vim 飘红能够疏忽code# -*- coding: utf-8 -*-import timeimport tracebackimport loggingfrom pyVim.connect import SmartConnectNoSSL, Disconnectfrom pyVmomi import vimclass 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: print("##get_host_by_name host:", host.key.summary.config.name) 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 change_disk_size(self, vm_obj, vm_disk): """ :param vm_obj: 虚拟机对象(模板对象也实用) :param vm_disk: 硬盘大小(单位:G) :return: disk_change 配置列表 """ virtual_disk_devices = [] virtual_disk_device = None virtual_scsi_controller = None # Find the disk device for dev in vm_obj.config.hardware.device: if isinstance(dev, vim.vm.device.VirtualSCSIController): virtual_scsi_controller = dev if isinstance(dev, vim.vm.device.VirtualDisk): virtual_disk_devices.append(dev) for dev in virtual_disk_devices: if dev.controllerKey == virtual_scsi_controller.key: virtual_disk_device = dev new_disk_kb = int(vm_disk) * 1024 * 1024 virtual_disk_spec = vim.vm.device.VirtualDeviceSpec() virtual_disk_spec.operation = \ vim.vm.device.VirtualDeviceSpec.Operation.edit virtual_disk_spec.device = virtual_disk_device virtual_disk_spec.device.capacityInKB = new_disk_kb disk_changes = [] disk_changes.append(virtual_disk_spec) return disk_changes def add_disk(self, vm_obj, capacity): spec = vim.vm.ConfigSpec() dev_changes = [] # capacity 为 存储盘容量将单位改为 G new_disk_kb = capacity * 1024 * 1024 unit_number = 0 # 遍历所有的硬件设施,找适合的地位增加 for dev in vm_obj.config.hardware.device: if hasattr(dev.backing, 'fileName'): unit_number = int(dev.unitNumber) + 1 # unit_number 7 reserved for scsi controller if unit_number == 7: unit_number += 1 if unit_number >= 16: logging.error('we don\'t support this many disks') if isinstance(dev, vim.vm.device.VirtualSCSIController): controller = dev disk_spec = vim.vm.device.VirtualDeviceSpec() disk_spec.fileOperation = "create" disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add disk_spec.device = vim.vm.device.VirtualDisk() disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo() disk_spec.device.backing.thinProvisioned = True disk_spec.device.backing.diskMode = 'persistent' disk_spec.device.unitNumber = unit_number disk_spec.device.capacityInKB = new_disk_kb disk_spec.device.controllerKey = controller.key dev_changes.append(disk_spec) return dev_changes def add_disk_to_vm(self, vm_name, capacity): vm_obj = self._get_obj([vim.VirtualMachine], vm_name) spec = vim.vm.ConfigSpec() dev_changes = [] # capacity 为 存储盘容量将单位改为 G new_disk_kb = capacity * 1024 * 1024 unit_number = 0 # 遍历所有的硬件设施,找适合的地位增加 for dev in vm_obj.config.hardware.device: if hasattr(dev.backing, 'fileName'): unit_number = int(dev.unitNumber) + 1 # unit_number 7 reserved for scsi controller if unit_number == 7: unit_number += 1 if unit_number >= 16: logging.error('we don\'t support this many disks') if isinstance(dev, vim.vm.device.VirtualSCSIController): controller = dev disk_spec = vim.vm.device.VirtualDeviceSpec() disk_spec.fileOperation = "create" disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add disk_spec.device = vim.vm.device.VirtualDisk() disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo() disk_spec.device.backing.thinProvisioned = True disk_spec.device.backing.diskMode = 'persistent' disk_spec.device.unitNumber = unit_number disk_spec.device.capacityInKB = new_disk_kb disk_spec.device.controllerKey = controller.key dev_changes.append(disk_spec) spec.deviceChange = dev_changes task = vm_obj.ReconfigVM_Task(spec=spec) result1 = self.wait_for_task(task) if result1['status']: data = {'message': u'硬盘增加胜利', 'result': True} else: data = {'message': u'硬盘增加失败: %s' % result1['message'], 'result': False} return data 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 def device_nic(self, vm, network_name, switch_type): """ :param vm: 虚拟机模板对象 :param network_name: 要批改的网络段名称 :param switch_type: 网络段类型 :return: """ device_change = [] no_vlan = False for device in vm.config.hardware.device: # 判断是否存在网络适配器 if isinstance(device, vim.vm.device.VirtualEthernetCard): nicspec = vim.vm.device.VirtualDeviceSpec() # 肯定要是vim.vm.device.VirtualDeviceSpec.Operation.edit 代表编辑 nicspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit nicspec.device = device nicspec.device.wakeOnLanEnabled = True if switch_type == 1: # 规范交换机设置 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) if network is None: logging.error(u'分组交换机没有{0}网段'.format(network_name)) no_vlan = True break # 分布式交换机设置 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) logging.info('网络适配器设置') break if device_change: return device_change else: if not no_vlan: logging.error(u'网络适配器不存在,无奈批改网络') return device_change def get_vm_ip(self, vm_obj): """ 获取 VM 所有 IP 地址 """ ips = [] for i in vm_obj.guest.net: if i.network: for b in range(6): if i.ipAddress: ips.extend(i.ipAddress) return ips else: print("VM 没有获取到IP地址期待10秒") time.sleep(10) else: print('VM 未配置网卡') return 'VM 未获取到IP地址,请检测网络状态或者 DHCP 服务器' def clone_cluster(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=None, vm_domain=None, vm_hostname=None): # 获取模版 template = self._get_obj([vim.VirtualMachine], template_name) # 模版不存在 if template is None: return {'message': u'克隆失败: 模版不存在', 'result': False} else: print(template.name, "模版存在hehe") # 抉择克隆的虚拟机寄存地位,通过数据中心获取对象 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: print(datastore.name, "数据中心存储存在") 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} else: print(template.name, datastore, "模版存在hehe") # 获取集群 cluster = self.get_cluster_by_name(cluster_name, datacenter) print("##cluster:", cluster) if cluster is None: print(self.get_host_by_name(host_name, datastore)) return {'message': u'克隆失败: cluster %s no存在' % cluster, 'result': False} else: 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 def clone_host(self, template_name, vm_name, datacenter_name, datastore_name, vm_exi_ip, vm_vlan=None, switch_type=None, vm_folder=None, cup_num=None, memory=None, vm_disk=None, vm_add_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: print("##host vm:", vm) 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: # logging.info(u'设置网卡') # nic_change = self.add_nic('VM Network') # device_change.extend(nic_change) # 指定网络段配置 if vm_vlan: device_nic_change = self.device_nic( vm=template, network_name=vm_vlan, switch_type=switch_type ) device_change.extend(device_nic_change) # 批改硬盘大小 # vm_disk 必须比原来值大哦 if vm_disk: logging.info(u'扩容硬盘') 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} # add_disk if vm_add_disk: logging.info(u'扩容硬盘') add_disk_change = self.add_disk(template, vm_add_disk) if type(add_disk_change) is list: device_change.extend(add_disk_change) else: return {'message': add_disk_change, 'result': False} # 更新配置 vmconf = vim.vm.ConfigSpec(deviceChange=device_change) logging.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) logging.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 dataif __name__ == '__main__': ip = '10.31.1xx.4x' user = 'axxxxcal' password = 'Qxxxxx4' port = 443 template_name = u'centos7temp' vm_name = u'centos7test60' data_center_name = u'Datacenter' datastore_name = u'datastore1' cluster_name = "10.31.1xx.x6" host_name = "10.31.1x.x6" cup_num = 2 memory = 2 resource_pool = None power_on = True vm_ext_disk = 50 vm_add_disk = 20 vm_ip = '10.31.1xx.x8' vm_subnetmask = '255.255.255.0' vm_gateway = '10.31.1xx.1' vm_dns = '8.8.8.8' vm_vlan = None switch_type = None vm_domain = 'xiaohxx.com' vm_hostname = 'test' vm = VmManage(host=ip, user=user, password=password, port=port, ssl=None) data = vm.clone_host(template_name=template_name, vm_name=vm_name, datacenter_name=data_center_name, datastore_name=datastore_name, vm_exi_ip=host_name, vm_vlan=None, switch_type=None, vm_folder=None, cup_num=4, memory=2, vm_disk=None, vm_add_disk=30, vm_ip=vm_ip, vm_subnetmask=vm_subnetmask, vm_gateway=vm_gateway, vm_dns=vm_dns, vm_domain=vm_domain, vm_hostname=vm_hostname) # 给vm add 30G磁盘 #data = vm.add_disk_to_vm(vm_name=template_name, capacity=30) # # 虚拟机开机,期待x0秒,待虚拟机开机启动获取IP地址 # vm_obj = vm._get_obj([vim.VirtualMachine], vm_name) # task = vm_obj.PowerOn() # print("启动 VM 电源") # print(vm.wait_for_task(task)) # time.sleep(50) # print("开始获取 VM IP地址") # vm_obj_ip = vm.get_vm_ip(vm_obj) # # 虚拟机 off # vm_obj = vm._get_obj([vim.VirtualMachine], template_name) # task = vm_obj.PowerOff() # print("PowerOff") # print(vm.wait_for_task(task)) # time.sleep(20) # data = vm.clone_cluster( # 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 # ) print(data)
...