This is the 19th day of my participation in the August Wenwen Challenge.More challenges in August

Introduction to the

Vsphere Client, a custom specification of vcenter, realizes vm customization on a graphical interface. Although the operation is simplified to some extent, I think the final application scenario should be access to our internal automation platform to connect CMDB, jump-board, monitoring and other components. So let’s continue to introduce the Python module Pyvmomi to achieve virtual machine customization, hoping to give you some inspiration.

Environment to prepare

The name of the version note
vCenter 5.5.0-218311. VCenter Server5.5 Update 2 b
Centos 7.5 The delivered IP address of the template is 192.168.3.253
pyvmomi 6.7.3
python 2.7.15

The Pyvmomi module provides some examples, which we modified with the clone_vm.py example.

Vm customization process

Different from vsphere Client, PyVmomi implements custom specification customization. Its process is as follows:

  1. Run the clone_VM function to clone a VM from a template. Do not start the VM after cloning.

  2. After cloning, the ip_assign function is used to customize according to custom specifications in shutdown state.

  3. After the customization is complete, the VM is shut down and needs to be started using the powerOn function.

implementation

1. Configure the network

def ip_assign(vm, vm_ip, vm_name) :
    """ Set IP address """
    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 Settings "" "
    globalip = vim.vm.customization.GlobalIPSettings()
    globalip.dnsServerList = "114.114.114.114"
    """ Set host name """
    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 = vm.Customize(spec=customspec)
    wait_for_task(task)
Copy the code

The above mainly implement IP address Settings, DNS Settings, host name Settings, because we have defined CPU, MEMORY, DISK and other Settings in the template, based on the template for customization, so there is no definition in this respect.

2. Start the VM

By default, after customization is completed, the VIRTUAL machine is in the shutdown state. We need to start the operation to integrate with other processes.

Get obj from vm name
vm = get_obj(content, [vim.VirtualMachine], args.vm_name)
Perform boot operation
task = vm.PowerOn()
wait_for_task(task)
Copy the code

3. Clone_vm_customize. Py

Clone_vm. py is only a clone of a VM template and does not support custom specifications. Therefore, you can customize a clone template by integrating network configuration and VM startup.

Clone_vm. py is consolidated as follows:


vim clone_vm_customize.py
#! /usr/bin/env python
#-*- coding: utf-8 -*-
""" Written by Dann Bohn Github: https://github.com/whereismyjetpack Email: [email protected] Clone a VM from template example """

from pyVmomi import vim
from pyVim.connect import SmartConnect, SmartConnectNoSSL, Disconnect
import atexit
import argparse
import getpass

from add_nic_to_vm import add_nic


def get_args() :
    """ Get arguments from CLI """
    parser = argparse.ArgumentParser(
        description='Arguments for talking to vCenter')

    parser.add_argument('-s'.'--host',
                        required=True,
                        action='store'.help='vSpehre service to connect to')

    parser.add_argument('-o'.'--port'.type=int,
                        default=443,
                        action='store'.help='Port to connect on')

    parser.add_argument('-u'.'--user',
                        required=True,
                        action='store'.help='Username to use')

    parser.add_argument('-p'.'--password',
                        required=False,
                        action='store'.help='Password to use')

    parser.add_argument('-v'.'--vm-name',
                        required=True,
                        action='store'.help='Name of the VM you wish to make')

    parser.add_argument('--no-ssl',
                        action='store_true'.help='Skip SSL verification')

    parser.add_argument('--template',
                        required=True,
                        action='store'.help='Name of the template/VM \ you are cloning from')

    parser.add_argument('--datacenter-name',
                        required=False,
                        action='store',
                        default=None.help='Name of the Datacenter you\ wish to use. If omitted, the first\ datacenter will be used.')

    parser.add_argument('--vm-folder',
                        required=False,
                        action='store',
                        default=None.help='Name of the VMFolder you wish\ the VM to be dumped in. If left blank\ The datacenter VM folder will be used')

    parser.add_argument('--datastore-name',
                        required=False,
                        action='store',
                        default=None.help='Datastore you wish the VM to end up on\ If left blank, VM will be put on the same \ datastore as the template')

    parser.add_argument('--datastorecluster-name',
                        required=False,
                        action='store',
                        default=None.help='Datastorecluster (DRS Storagepod) you wish the VM to end up on \ Will override the datastore-name parameter.')

    parser.add_argument('--cluster-name',
                        required=False,
                        action='store',
                        default=None.help='Name of the cluster you wish the VM to\ end up on. If left blank the first cluster found\ will be used')

    parser.add_argument('--resource-pool',
                        required=False,
                        action='store',
                        default=None.help='Resource Pool to use. If left blank the first\ resource pool found will be used')

    parser.add_argument('--power-on',
                        dest='power_on',
                        action='store_true'.help='power on the VM after creation')

    parser.add_argument('--opaque-network',
                        required=False.help='Name of the opaque network to add to the VM')

    args = parser.parse_args()

    if not args.password:
        args.password = getpass.getpass(
            prompt='Enter password')

    return args

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("there was an error")
            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) :
    Custom specification Settings
    """ Set IP address """
    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 Settings "" "
    globalip = vim.vm.customization.GlobalIPSettings()
    globalip.dnsServerList = "114.114.114.114"
    """ Set host name """
    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)

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)
    wait_for_task(task)


def main() :
    """ Let this thing fly """
    args = get_args()

    # connect this thing
    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:
        # clone template
        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)
        vm = get_obj(content, [vim.VirtualMachine], args.vm_name)
        if args.opaque_network:
            add_nic(si, vm, args.opaque_network)
        
        # Customize specifications Customize VMS
        ip_assign(vm, "192.168.3.254", args.vm_name)
    else:
        print("template not found")
    
    # Start the VM
    vm = get_obj(content, [vim.VirtualMachine], args.vm_name)
    task = vm.PowerOn()
    wait_for_task(task)

# start this thing
if __name__ == "__main__":
    main()
Copy the code

4. Run commands

# - s: vcenter address
# - u: vcenter account
# - p: vcneter password
#-v: indicates the VM name and host name we123
#--template: the name of the clone template template_centos7
#--datacenter-name: datacenter unicom-idc
#-- Vm-folder: The test folder where the new VM resides
#--datastore-name: indicates the storage vm to which the VM is mounted. Datastore1
#--cluster-name: indicates the IDC in the vm cluster
python clone_vm_customize.py -s 192.1683..xxx -u [email protected] -p xxxxxxx -v we123 --template template_centos7 --datacenter-name unicom-idc --vm-folder test --datastore-name vm.datastore1 --cluster-name idc  --no-ssl
Copy the code

After the command is executed, customize vm networks based on the ip_assign(VM, “192.168.3.254”, args.vm_name) as follows:

  • IP address: 192.168.3.254;

  • The subnet mask is 255.255.255.0.

  • Gateway: 192.168.3.1;

  • DNS: 114.114.114.114;

  • Vm name and host name: we123.

conclusion

After comparison, the vm customization process can deliver a VM in less than 3 minutes, greatly improving o&M efficiency. As for the remaining work of the delivered VIRTUAL machine, we need to connect with other components based on our actual situation.