查看原文
其他

蓝鲸实现多环境vsphere虚拟机交付

木讷大叔爱运维 木讷大叔爱运维 2022-07-13




读完需 5 分钟

速读需 3 分钟 



需求

蓝鲸实现vsphere虚拟机交付 -虚拟机管理(VSPHERE)》一文主要介绍了蓝鲸基于单一的生产vphere环境实现了虚拟机的交付,但是在实际应用中可能存在多个vsphere环境,如生产、测试等,因此我们更希望的是通过蓝鲸能够实现多环境vsphere虚拟机的交付。


如果要实现这一个场景需求,可能存在以下几个问题:

  1. 多环境vsphere 会划分不同的vlan、子网掩码、网关、dns等;

  2. vsphere自定义规范不支持更改vlan,因此需要手动调整vlan;

  3. 多环境vsphere 需要匹配多个vCenter,并输入相关自定义参数;


我们带着以上问题进入下一阶段。


思路解析

1.多vlan网段

由于vsphere自定义规范的限制,我们不能够在自定义规范中直接指定虚拟机所属vlan。但是退一步思考:

  • 一般情况下,只有在新vlan中第一台虚拟机才需要指定vlan,后续我们可以根据这台已经更改完vlan的虚拟机进行克隆;

  • 修改模板机vlan,后续可直接从模板机直接创建;

  • 手动调整克隆后虚拟机的vlan;


因此我们不必纠结于自定义规范的限制,只需要关注如何快速适配vlan的子网掩码、网关、dns,此时就可以利用自定义规范天生具有的功能了。


在此我的解决方法是将vlan的子网掩码、网关、dns的等可能变动的参数在vsphere前端原子中根据需求自定义,而不是直接写死。


当使用新vlan时,我们只需在vsphere原子中更改以上参数,就可以通过自定义规范中直接定制了。


2.多环境vsphere

多环境vsphere 意味着有多个vCenter,但是最终执行通过自定义规范创建虚拟机的功能是不变的,因此当我们在原子前端输入新虚拟机的配置信息时就已经确定此虚拟机属于哪个环境了。


根据这一特性,我的解决思路是提供三个不同环境的前端原子,而后端原子保持一个即可。


前端原子

1.创建生产虚拟机

通过"vsphere_env"参数来确定使用生产vCenter

# 生产虚拟机参数# vim static/test_atoms/vsphere_vm_create.js(function(){ $.atoms.vsphere_vm_create = [ { tag_code: "vsphere_env", type: "radio", attrs: { name: gettext("环境"), items: [ {value: "prod", name: gettext("生产")}, ], default: "prod", hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_name", type: "input", attrs: { name: gettext("虚拟机名"), placeholder: gettext("新建虚拟机名"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_ip", type: "input", attrs: { name: gettext("虚拟机IP"), placeholder: gettext("虚拟机IP"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_subnetmask", type: "input", attrs: { name: gettext("虚拟机掩码"), placeholder: gettext("虚拟机掩码"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_gateway", type: "input", attrs: { name: gettext("虚拟机网关"), placeholder: gettext("虚拟机网关"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_dns", type: "input", attrs: { name: gettext("虚拟机DNS"), placeholder: gettext("虚拟机DNS"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_template_name", type: "input", attrs: { name: gettext("模板或虚拟机"), placeholder: gettext("模板或虚拟机名称"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_datacenter_name", type: "radio", attrs: { name: gettext("数据中心"), items: [ {value: "VSAN-DC", name: "VSAN-DC"}, ], default: "VSAN-DC", hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_cluster_name", type: "radio", attrs: { name: gettext("集群名"), items: [ {value: "VSAN-Cluster2", name: "VSAN-Cluster2"}, {value: "VSAN-Cluster3", name: "VSAN-Cluster3"}, ], default: "VSAN-Cluster3", hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_folder_name", type: "input", attrs: { name: gettext("存放位置"), placeholder: gettext("虚拟机存放位置"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_datastore_name", type: "select", attrs: { name: gettext("存储器名"), placeholder: gettext("存储器名"), items: [ {text: "vsanDatastore_Cluster2", value: "vsanDatastore_Cluster2"}, {text: "vsanDatastore_Cluster3", value: "vsanDatastore_Cluster3"}, ], hookable: true, validation: [ { type: "required" } ] } }, ]})();


2.创建测试虚拟机

通过"vsphere_env"参数来确定使用测试vCenter

# 生产虚拟机参数# vim static/test_atoms/vsphere_vm_create.js(function(){ $.atoms.vsphere_vm_create_test = [ { tag_code: "vsphere_env", type: "radio", attrs: { name: gettext("环境"), items: [ {value: "test", name: gettext("测试")}, ], default: "test", hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_name", type: "input", attrs: { name: gettext("虚拟机名"), placeholder: gettext("新建虚拟机名"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_ip", type: "input", attrs: { name: gettext("虚拟机IP"), placeholder: gettext("虚拟机IP"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_subnetmask", type: "input", attrs: { name: gettext("虚拟机掩码"), placeholder: gettext("虚拟机掩码"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_gateway", type: "input", attrs: { name: gettext("虚拟机网关"), placeholder: gettext("虚拟机网关"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_vm_dns", type: "input", attrs: { name: gettext("虚拟机DNS"), placeholder: gettext("虚拟机DNS"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_template_name", type: "input", attrs: { name: gettext("模板或虚拟机"), placeholder: gettext("模板或虚拟机名称"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_datacenter_name", type: "radio", attrs: { name: gettext("数据中心"), items: [ {value: "Test-Datacenter", name: "Test-Datacenter"}, ], default: "Test-Datacenter", hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_cluster_name", type: "radio", attrs: { name: gettext("集群名"), items: [ {value: "cluster", name: "cluster"}, {value: "vsan-cluster", name: "vsan-cluster"}, ], default: "VSAN-Cluster3", hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_folder_name", type: "input", attrs: { name: gettext("存放位置"), placeholder: gettext("虚拟机存放位置"), hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_datastore_name", type: "select", attrs: { name: gettext("存储器名"), placeholder: gettext("存储器名"),                items: [ {text: "vsan-cluster-vsanDatastore", value: "vsanDatastore"}, ], hookable: true, validation: [ { type: "required" } ] } },
]})();


后端原子


# vim test_atoms/components/collections/vsphere.py# -*- coding: utf-8 -*-import logging
from pipeline.conf import settingsfrom pipeline.core.flow.activity import Servicefrom pipeline.component_framework.component import Component#vsphere 原子所需扩展from pyVmomi import vimfrom pyVim.connect import SmartConnect, SmartConnectNoSSL, Disconnectimport atexitimport argparseimport getpassimport jsonimport time
logger = logging.getLogger('celery')
__group_name__ = u"虚拟机管理(VSPHERE)"
#生产vsphere vcenter连接参数prod_host = "10.10.30.29"prod_user = "administrator@vsphere.local"pord_password = "prod@2019"#测试vsphere vcenter连接参数test_host = "10.11.30.29"test_user = "administrator@vsphere.local"test_password = "test@2019"port = 443no_ssl = Truepower_on = Falseresource_pool = Falsevsphere_datastorecluster_name = False
'''vsphere 基础函数'''def wait_for_task(task): """ wait for a vCenter task to finish """ task_done = False while not task_done: if task.info.state == 'success': return task.info.result
if task.info.state == 'error': print("go to vCenter") return task.info.result task_done = True
def get_obj(content, vimtype, name): """ Return an object by name, if name is None the first found object is returned """ obj = None container = content.viewManager.CreateContainerView( content.rootFolder, vimtype, True) for c in container.view: if name: if c.name == name: obj = c break else: obj = c break
return obj
def ip_assign(vm, vm_ip, vm_name, vm_subnetmask, vm_gateway, vm_dns): """设置IP地址""" adaptermap = vim.vm.customization.AdapterMapping() adaptermap.adapter = vim.vm.customization.IPSettings() adaptermap.adapter.ip = vim.vm.customization.FixedIp() adaptermap.adapter.ip.ipAddress = vm_ip adaptermap.adapter.subnetMask = vm_subnetmask adaptermap.adapter.gateway = vm_gateway #adaptermap.adapter.dnsDomain = "localhost" """dns设置""" globalip = vim.vm.customization.GlobalIPSettings() globalip.dnsServerList = vm_dns """设置主机名""" ident = vim.vm.customization.LinuxPrep() #ident.domain = "localhost" ident.hostName = vim.vm.customization.FixedName() ident.hostName.name = vm_name customspec = vim.vm.customization.Specification() customspec.nicSettingMap = [adaptermap] customspec.globalIPSettings = globalip customspec.identity = ident print "Reconfiguring VM Networks . . ." #task = get_obj([vim.VirtualMachine],vm).Customize(spec=customspec) task = vm.Customize(spec=customspec) wait_for_task(task) return True
def clone_vm( content, template, vm_name, si, datacenter_name, vm_folder, datastore_name, cluster_name, resource_pool, power_on, datastorecluster_name): """ Clone a VM from a template/VM, datacenter_name, vm_folder, datastore_name cluster_name, resource_pool, and power_on are all optional. """
# if none git the first one datacenter = get_obj(content, [vim.Datacenter], datacenter_name)
if vm_folder: destfolder = get_obj(content, [vim.Folder], vm_folder) else: destfolder = datacenter.vmFolder
if datastore_name: datastore = get_obj(content, [vim.Datastore], datastore_name) else: datastore = get_obj( content, [vim.Datastore], template.datastore[0].info.name)
# if None, get the first one cluster = get_obj(content, [vim.ClusterComputeResource], cluster_name)
if resource_pool: resource_pool = get_obj(content, [vim.ResourcePool], resource_pool) else: resource_pool = cluster.resourcePool
vmconf = vim.vm.ConfigSpec()
if datastorecluster_name: podsel = vim.storageDrs.PodSelectionSpec() pod = get_obj(content, [vim.StoragePod], datastorecluster_name) podsel.storagePod = pod
storagespec = vim.storageDrs.StoragePlacementSpec() storagespec.podSelectionSpec = podsel storagespec.type = 'create' storagespec.folder = destfolder storagespec.resourcePool = resource_pool storagespec.configSpec = vmconf
try: rec = content.storageResourceManager.RecommendDatastores( storageSpec=storagespec) rec_action = rec.recommendations[0].action[0] real_datastore_name = rec_action.destination.name except: real_datastore_name = template.datastore[0].info.name
datastore = get_obj(content, [vim.Datastore], real_datastore_name)
# set relospec relospec = vim.vm.RelocateSpec() relospec.datastore = datastore relospec.pool = resource_pool
clonespec = vim.vm.CloneSpec() clonespec.location = relospec clonespec.powerOn = power_on
print("cloning VM...") task = template.Clone(folder=destfolder, name=vm_name, spec=clonespec)
#返回结果函数,用于判断任务是否完成 return wait_for_task(task)

class VsphereVMCreateService(Service): __need_schedule__ = False
def execute(self, data, parent_data): vsphere_env = data.get_one_of_inputs('vsphere_env') vsphere_vm_name = data.get_one_of_inputs('vsphere_vm_name') vsphere_vm_ip = data.get_one_of_inputs('vsphere_vm_ip') vsphere_vm_subnetmask = data.get_one_of_inputs('vsphere_vm_subnetmask') vsphere_vm_gateway = data.get_one_of_inputs('vsphere_vm_gateway') vsphere_vm_dns = data.get_one_of_inputs('vsphere_vm_dns') vsphere_template_name = data.get_one_of_inputs('vsphere_template_name') vsphere_folder_name = data.get_one_of_inputs('vsphere_folder_name') vsphere_datacenter_name = data.get_one_of_inputs('vsphere_datacenter_name') vsphere_cluster_name = data.get_one_of_inputs('vsphere_cluster_name') vsphere_datastore_name = data.get_one_of_inputs('vsphere_datastore_name')
args = { "env": vsphere_env, "port": port, "no_ssl": no_ssl, "power_on": power_on, "vm_name": vsphere_vm_name, "vm_ip": vsphere_vm_ip, "vm_subnetmask": vsphere_vm_subnetmask, "vm_gateway": vsphere_vm_gateway, "vm_dns": vsphere_vm_dns, "template": vsphere_template_name, "datacenter_name": vsphere_datacenter_name, "cluster_name": vsphere_cluster_name, "vm_folder": vsphere_folder_name, "datastore_name": vsphere_datastore_name, "datastorecluster_name": vsphere_datastorecluster_name, "resource_pool": resource_pool }
try: si = None if args['no_ssl']: if args['env'] == 'prod': si = SmartConnectNoSSL( host=prod_host, user=prod_user, pwd=pord_password, port=args['port']) else: si = SmartConnectNoSSL( host=test_host, user=test_user, pwd=test_password, port=args['port'])
else: si = SmartConnect( host=args.host, user=args.user, pwd=args.password, port=args.port) # disconnect this thing atexit.register(Disconnect, si) content = si.RetrieveContent() template = None template = get_obj(content, [vim.VirtualMachine], args['template'])
if template: task_info_result = clone_vm( content, template, args['vm_name'], si, args['datacenter_name'], args['vm_folder'], args['datastore_name'], args['cluster_name'], args['resource_pool'], args['power_on'], args['datastorecluster_name']) #if args.opaque_network: #此功能关闭 # vm = get_obj(content, [vim.VirtualMachine], args.vm_name) # add_nic(si, vm, args.opaque_network) if task_info_result: print task_info_result #自定义规范定制虚拟机 vm = get_obj(content, [vim.VirtualMachine], args['vm_name']) task_info_result = ip_assign(vm, args['vm_ip'], args['vm_name'], args['vm_subnetmask'], args['vm_gateway'], args['vm_dns']) if task_info_result: #启动自定义后的虚拟机 print "PowerOn vm..." vm.PowerOn() #启动完毕,等待50s完全启动 time.sleep(50)
data.set_outputs('vm_ip', vsphere_vm_ip) return True else: print task_info_result data.set_outputs('ex_data', u"自定义虚拟机错误") return False else: print task_info_result data.set_outputs('ex_data', u"克隆虚拟机指定的参数错误或虚拟机名已重复") return False else: data.set_outputs('ex_data', u"虚拟机模板没有找到") return False except Exception as e: data.set_outputs('ex_data', e) logger.exception(e) return False
def outputs_format(self): return [ self.OutputItem(name=u'虚拟机IP', key='vm_ip', type='string'), self.OutputItem(name=u'报错信息', key='ex_data', type='string') ]
class VsphereVMCreateComponent(Component): name = u'创建生产虚拟机' code = 'vsphere_vm_create' bound_service = VsphereVMCreateService #form = settings.STATIC_URL + 'custom_atoms/vsphere/vsphere_vm_create.js'    form = '%stest_atoms/vsphere_vm_create.js' % settings.STATIC_URL
class VsphereTestVMCreateComponent(Component): name = u'创建测试虚拟机' code = 'vsphere_vm_create_test' bound_service = VsphereVMCreateService #form = settings.STATIC_URL + 'custom_atoms/vsphere/vsphere_test_vm_create.js'    form = '%stest_atoms/vsphere_vm_create_test.js' % settings.STATIC_URL



最终效果


ansible自动化:基础软件的自定义安装

K8SEASY:一键安装K8S高可用集群

运维思索:如何纳管服务器实现统一登录

Prometheus+k8s之告警通知

滴滴夜莺:从监控告警系统向运维平台演化

蓝鲸实现vsphere虚拟机交付 -虚拟机管理(VSPHERE)



你与世界

只差一个

公众号




您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存