克隆虚拟机是依赖于虚拟机模板,在虚拟机模板失常的状况下,以上代码可能实现克隆虚拟机并指定 IP 的操作的,那么先来说一说克隆虚拟机的坑。
- 景象 1:虚拟机克隆胜利,虚拟机主动失常开机,但没有指定上网络 IP。
景象产生的起因:
克隆应用的模板没有网络适配器 - 景象 2:虚拟机克隆胜利,然而没有自动开机,并且指定网络 IP 也失败了,在 vSphere 上还报了 ident.hostName.name 异样。
景象产生的起因:
①虚拟机主机名:不能够用下划线等特殊字符
②虚拟机主机名为空 - 景象 3:虚拟机克隆胜利,虚拟机主动失常开机,网络指定胜利,但虚拟机网络不通,无奈失常应用网络。
景象产生的起因:
指定的虚拟机 IP 和虚拟机所在的网络适配器的网络段不匹配,导致网络不通。
补充:当克隆虚拟机,没有集群,层级:数据中心–> 文件夹–> 主机
vim 飘红能够疏忽
code
# -*- coding: utf-8 -*-
import time
import traceback
import logging
from pyVim.connect import SmartConnectNoSSL, Disconnect
from pyVmomi import vim
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:
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 data
if __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)