当前位置: 首页 > 工具软件 > Pyvmomi > 使用案例 >

pyvmomi批量克隆虚拟机【自定义主机名、网卡信息、追加硬盘、虚拟机名称】以及批量开关机和批量追加硬盘

潘嘉佑
2023-12-01
Author: Harper Joe

目的

根据场景需要,自动化克隆虚拟机,减少运维工作量;


1. 代码介绍

1.1. 功能

  • 批量克隆虚拟机,根据虚拟交换网络实现开机互通;
    • 根据模版配置克隆虚拟机;
    • 可以自定义主机名、网卡、网卡信息、追加硬盘容量;
    • 可以自定义虚拟机名称;
  • 批量开关机
  • 批量追加硬盘

1.2. 环境

  1. 网络问题不过多描述;
  2. pip freeze | grep pyvmomi 检查版本,当前使用7.0.3。可以使用pip install --upgrade pyvmomi升级
  3. 源码包地址
    https://github.com/vmware/pyvmomi

1.3. 注意事项

  • VCSA(Vcenter)层级不同,部分代码些许不同,注意folder
    • 第一种情况:数据中心–>文件夹–>主机
    • 第二种情况:数据中心–>集群–>主机

2. 参考链接

  • pyvmomi提供的社区样本项目:
    • https://github.com/vmware/pyvmomi-community-samples
      git clone https://github.com/vmware/pyvmomi-community-samples.git
  • CSDN博主发布的参考链接
    • https://blog.csdn.net/qq_42631707/article/details/100156258
    • https://blog.csdn.net/weixin_34050389/article/details/92148334

3.参考代码:

3.1. 批量克隆并自定义虚拟机

# -*- coding: utf-8 -*-
"""
    func: 批量克隆虚拟机
    Author: CSDN.晴空万里长风微凉
"""
import json
import logging
import sys, time
import traceback
import atexit
try:
    from pyVim.connect import SmartConnect, SmartConnectNoSSL, Disconnect
except ImportError:
    from pyvim.connect import SmartConnect, SmartConnectNoSSL, Disconnect
from pyVmomi import vim

reload(sys)
sys.setdefaultencoding('utf8')


# create logger object
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger_handler = logging.StreamHandler(stream=sys.stdout)
logger_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(lineno)d] %(message)s', '%Y-%m-%d %H:%M:%S')
logger_handler.setFormatter(formatter)
logger.addHandler(logger_handler)


class VmManage(object):

    def __init__(self, host, user, password, port, ssl):
        self.host = host
        self.user = user
        self.pwd = password
        self.port = port
        self.sslContext = ssl
        try:
            self.client = SmartConnectNoSSL(host=host,  # SmartConnectNoSSL: 不需要ssl证书验证
                                            user=user,
                                            pwd=password,
                                            port=443
                                            )
            self.content = self.client.RetrieveContent()
            self.result = True
        except Exception as e:
            self.result = False
            self.message = 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])

    def get_datacenter_by_name(self, datacenter_name):
        """
        根据数据中心名称获取数据中心对象
        """
        return self._get_obj([vim.Datacenter], datacenter_name)

    def get_datacenter_objs(self):
        """

        :return: 获取数据中心下有多少集群、主机、目录(只获取数据中心当层数据)
        """
        datacenter_objs = self._get_all_objs([vim.Datacenter])
        data = []
        for i in datacenter_objs:
            datacenter_data = {'datacenter_name': i.name,  # 数据中心
                               'data': {}
                               }
            datacenter_data['data']['cluster_nums'] = 0
            datacenter_data['data']['cluster_data'] = []
            datacenter_data['data']['host_nums'] = 0
            datacenter_data['data']['host_data'] = []
            datacenter_data['data']['folder_nums'] = 0
            datacenter_data['data']['folder_data'] = []
            for j in i.hostFolder.childEntity:
                if type(j) == vim.ClusterComputeResource:
                    datacenter_data['data']['cluster_nums'] += 1
                    datacenter_data['data']['cluster_data'].append(j.name)
                elif type(j) == vim.HostSystem:
                    datacenter_data['data']['host_nums'] += 1
                    datacenter_data['data']['host_data'].append(j.name)
                elif type(j) == vim.Folder:
                    datacenter_data['data']['folder_nums'] += 1
                    datacenter_data['data']['folder_data'].append(j.name)
            data.append(datacenter_data)
        return data

    # ------------
    # 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
            time.sleep(2)
            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': False}

    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 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:
                        logger.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)
                logger.info('网络适配器设置')
                break
        if device_change:
            return device_change
        else:
            if not no_vlan:
                logger.error(u'网络适配器不存在,无法修改网络')

        return device_change

    def add_disk(self, vm_obj, capacity):
        """

        :param vm_obj:
        :param capacity:
        :return: 追加硬盘(配置列表)
        """
        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

        return dev_changes

    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 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, vm_vlan=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
        # vmfolder = self._get_obj([vim.Folder], vm_folder)
        # print vmfolder
        vmfolder = datacenter.vmFolder
        # print 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('设置网卡')
        #     nic_change = self.add_nic('VM Network')
        #     device_change.extend(nic_change)
        # 指定网络段配置
        switch_type = 1  # 网络段类型
        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)

        # 修改硬盘大小
        if vm_disk:
            disk_change = self.add_disk(template, vm_disk)
            if type(disk_change) is list:
                device_change.extend(disk_change)
                logger.info('追加硬盘设置')
            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)
        # vmconf = vim.vm.ConfigSpec()

        # 设置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('网卡信息设置, hostname设置')

        # 更改cpu和内存
        if cup_num:
            vmconf.numCPUs = cup_num
            logger.info('处理器核数设置')
        if memory:
            vmconf.memoryMB = memory * 1024
            logger.info('内存设置')
        if vmconf is not None:
            clonespec.config = vmconf

        # 开始克隆
        task = template.Clone(folder=vmfolder, name=vm_name, spec=clonespec)
        # [ 开始执行 ]记录执行日志.
        result = self.wait_for_task(task)
        if result['status']:
            # data = {'message': u'克隆成功', 'result': True}
            data = {
                'message': u'克隆成功',
                'result': True,
                'vm_name': vm_name,
                'vm_ip': vm_ip,
                'vm_exi_ip': vm_exi_ip
            }
        else:
            data = {'message': '克隆失败: %s' % result['message'], 'result': False}

        # [ 开始执行 ]不记录vm日志.
        # vm_task = {
        #     'task': task,
        #     'vm_name': vm_name,
        #     'vm_ip': vm_ip,
        #     'vm_exi_ip': vm_exi_ip,
        # }
        # data = {'message': u'任务下发成功', 'description': 'Task delivery succeeded!', 'result': True, 'data': vm_task}

        return data


# 获取资源信息(每台需克隆机器的相关信息)
def gen_resource_info():
    # VCSA登录信息
    vm_vcsa_info = {}
    vm_vcsa_info.update({
        "vm_judge_clone": vm_judge_clone, # 是否自动克隆判断
        "vm_vcsa_addr": vm_vcsa_addr,  # VCSA地址
        "vm_vcsa_user": vm_vcsa_user, # VCSA用户名
        "vm_vcsa_passwd": vm_vcsa_passwd,  # VCSA密码密钥键
        "vm_datacenter_name": vm_datacenter_name, })  # 数据中心名称

    # 资源表
    resource_table_cl = []	# 每台申请虚拟机的信息,包括以下
    resource_table = []
    if resource_table_cl:
        for r in resource_table_cl:
            host_source_data_of_resource = {
                "server_name": server_name, # 主机名
                "host_server_use": host_server_use,# 主机用途
                "host_node_config": host_node_config,# cpu核数、内存大小
                "host_net_ip": host_net_ip,
                "host_net_gateway": host_net_gateway,
                "host_net_dns": host_net_dns,
                "host_net_netmask": host_net_netmask,
                "host_owner_name": host_owner_name, # 应用负责人
                "disk_root_add": disk_root_add,# 系统盘扩容申请
                "disk_root_num": disk_root_num, # 系统盘扩容大小
                "vm_info": {
                    "vm_esxi_name": vm_esxi_name, # ESXI名称
                    "vm_template_name": vm_template_name,  # 模版
                    "vm_storage_name": vm_storage_name, # 存储名称
                },
            }
            resource_table.append(host_source_data_of_resource)

    return vm_vcsa_info, resource_table


def app_run():
    vm_vcsa_info, resource_info = gen_resource_info()  # 资源表

    # vcenter_login—check
    vm_judge_clone = vm_vcsa_info["vm_judge_clone"]  # 是否自动克隆判断
    vm_vcsa_addr = vm_vcsa_info["vm_vcsa_addr"]  # VCSA地址
    vm_vcsa_user = vm_vcsa_info["vm_vcsa_user"]  # VCSA用户名
    vm_vcsa_passwd = vm_vcsa_info["vm_vcsa_passwd"]  # VCSA密码
    vm_datacenter_name = vm_vcsa_info["vm_datacenter_name"]  # 数据中心名称
    vm_port = 443
    if isinstance(resource_info, list):
        if vm_judge_clone == "是":
            pass
        else:
            pass
    vm = VmManage(host=vm_vcsa_addr, user=vm_vcsa_user, password=vm_vcsa_passwd, port=vm_port, ssl=None)
    if vm.result:
        logger.info("与VCSA连接成功, 准备读取table容器...")  # 说明连接成功,可以使用vm.client等
    else:
        raise Exception("与VCSA连接失败,报错信息: %s" % vm.message)
    atexit.register(Disconnect, vm)  # 注册程序退出时的回调函数(断开时操作)

    # clone
    for i in resource_info:

        # vm_info
        vm_info = i["vm_info"]
        # vm_folder_name = vm_info["vm_folder_name"]  # 文件夹名称
        vm_esxi_name = vm_info["vm_esxi_name"]  # ESXI名称
        vm_template_name = vm_info["vm_template_name"]  # 模版
        vm_storage_name = vm_info["vm_storage_name"]  # 存储名称

        # vm_Virtual
        server_name = i["server_name"]  # 主机名
        host_server_use = i["host_server_use"]  # 主机用途
        host_node_config = i["host_node_config"]  # cpu核数、内存大小
        host_net_ip = i["host_net_ip"]
        host_net_gateway = i["host_net_gateway"]
        host_net_dns = i["host_net_dns"]
        host_net_netmask = i["host_net_netmask"]
        host_owner_name = i["host_owner_name"]  # 应用负责人
        disk_root_add = i["disk_root_add"]  # 系统盘扩容申请
        disk_root_num = i["disk_root_num"]  # 系统盘扩容大小

        try:
            # clone
            vm_hostname_comp = server_name + "-%s" % host_owner_name + "-%s" % host_net_ip
            host_node_cpu = int(host_node_config.split("/")[0][:-1])
            host_node_mem = int(host_node_config.split("/")[1][:-1])
            host_network_port_group = '.'.join(host_net_ip.split('.')[:-1])
            disk_increase = None
            if disk_root_add == "是":  # 磁盘追加
                disk_increase = disk_root_num

            data = vm.clone(
                # vm_info —— 向vm中写入信息
                template_name=vm_template_name,
                vm_name=vm_hostname_comp,
                datacenter_name=vm_datacenter_name,
                datastore_name=vm_storage_name,
                vm_exi_ip=vm_esxi_name,
                # vm_folder=vm_folder_name,
                # vm_Virtual —— 自定义虚拟机信息
                cup_num=host_node_cpu,
                memory=host_node_mem,
                vm_disk=disk_increase,
                vm_ip=host_net_ip,
                vm_subnetmask=host_net_netmask,
                vm_gateway=host_net_gateway,
                vm_dns=host_net_dns,
                vm_domain=None,
                vm_hostname=server_name,
                vm_vlan=host_network_port_group,
            )
            logger.info(data)

        except Exception as e:
            logger.error("%s 克隆出错!及时查看, info: %s" % (host_net_ip, e))


app_run()

3.2. 批量开机

# -*- coding:utf-8 -*-
# 参考https://github.com/vmware/pyvmomi/blob/master/sample/poweronvm.py

"""
    func: 批量开启虚拟机
    Author: CSDN.晴空万里长风微凉
"""

from __future__ import print_function
from pyvim.connect import SmartConnect, Disconnect, SmartConnectNoSSL
from pyVmomi import vim, vmodl
import logging
import argparse
import atexit
import getpass
import sys
import ssl

reload(sys)
sys.setdefaultencoding('utf8')

# create logger object
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger_handler = logging.StreamHandler(stream=sys.stdout)
logger_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(lineno)d] %(message)s', '%Y-%m-%d %H:%M:%S')
logger_handler.setFormatter(formatter)
logger.addHandler(logger_handler)


def GetArgs():
    """
    Supports the command-line arguments listed below.
    """

    parser = argparse.ArgumentParser(description='Process args for powering on a Virtual Machine')
    parser.add_argument('-s', '--host', required=True, action='store', help='Remote host to connect to')
    parser.add_argument('-o', '--port', type=int, default=443, action='store', help='Port to connect on')
    parser.add_argument('-u', '--user', required=True, action='store', help='User name to use when connecting to host')
    parser.add_argument('-p', '--password', required=False, action='store',
                        help='Password to use when connecting to host')
    parser.add_argument('-v', '--vmname', required=True, action='append',
                        help='Names of the Virtual Machines to power on')
    args = parser.parse_args()
    return args


def WaitForTasks(tasks, si):
    """
    Given the service instance si and tasks, it returns after all the
    tasks are complete
    """

    pc = si.content.propertyCollector

    taskList = [str(task) for task in tasks]

    # Create filter
    objSpecs = [vmodl.query.PropertyCollector.ObjectSpec(obj=task)
                for task in tasks]
    propSpec = vmodl.query.PropertyCollector.PropertySpec(type=vim.Task,
                                                          pathSet=[], all=True)
    filterSpec = vmodl.query.PropertyCollector.FilterSpec()
    filterSpec.objectSet = objSpecs
    filterSpec.propSet = [propSpec]
    filter = pc.CreateFilter(filterSpec, True)

    try:
        version, state = None, None

        # Loop looking for updates till the state moves to a completed state.
        while len(taskList):
            update = pc.WaitForUpdates(version)
            for filterSet in update.filterSet:
                for objSet in filterSet.objectSet:
                    task = objSet.obj
                    for change in objSet.changeSet:
                        if change.name == 'info':
                            state = change.val.state
                        elif change.name == 'info.state':
                            state = change.val
                        else:
                            continue

                        if not str(task) in taskList:
                            continue

                        if state == vim.TaskInfo.State.success:
                            # Remove task from taskList
                            taskList.remove(str(task))
                        elif state == vim.TaskInfo.State.error:
                            raise task.info.error
            # Move to next version
            version = update.version
    finally:
        if filter:
            filter.Destroy()


# Start program
def main():
    """
    Simple command-line program for powering on virtual machines on a system.
    """

    # args = GetArgs()
    # if args.password:
    #    password = args.password
    # else:
    #    password = getpass.getpass(prompt='Enter password for host %s and user %s: ' % (args.host,args.user))

    try:
        # vmnames = args.vmname
        # 将需要开机的vmwarem_name用逗号或者空格拼凑起来
        vmnames = 'clone_auto_WB2'
        if not len(vmnames):
            print("No virtual machine specified for poweron")
            sys.exit()

        context = None
        # si = SmartConnectNoSSL(host="192.168.0.0",	# SmartConnectNoSSL: 不需要ssl证书验证
        #                        user="u",
        #                        pwd="",
        #                        port=443)
        if hasattr(ssl, '_create_unverified_context'):
            context = ssl._create_unverified_context()
        si = SmartConnect(host="vcsa/esxi_address",
                          user="user",
                          pwd="",
                          port=443,
                          sslContext=context)

        if not si:
            print("Cannot connect to specified host using specified username and password")
            sys.exit()

        atexit.register(Disconnect, si)

        # 从目录对象中检索虚拟机列表
        # 下面的rootFolder
        content = si.content
        objView = content.viewManager.CreateContainerView(content.rootFolder,
                                                          [vim.VirtualMachine],
                                                          True)
        vmList = objView.view
        objView.Destroy()

        # Find the vm and power it on
        # 遍历vmList拼凑任务

        virtual_lists = [vm.name for vm in vmList]
        logger.info("虚拟机列表: %s" % virtual_lists)
        # for vm in vmList:
        #     print(vm.name)
        tasks = [vm.PowerOn() for vm in vmList if vm.name in vmnames]
        logger.info("虚拟机电源开启清单: %s" % tasks)

        # Wait for power on to complete
        # 启动任务
        # WaitForTasks(tasks, si)

        logger.info("Virtual Machine(s) have been powered on successfully")
    except vmodl.MethodFault as e:
        logger.info("Caught vmodl fault : " + e.msg)
    except Exception as e:
        logger.info("Caught Exception : " + str(e))


# Start program
if __name__ == "__main__":
    main()

# -s vcenter 的ip地址
# -u vcenter账号  -p vcenter密码
# -v 需要开机的虚拟机hostname
# [root@Max_master:~#]$ python3 python_poweron.py -s IP -o 443 -u USERNAME -p PASSWORD -v VM_HOSTNAME

3.3. 批量关机

# -*- coding:utf-8 -*-
# 参考https://github.com/vmware/pyvmomi/blob/master/sample/poweronvm.py

"""
    func: 批量关闭虚拟机
    Author: CSDN.晴空万里长风微凉
"""

from __future__ import print_function
from pyvim.connect import SmartConnect, Disconnect, SmartConnectNoSSL
from pyVmomi import vim, vmodl
import logging
import argparse
import atexit
import getpass
import sys
import ssl

reload(sys)
sys.setdefaultencoding('utf8')

# create logger object
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger_handler = logging.StreamHandler(stream=sys.stdout)
logger_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(lineno)d] %(message)s', '%Y-%m-%d %H:%M:%S')
logger_handler.setFormatter(formatter)
logger.addHandler(logger_handler)


def GetArgs():
    """
    Supports the command-line arguments listed below.
    """

    parser = argparse.ArgumentParser(description='Process args for powering on a Virtual Machine')
    parser.add_argument('-s', '--host', required=True, action='store', help='Remote host to connect to')
    parser.add_argument('-o', '--port', type=int, default=443, action='store', help='Port to connect on')
    parser.add_argument('-u', '--user', required=True, action='store', help='User name to use when connecting to host')
    parser.add_argument('-p', '--password', required=False, action='store',
                        help='Password to use when connecting to host')
    parser.add_argument('-v', '--vmname', required=True, action='append',
                        help='Names of the Virtual Machines to power on')
    args = parser.parse_args()
    return args


def WaitForTasks(tasks, si):
    """
    Given the service instance si and tasks, it returns after all the
    tasks are complete
    """

    pc = si.content.propertyCollector

    taskList = [str(task) for task in tasks]

    # Create filter
    objSpecs = [vmodl.query.PropertyCollector.ObjectSpec(obj=task)
                for task in tasks]
    propSpec = vmodl.query.PropertyCollector.PropertySpec(type=vim.Task,
                                                          pathSet=[], all=True)
    filterSpec = vmodl.query.PropertyCollector.FilterSpec()
    filterSpec.objectSet = objSpecs
    filterSpec.propSet = [propSpec]
    filter = pc.CreateFilter(filterSpec, True)

    try:
        version, state = None, None

        # Loop looking for updates till the state moves to a completed state.
        while len(taskList):
            update = pc.WaitForUpdates(version)
            for filterSet in update.filterSet:
                for objSet in filterSet.objectSet:
                    task = objSet.obj
                    for change in objSet.changeSet:
                        if change.name == 'info':
                            state = change.val.state
                        elif change.name == 'info.state':
                            state = change.val
                        else:
                            continue

                        if not str(task) in taskList:
                            continue

                        if state == vim.TaskInfo.State.success:
                            # Remove task from taskList
                            taskList.remove(str(task))
                        elif state == vim.TaskInfo.State.error:
                            raise task.info.error
            # Move to next version
            version = update.version
    finally:
        if filter:
            filter.Destroy()


# Start program
def main():
    """
    Simple command-line program for powering on virtual machines on a system.
    """

    # args = GetArgs()
    # if args.password:
    #    password = args.password
    # else:
    #    password = getpass.getpass(prompt='Enter password for host %s and user %s: ' % (args.host,args.user))

    try:
        # vmnames = args.vmname
        # 将需要关机的vmwarem_name用逗号或者空格拼凑起来
        vmnames = 'clone_auto_WB2'
        if not len(vmnames):
            print("No virtual machine specified for poweron")
            sys.exit()

        context = None
        if hasattr(ssl, '_create_unverified_context'):
            context = ssl._create_unverified_context()
        si = SmartConnect(host="192.168.",
                          user='a',
                          pwd="",
                          port=443,
                          sslContext=context)

        if not si:
            print("Cannot connect to specified host using specified username and password")
            sys.exit()

        atexit.register(Disconnect, si)

        # 从目录对象中检索虚拟机列表
        # 下面的rootFolder
        content = si.content
        objView = content.viewManager.CreateContainerView(content.rootFolder,
                                                          [vim.VirtualMachine],
                                                          True)
        vmList = objView.view
        objView.Destroy()

        # Find the vm and power it on
        # 遍历vmList拼凑任务

        virtual_lists = [vm.name for vm in vmList]
        logger.info("虚拟机列表: %s" % virtual_lists)
        # for vm in vmList:
        #     print(vm.name)

        tasks = [vm.PowerOff() for vm in vmList if vm.name in vmnames]
        logger.info("虚拟机电源开启清单: %s" % tasks)

        # Wait for power on to complete
        # 启动任务
        # WaitForTasks(tasks, si)

        logger.info("Virtual Machine(s) have been powered off successfully")
    except vmodl.MethodFault as e:
        logger.info("Caught vmodl fault : " + e.msg)
    except Exception as e:
        logger.info("Caught Exception : " + str(e))


# Start program
if __name__ == "__main__":
    main()

# -s vcenter 的ip地址
# -u vcenter账号  -p vcenter密码
# -v 需要关机的虚拟机hostname
# [root@Max_master:~#]$ python3 python_poweroff.py -s IP -o 443 -u USERNAME -p PASSWORD -v VM_HOSTNAME

3.4. 批量追加磁盘

"""
    func: 批量关闭虚拟机
    Author: CSDN.晴空万里长风微凉
"""
# -*- coding:utf-8 -*-
import json
from pyvim.connect import SmartConnect, SmartConnectNoSSL, Disconnect
from pyVmomi import vim
import traceback
import atexit
import sys, time
import logging

reload(sys)
sys.setdefaultencoding('utf8')

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger_handler = logging.StreamHandler(stream=sys.stdout)
logger_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(lineno)d] %(message)s', '%Y-%m-%d %H:%M:%S')
logger_handler.setFormatter(formatter)
logger.addHandler(logger_handler)

class VmManage(object):

    def __init__(self, host, user, password, port, ssl):
        self.host = host
        self.user = user
        self.pwd = password
        self.port = port
        self.sslContext = ssl
        try:
            self.client = SmartConnectNoSSL(host=host,  # SmartConnectNoSSL: 不需要ssl证书验证
                                            user=user,
                                            pwd=password,
                                            port=443
                                            )
            self.content = self.client.RetrieveContent()
            self.result = True
        except Exception as e:
            self.result = False
            self.message = 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 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
            time.sleep(2)
            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': False}

    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)
        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


if __name__ == '__main__':

    # ------------------ ESXI/VcenterSA - 登录检查'Start ------------------ Top密码需要存入密钥加密
    ip = 'vcsa/esxi address'
    user = 'administrator@vsphere.local'
    password = ''
    port = 443
    vm = VmManage(host=ip,
                  user=user,
                  password=password,
                  port=443, ssl=None)
    if vm.result:
        pass  # 说明连接成功,可以使用vm.client等
    else:
        print vm.message
    atexit.register(Disconnect, vm)  # 注册程序退出时的回调函数(断开时操作)
    # ------------------ ESXI/VcenterSA - 登录检查'End ------------------

    # pyvmomi 登录设备
    vm_name = "clone_auto_WB2"		# 虚拟机名称
    add_disk_capacity = 2   # GB
    vm_obj = vm._get_obj([vim.VirtualMachine], vm_name)

    # 虚拟机添加硬盘
    vm_obj_add_disk = vm.add_disk(vm_obj, add_disk_capacity)
    print(vm_obj_add_disk)
 类似资料: