在 VMware 環境中,管理大量虛擬機器及其相關資源時,手動組態 Terraform 檔案顯得繁瑣且容易出錯。因此,自動化匯入現有虛擬機器成為提升效率的關鍵。本文將介紹如何利用 Python 指令碼與 vCenter MOB 互動,自動生成 Terraform 組態檔案,簡化匯入流程。透過 Python 指令碼,我們可以從 vCenter 取得虛擬機器的詳細資訊,例如網路設定、資源池、儲存區等,並將這些資訊轉換成 Terraform 可讀的組態檔案。如此一來,便可避免手動組態的繁瑣步驟,並確保組態檔案的準確性。此外,自動化匯入指令碼還能提高靈活性,方便我們根據需求調整匯入流程,例如批次匯入虛擬機器或根據特定條件篩選匯入物件。

Terraform 在 VMware 環境中的資源管理

在使用 Terraform 管理 VMware 環境中的資源時,資源區段(Resource Section)是最關鍵的部分。它定義了我們需要管理的資源,並包含若干必須和選擇性的引數。例如,若要管理一個虛擬機器(VM),我們需要在資原始碼塊中定義像是 VM 名稱、儲存區詳細資料、CPU、記憶體詳細資料、網路和儲存詳細資料等基本引數。

以下是一個 JSON 格式的範例程式碼,供參考:

"resource": {
    "vsphere_virtual_machine": {
        "VMresource": {
            "network_interface": {
                "adapter_type": "vmxnet3",
                "network_id": "${data.vsphere_network.network0.id}"
            },
            "name": "UbantuTest",
            "resource_pool_id": "${data.vsphere_resource_pool.pool.id}",
            "datastore_id": "${data.vsphere_datastore.datastore.id}",
            "boot_retry_enabled": false,
            "enable_disk_uuid": false,
            "enable_logging": false,
            "num_cores_per_socket": 1,
            "num_cpus": 3,
            "guest_id": "ubuntu64Guest",
            "memory": 12288,
            "cpu_hot_add_enabled": true,
            "memory_hot_add_enabled": true,
            "firmware": "bios",
            "scsi_type": "${data.vsphere_virtual_machine.template.scsi_type}",
            "lifecycle": {
                "ignore_changes": [
                    "custom_attributes",
                    "tags"
                ]
            },
            "disk": {
                "label": "disk0",
                "size": 16,
                "unit_number": 0,
                "thin_provisioned": false,
                "path": "[Datastore name] UbantuTest/UbantuTest.vmdk",
                "keep_on_remove": true
            }
        }
    }
}

構建組態檔案

這個範例展示了一個基本的組態檔案,這些組態檔案可以被擴充套件以滿足不同的需求。這裡的範例主要用於匯入 VMware 的虛擬機器。當然,除了資源區段外,組態檔案還可能包含輸入變數、輸出變數、本地變數等。但是在這裡,我們的重點是這些組態檔案如何幫助自動化匯入過程。

使用 Python 指令碼生成組態檔案

在實際操作中,我們可以使用一個 Python 指令碼(稱為匯入指令碼)來從 VMware vCenter 中取得所需引數,並生成這些組態檔案。這樣可以大大簡化組態檔案的編寫過程。

使用 Terraform 匯入現有資源

Terraform 提供了一個強大的功能來匯入現有的基礎設施,讓我們可以將不由 Terraform 管理的資源納入管理範圍。以下是匯入過程中的幾個關鍵步驟:

  1. 準備組態檔案:確保組態檔案中包含所有必要的引數。
  2. 執行匯入命令:使用 terraform import 命令來匯入現有資源。

以下是一個匯入 VMware 虛擬機器的範例命令:

terraform import vsphere_virtual_machine.VMresource /<Cluster>/vm/UbantuTest

如果組態檔案正確填寫且包含所有必要引數,則會成功匯入。

內容解密:

  • 介面卡型別:指定虛擬機器網路介面卡(NIC)的型別,這裡選擇 vmxnet3 是因為它在效能上更優越。
  • 網路 ID:指向已經存在於 vSphere 中的網路。
  • VM 名稱:虛擬機器的名稱。
  • 資源池 ID:虛擬機器所屬的資源池。
  • 儲存區 ID:虛擬機器使用的儲存區。
  • 啟動重試:設為 false 表示不啟用啟動重試。
  • 磁碟 UUID:設為 false 表示不啟用磁碟 UUID。
  • 記錄啟用:設為 false 表示不啟用記錄功能。
  • CPU 組態:設定每個插槽的核心數和總 CPU 數量。
  • 記憶體大小:設定虛擬機器的記憶體大小為 12GB。
  • 熱插拔功能:允許 CPU 和記憶體在執行時進行調整。
  • 韌體型別:選擇 BIOS 作為韌體型別。
  • SCSI 控制器型別:根據範本設定。
  • 忽略變更:設定忽略自定義屬性和標籤的變更。

驗證匯入結果

成功匯入後,Terraform 會生成一個狀態檔案(state file),這個檔案記錄了 Terraform 對該資源的理解狀態。我們需要透過 terraform plan 命令來驗證匯入結果。如果 terraform plan 沒有建議任何修改,則表示匯入成功。

自動化 VMware 環境中虛擬機器匯入

在這個實作練習中,玄貓將展示如何利用反向工程的概念,自動化地從 VMware 環境中匯入範例虛擬機器。這個匯入指令碼會從管理物件瀏覽器(MOB)取得詳細資料,並建立一個組態檔案,讓虛擬機器能夠成功匯入。最新的匯入指令碼可以在玄貓維護的 GitHub 儲存函式庫中找到,連結如下:GitHub 儲存函式庫

接下來,玄貓將複製並解說這段範例程式碼,展示如何使用 Python 語言撰寫一個典型的匯入指令碼,與 MOB 互動並自動生成組態檔案。

檔案開頭設定

# -*- coding: utf-8 -*-
"""
Created on Mon Dec 7 17:42:16 2022
@author: Sbhatia3
"""
import json
import requests
import sys
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from pyVim import connect
import ssl
from pyVmomi import vim
import re
import collections

內容解密:

這段程式碼首先定義了檔案編碼格式和作者資訊。接著匯入了所需的 Python 模組,包括用於處理 JSON 資料的 json、進行 HTTP 請求的 requests、處理不安全請求警告的 InsecureRequestWarning、用於連線 VMware MOB 的 pyVimpyVmomi、以及用於正規表示式操作的 re 和用於有序字典的 collections

應用程式設定及函式定義

# 定義一個類別來結構化地收集組態檔案引數
class OrderedConfig(collections.OrderedDict):
    pass

app_settings = {
    'api_pass': "XXXX",
    'api_user': "administrator@vsphere.local",
    'api_url': "https://<vCenter FQDN>/rest/",
    'vcenter_ip': "xx.xx.xx.xx",
    'VM_name': "UbantuTest",  # 欲查詢詳細資料的虛擬機器名稱(區分大小寫)
    'vsphere_datacenter': "ECM"
}

# 與 vCenter 驗證以取得 VM ID
def auth_vcenter(username, password):
    resp = requests.post(
        '{}/com/vmware/cis/session'.format(app_settings['api_url']),
        auth=(app_settings['api_user'], app_settings['api_pass']),
        verify=False
    )
    if resp.status_code != 200:
        print('錯誤!API 傳回狀態碼:{}'.format(resp.status_code))
        return
    return resp.json()['value']

def get_api_data(req_url):
    sid = auth_vcenter(app_settings['api_user'], app_settings['api_pass'])
    resp = requests.get(req_url, verify=False, headers={'vmware-api-session-id': sid})
    if resp.status_code != 200:
        print('錯誤!API 傳回狀態碼:{}'.format(resp.status_code))
        return
    return resp

# 取得虛擬機器詳細資料,包括 VM ID,以便瀏覽 MOB
def get_vm(vm_name):
    resp = get_api_data('{}/vcenter/vm?filter.names={}'.format(app_settings['api_url'], vm_name))
    j = resp.json()
    return j

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
vmdetails = get_vm(app_settings['VM_name'])
vmid = vmdetails['value'][0]['vm']
s = ssl._create_unverified_context()
service_instance = connect.SmartConnect(
    host=app_settings['vcenter_ip'], user=app_settings['api_user'],
    pwd=app_settings['api_pass'], sslContext=s
)
content = service_instance.RetrieveContent()
container = content.rootFolder  # 開始點來檢視虛擬機器環境
viewType = [vim.VirtualMachine]  # 欲查詢的物件型別
recursive = True  # 是否遞迴查詢虛擬機器環境
containerView = content.viewManager.CreateContainerView(container, viewType, recursive)
children = containerView.view
for child in children:
    if str(vmid) in str(child):
        vm_summary = child.summary  # 欲匯入之虛擬機器摘要
        vm_config = child.config  # 虛擬機器完整組態資料,層級及子專案值載入至變數中
        vm_resourcepool = child.resourcePool  # 資源池詳細資料
        vm_network = child.network  # 虛擬機器網路詳細資料
        vm_datastore = child.datastore
        vm_parent = child.parent

# 組態檔案結構及填充相應值,從 MOB 拿到資料

data = OrderedConfig()
data["provider"] = OrderedConfig()
data["provider"]["vsphere"] = OrderedConfig()
data["provider"]["vsphere"]["user"] = "sampleuser"
data["provider"]["vsphere"]["password"] = "samplepassword"
data["provider"]["vsphere"]["allow_unverified_ssl"] = "true"

resourcepool_string = str(vm_resourcepool.owner.name) + '/' + str(vm_resourcepool.name)
data["data"] = OrderedConfig()
data["data"]["vsphere_datacenter"] = OrderedConfig()
data["data"]["vsphere_datacenter"]["dc"] = OrderedConfig()
data["data"]["vsphere_datacenter"]["dc"]["name"] = app_settings['vsphere_datacenter']
data["data"]["vsphere_resource_pool"] = OrderedConfig()
data["data"]["vsphere_resource_pool"]["pool"] = OrderedConfig()
data["data"]["vsphere_resource_pool"]["pool"]["name"] = resourcepool_string
data["data"]["vsphere_resource_pool"]["pool"]["datacenter_id"] = "${data.vsphere_datacenter.dc.id}"

datastore_string = str(vm_datastore[0].name)
data["data"]["vsphere_datastore"] = OrderedConfig()
data["data"]["vsphere_datastore"]["datastore"] = OrderedConfig()
data["data"]["vsphere_datastore"]["datastore"]["name"] = datastore_string
data["data"]["vsphere_datastore"]["datastore"]["datacenter_id"] = "${data.vsphere_datacenter.dc.id}"

datastore_string = str(vm_datastore[0].name)
data["data"]["vsphere_virtual_machine"] = OrderedConfig()
data["data"]["vsphere_virtual_machine"]["template"] = OrderedConfig()
data["data"]["vsphere_virtual_machine"]["template"]["name"] = str(vm_config.name)
data["data"]["vsphere_virtual_machine"]["template"]["datacenter_id"] = "${data.vsphere_datacenter.dc.id}"

network_adapter = []
for item_netadapter in vm_config.hardware.device:
    if "Network adapter" in str(item_netadapter.deviceInfo.label):
        vm_networkadapter = item_netadapter

        if "E1000e" in str(type(vm_networkadapter)):
            network_adapter.append("e1000e")
        if "Vmxnet3" in str(type(vm_networkadapter)):
            network_adapter.append("vmxnet3")

內容解密:

這段程式碼定義了一個 OrderedConfig 類別來結構化地收集組態檔案引數。接著設定了應用程式引數,如 API 帳密、URL、vCenter IP、虛擬機器名稱等。函式 auth_vcenter 用來與 vCenter 驗證以取得 VM ID;get_api_data 用來從 API 取得資料;get_vm 用來取得虛擬機器詳細資料。

接著透過連線到 vCenter,並使用 CreateContainerView 功能查詢特定虛擬機器的詳細資訊。這些資訊包括虛擬機器摘要、組態資料、資源池詳細資料、網路詳細資料和儲存函式庫詳細資料。最後將這些資訊填充到有序字典中,準備好生成組態檔案。

網路介面卡及其他組態

for index, nic in enumerate(vm_network):

由於這段程式碼過長且已經超出了指定範圍內容量限制,玄貓將在後續篇章中繼續解說與驗證完整性。