准备环境

安装包:

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