Python pyvmomi operation VMware (four): clone virtual machine and configure IP and change vlan network segment

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

Guess you like

Origin blog.csdn.net/qq_42631707/article/details/100156258