藍鯨實現vsphere虛擬機交付 -虛擬機管理(VSPHERE)


藍鯨實現vsphere虛擬機交付 -虛擬機管理(VSPHERE)

介紹了虛擬機上架的交付流程,但是藍鯨標準運維自帶的原子不滿足我們我們的環境需求,因此我們需要針對VSPHERE單獨開發。

環境


藍鯨實現vsphere虛擬機交付 -虛擬機管理(VSPHERE)

其中

  1. 由於需要單獨開發標準運維原子,需要將藍鯨自帶的標準運維下架,然後部署從源碼開發的定製版的標準運維;
  1. 其中標準運維需要redis支持,否則無法運行,可參見github或gitee上的Tencent/bk-sops;
  1. 標準運維的原子開發需要學習藍鯨社區"早起鳥兒有蟲吃";

思路

1.使用vcenter自定義規範管理器直接從模板克隆虛擬機,定製過程中我們需要輸入以下參數:虛擬機名、虛擬機ip、模板名稱、數據中心、集群名、存放位置、存儲器名等;
2.新虛擬機啟動後,需要開機啟動修改內核參數、安裝藍鯨agent、修改zabbix-agent地址等,此步已經在模板中提前設置;
3.後續的步驟均以輸入的虛擬機IP為準,進行資產創建、cmdb註冊等;

虛擬機管理(VSPHERE)原子開發

1.原子前端開發

定義虛擬機管理需要輸入的參數 ,以web的形式展示

<code>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"
}
]
}
},

]
})();/<code>

具體的web展示如下:

藍鯨實現vsphere虛擬機交付 -虛擬機管理(VSPHERE)

2.原子後端開發

後端實現了虛擬機創建,主要由三部分組成:

(1)虛擬機克隆,根據pyvmomi的clone_vm.py 進行修改;

(2)自定義規範修改ip、主機名,也需要通過pyvmomi進行修改;

(3)啟動虛擬機,自定義規範訂製後的虛擬機是關機狀態的,我們需要實現開機的操作;

具體實現如下:

<code>#自行修改vcenter鏈接參數
vim vsphere.py
# -*- coding: utf-8 -*-
"""
vsphere虛擬機管理

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.
"""


import logging

from pipeline.conf import settings
from pipeline.core.flow.activity import Service

from pipeline.component_framework.component import Component
#vsphere 原子所需擴展
from pyVmomi import vim
from pyVim.connect import SmartConnect, SmartConnectNoSSL, Disconnect
import atexit
import argparse
import getpass
import json
import time

logger = logging.getLogger('celery')

__group_name__ = u"虛擬機管理(VSPHERE)"

#vsphere vcenter連接參數
host = "10.10.5.88"
user = "[email protected]"
password = "xxxxxxxxxx"
port = 443
no_ssl = True
power_on = False
resource_pool = False
vsphere_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/<code>

以上過程要注意:

  1. 日誌打印,幫助我們排查開發過程中的問題;
  1. ex_data,用於前段展示錯誤提示;
  1. 最後啟動虛擬機的時間,根據實際情況調整下,我設置在50秒;

3.最終效果

(1)填寫參數

藍鯨實現vsphere虛擬機交付 -虛擬機管理(VSPHERE)

(2)創建完成


藍鯨實現vsphere虛擬機交付 -虛擬機管理(VSPHERE)

總結

經過以上自動創建虛擬機,我們完成了初步的虛擬機交付,後續還需要添加跳板機、註冊cmdb等操作,涉及到跳板機管理(JUMP)、配置平臺自定義(CMDB)原子的開發。

注意在開發過程中要遵循標準插件開發規範:

  1. 分組命名規則是“系統名(系統英文縮寫)”,如“作業平臺(JOB)”;
  1. 標準插件編碼(code)使用下劃線方式,規則是“系統名_接口名”,如 job_execute_task;
  1. 後臺類名使用駝峰式,規則是“標準插件編碼+繼承類名”,如 JobExecuteTaskService;
  1. 前端 JS 文件目錄保持和系統名縮寫一致,JS 文件名保持和標準插件編碼一致;
  1. 參數 tagcode 命名規則是“系統名參數名”,這樣可以保證全局唯一;長度不要超過 20 個字符;


分享到:


相關文章: