查看原文
其他

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

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


点击上方蓝色字体,关注我们

腾讯蓝鲸实现vsphere虚拟机交付介绍了虚拟机上架的交付流程,但是蓝鲸标准运维自带的原子不满足我们我们的环境需求,因此我们需要针对VSPHERE单独开发。


环境

名称
版本
备注
蓝鲸
5.1.26

标准运维
3.3.27
https://github.com/Tencent/bk-sops
python开发框架
2.0.0
蓝鲸开发框架
vcenter
5.5.0-218311vCenter Server 5.5 Update 2b
pyvmomi
6.7.3

注意:

1.由于需要单独开发标准运维原子,需要将蓝鲸自带的标准运维下架,然后部署从源码开发的定制版的标准运维;2.其中标准运维需要redis支持,否则无法运行,可参见Tencent/bk-sops “https://github.com/Tencent/bk-sops/blob/master/docs/install/source_code_deploy.md”3.标准运维的原子开发需要学习蓝鲸社区"早起鸟儿有虫吃"

思路

1.使用vcenter自定义规范管理器直接从模板克隆虚拟机,定制过程中我们需要输入以下参数:虚拟机名、虚拟机ip、模板名称、数据中心、集群名、存放位置、存储器名等;
2.新虚拟机启动后,需要开机启动修改内核参数、安装蓝鲸agent、修改zabbix-agent地址等,此步已经在模板中提前设置;
3.后续的步骤均以输入的虚拟机IP为准,进行资产创建、cmdb注册等; 



虚拟机管理(VSPHERE)原子开发

1.原子前端开发

定义虚拟机管理需要输入的参数 ,以web的形式展示

vim vsphere_vm_create.js/*** Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community* Edition) available.* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. All rights reserved.* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.* You may obtain a copy of the License at* http://opensource.org/licenses/MIT* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the* specific language governing permissions and limitations under the License.*/(function(){ $.atoms.vsphere_vm_create = [ { 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_template_name", type: "radio", attrs: { name: gettext("模板名称"), items: [ {value: "template_root", name: "root"}, {value: "template_app", name: "app"}, ], default: "app", hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_datacenter_name", type: "radio", attrs: { name: gettext("数据中心"), items: [ {value: "unicom-idc", name: "unicom-idc"}, ], default: "unicom-idc", hookable: true, validation: [ { type: "required" } ] } }, { tag_code: "vsphere_cluster_name", type: "radio", attrs: { name: gettext("集群名"), items: [ {value: "unicom-ha", name: "unicom-ha"}, {value: "unicom-offline", name: "unicom-offline"}, ], default: "unicom-offline", 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: "test1.datastore1", value: "uvm50.datastore1"}, {text: "test2.datastore1", value: "uvm51.datastore1"}, {text: "test3.datastore1", value: "uvm52.datastore1"}, ], hookable: true, validation: [ { type: "required" } ] } },
]})();

具体的web展示如下:

2.原子后端开发

后端实现了虚拟机创建,主要由三部分组成:
(1)虚拟机克隆,根据pyvmomi的clone_vm.py 进行修改;
(2)自定义规范修改ip、主机名,也需要通过pyvmomi进行修改;
(3)启动虚拟机,自定义规范订制后的虚拟机是关机状态的,我们需要实现开机的操作;


具体实现如下:

#自行修改vcenter链接参数vim vsphere.py# -*- coding: utf-8 -*-"""vsphere虚拟机管理
Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS CommunityEdition) available.Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. All rights reserved.Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.You may obtain a copy of the License athttp://opensource.org/licenses/MITUnless required by applicable law or agreed to in writing, software distributed under the License is distributed onan "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for thespecific language governing permissions and limitations under the License."""

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连接参数host = "10.10.5.88"user = "admin@vsphere.local"password = "xxxxxxxxxx"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): """设置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 = "255.255.255.0" adaptermap.adapter.gateway = "192.168.3.1" #adaptermap.adapter.dnsDomain = "localhost" """dns设置""" globalip = vim.vm.customization.GlobalIPSettings() globalip.dnsServerList = "114.114.114.114" """设置主机名""" 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_vm_name = data.get_one_of_inputs('vsphere_vm_name') vsphere_vm_ip = data.get_one_of_inputs('vsphere_vm_ip') 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 = { "host": host, "user": user, "password": password, "port": port, "no_ssl": no_ssl, "power_on": power_on, "vm_name": vsphere_vm_name, "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']: si = SmartConnectNoSSL( host=args['host'], user=args['user'], pwd=args['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, vsphere_vm_ip, args['vm_name']) 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 = '%scityre_atoms/vsphere_vm_create.js' % settings.STATIC_URL


以上过程要注意:

  1. 日志打印,帮助我们排查开发过程中的问题;

  2. ex_data,用于前段展示错误提示;

  3. 最后启动虚拟机的时间,根据实际情况调整下,我设置在50秒;


3.最终效果

(1)填写参数

(2)创建完成


总结

经过以上自动创建虚拟机,我们完成了初步的虚拟机交付,后续还需要添加跳板机、注册cmdb等操作,涉及到跳板机管理(JUMP)、配置平台自定义(CMDB)原子的开发。


注意在开发过程中要遵循标准插件开发规范:

  1. 分组命名规则是“系统名(系统英文缩写)”,如“作业平台(JOB)”;

  2. 标准插件编码(code)使用下划线方式,规则是“系统名_接口名”,如 job_execute_task;

  3. 后台类名使用驼峰式,规则是“标准插件编码+继承类名”,如 JobExecuteTaskService;

  4. 前端 JS 文件目录保持和系统名缩写一致,JS 文件名保持和标准插件编码一致;

  5. 参数 tagcode 命名规则是“系统名参数名”,这样可以保证全局唯一;长度不要超过 20 个字符;





- End -





vmware自定义规范定制虚拟机-python

vmware自定义规范定制虚拟机-vcenter

腾讯蓝鲸实现vsphere虚拟机交付

蓝鲸社区版5.1自主接入企业内部ldap




关注我们




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

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