]> Frank Brehm's Git Trees - pixelpark/create-vmware-tpl.git/commitdiff
Created virtual machine
authorFrank Brehm <frank.brehm@pixelpark.com>
Fri, 23 Mar 2018 16:52:55 +0000 (17:52 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Fri, 23 Mar 2018 16:52:55 +0000 (17:52 +0100)
etc/create-vmware-template.ini.default
lib/cr_vmware_tpl/config.py
lib/cr_vmware_tpl/handler.py

index a58ab1eb12481f43acdea2b72e165acd1d6054c6..ad9c3b20c99e2f04e58e6c581b09c5ac6f0867c9 100644 (file)
 
 ;data_size_gb = 30
 
+;num_cpus = 2
+
+;ram_mb = 4096
+
 ;network = 192.168.88.0_23
 
+;mac_address = 00:50:56:01:af:fe
+
+
+
+
+
+
 ; vim: filetype=dosini
index 5a661a09f53c62fe314412415ab84540b4bc4808..784281db7483a7f8cb0e3c829f97312b4197f505 100644 (file)
@@ -26,7 +26,7 @@ from .errors import FunctionNotImplementedError, PpError
 
 from .obj import PpBaseObject
 
-__version__ = '0.4.2'
+__version__ = '0.4.3'
 LOG = logging.getLogger(__name__)
 
 
@@ -53,7 +53,10 @@ class CrTplConfiguration(PpBaseObject):
     default_template_vm = 'template.pixelpark.com'
     default_template_name = 'oracle-linux-7.4-template'
     default_data_size_gb = 30.0
+    default_num_cpus = 2
+    default_ram_mb = 4 * 1024
     default_network = '192.168.88.0_23'
+    default_mac_address = '00:50:56:01:af:fe'
 
     # -------------------------------------------------------------------------
     def __init__(
@@ -69,7 +72,10 @@ class CrTplConfiguration(PpBaseObject):
         self.template_vm = self.default_template_vm
         self.template_name = self.default_template_name
         self.data_size_gb = self.default_data_size_gb
+        self.num_cpus = self.default_num_cpus
+        self.ram_mb = self.default_ram_mb
         self.network = self.default_network
+        self.mac_address = self.default_mac_address
 
         self.encoding = 'utf-8'
 
@@ -95,19 +101,25 @@ class CrTplConfiguration(PpBaseObject):
     @property
     def data_size_mb(self):
         """Size of template volume in MiB."""
-        return int(self.data_size_gb) * 1024
+        return int(self.data_size_gb * 1024.0)
 
     # -------------------------------------------------------------------------
     @property
     def data_size_kb(self):
         """Size of template volume in KiB."""
-        return int(self.data_size_gb) * 1024 * 1024
+        return self.data_size_mb * 1024
 
     # -------------------------------------------------------------------------
     @property
     def data_size(self):
         """Size of template volume in Bytes."""
-        return int(self.data_size_gb) * 1024 * 1024 * 1024
+        return self.data_size_mb * 1024 * 1024
+
+    # -------------------------------------------------------------------------
+    @property
+    def ram_gb(self):
+        """Size of RAM in GiB."""
+        return float(self.ram_mb) / 1024
 
     # -------------------------------------------------------------------------
     def as_dict(self, short=True):
@@ -130,10 +142,14 @@ class CrTplConfiguration(PpBaseObject):
         res['default_template_vm'] = self.default_template_vm
         res['default_template_name'] = self.default_template_name
         res['default_data_size_gb'] = self.default_data_size_gb
+        res['default_num_cpus'] = self.default_num_cpus
+        res['default_ram_mb'] = self.default_ram_mb
         res['default_network'] = self.default_network
+        res['default_mac_address'] = self.default_mac_address
         res['data_size_mb'] = self.data_size_mb
         res['data_size_kb'] = self.data_size_kb
         res['data_size'] = self.data_size
+        res['ram_gb'] = self.ram_gb
 
         return res
 
@@ -238,8 +254,18 @@ class CrTplConfiguration(PpBaseObject):
                         self.data_size_gb = float(value) / 1024.0 / 1024.0
                     if key.lower() == 'data_size':
                         self.data_size_gb = float(value) / 1024.0 / 1024.0 / 1024.0
+                    if key.lower() == 'num_cpus':
+                        self.num_cpus = int(value)
+                    if key.lower() == 'ram_gb':
+                        self.ram_mb = int(float(value) * 1024.0)
+                    if key.lower() == 'ram_mb':
+                        self.ram_mb = int(value)
                     if key.lower() == 'network':
                         self.network = value.strip()
+                    if key.lower() == 'mac_address':
+                        v = value.strip().lower()
+                        if v:
+                            self.mac_address = v
 
 
 # =============================================================================
index 13798ea6646c33c09293710604291e4b63b7755f..946f74694e0873664a6ab979daea606f2ee4fb37 100644 (file)
@@ -15,22 +15,25 @@ import logging
 import ssl
 import re
 import random
+import time
 
 # Third party modules
 import six
 
-from pyVmomi import vim
+from pyVmomi import vim, vmodl
 
 from pyVim.connect import SmartConnect, Disconnect
 
 # Own modules
+from .common import pp
+
 from .errors import FunctionNotImplementedError, PpError
 
 from .obj import PpBaseObject
 
 from .config import CrTplConfiguration
 
-__version__ = '0.4.1'
+__version__ = '0.4.2'
 LOG = logging.getLogger(__name__)
 
 
@@ -147,7 +150,7 @@ class CrTplHandler(PpBaseObject):
         )
 
         self.config = config
-        self.server_instance = None
+        self.service_instance = None
         self.tpl_vm_folder = None
         self.tpl_data_store = None
         self.tpl_network = None
@@ -176,12 +179,12 @@ class CrTplHandler(PpBaseObject):
         if hasattr(ssl, '_create_unverified_context'):
             ssl_context = ssl._create_unverified_context()
 
-        self.server_instance = SmartConnect(
+        self.service_instance = SmartConnect(
             host=self.config.vsphere_host, port=self.config.vsphere_port,
             user=self.config.vsphere_user, pwd=self.config.password,
             sslContext=ssl_context)
 
-        if not self.server_instance:
+        if not self.service_instance:
             raise CannotConnectError(
                 host=self.config.vsphere_host, port=self.config.vsphere_port,
                 user=self.config.vsphere_user)
@@ -192,10 +195,11 @@ class CrTplHandler(PpBaseObject):
             self.check_for_temp_tpl_vm()
             self.select_data_store()
             self.check_network()
+            self.create_vm()
         finally:
             LOG.debug("Disconnecting from vSphere host {h}:{p} ...".format(
                 h=self.config.vsphere_host, p=self.config.vsphere_port))
-            Disconnect(self.server_instance)
+            Disconnect(self.service_instance)
 
     # -------------------------------------------------------------------------
     def get_obj(self, content, vimtype, name):
@@ -220,11 +224,60 @@ class CrTplHandler(PpBaseObject):
 
         return result
 
+    # -------------------------------------------------------------------------
+    def wait_for_tasks(self, tasks):
+
+        LOG.debug("Waiting for tasks to finish ...")
+
+        property_collector = self.service_instance.content.propertyCollector
+        task_list = [str(task) for task in tasks]
+        LOG.debug("Waiting for tasks {} to finish ...".format(task_list))
+        # Create filter
+        obj_specs = [vmodl.query.PropertyCollector.ObjectSpec(obj=task)
+                    for task in tasks]
+        property_spec = vmodl.query.PropertyCollector.PropertySpec(
+            type=vim.Task, pathSet=[], all=True)
+        filter_spec = vmodl.query.PropertyCollector.FilterSpec()
+        filter_spec.objectSet = obj_specs
+        filter_spec.propSet = [property_spec]
+        pcfilter = property_collector.CreateFilter(filter_spec, True)
+        try:
+            version, state = None, None
+            # Loop looking for updates till the state moves to a completed state.
+            while len(task_list):
+                update = property_collector.WaitForUpdates(version)
+                for filter_set in update.filterSet:
+                    time.sleep(0.1)
+                    LOG.debug("Waiting ...")
+                    for obj_set in filter_set.objectSet:
+                        task = obj_set.obj
+                        for change in obj_set.changeSet:
+                            if change.name == 'info':
+                                state = change.val.state
+                            elif change.name == 'info.state':
+                                state = change.val
+                            else:
+                                continue
+
+                            if not str(task) in task_list:
+                                continue
+
+                            if state == vim.TaskInfo.State.success:
+                                # Remove task from taskList
+                                task_list.remove(str(task))
+                            elif state == vim.TaskInfo.State.error:
+                                raise task.info.error
+                    # Move to next version
+                version = update.version
+        finally:
+            if pcfilter:
+                pcfilter.Destroy()
+
     # -------------------------------------------------------------------------
     def get_cluster(self, content=None):
 
         if not content:
-            content = self.server_instance.RetrieveContent()
+            content = self.service_instance.RetrieveContent()
 
         cluster_list = self.get_obj_list(content, [vim.ClusterComputeResource])
         if not len(cluster_list):
@@ -251,7 +304,7 @@ class CrTplHandler(PpBaseObject):
     def get_tpl_folder(self, vm_folder=None):
 
         if not vm_folder:
-            content = self.server_instance.RetrieveContent()
+            content = self.service_instance.RetrieveContent()
             dc = self.get_obj(content, [vim.Datacenter], self.config.dc)
             vm_folder = dc.vmFolder
 
@@ -269,7 +322,7 @@ class CrTplHandler(PpBaseObject):
     # -------------------------------------------------------------------------
     def ensure_vm_folder(self):
 
-        content = self.server_instance.RetrieveContent()
+        content = self.service_instance.RetrieveContent()
         dc = self.get_obj(content, [vim.Datacenter], self.config.dc)
 
         tpl_vm_folder = self.get_tpl_folder(dc.vmFolder)
@@ -299,7 +352,7 @@ class CrTplHandler(PpBaseObject):
     # -------------------------------------------------------------------------
     def get_temp_tpl_vm(self):
 
-        content = self.server_instance.RetrieveContent()
+        content = self.service_instance.RetrieveContent()
         dc = self.get_obj(content, [vim.Datacenter], self.config.dc)
 
         for child in dc.vmFolder.childEntity:
@@ -329,7 +382,7 @@ class CrTplHandler(PpBaseObject):
     # -------------------------------------------------------------------------
     def check_network(self):
 
-        content = self.server_instance.RetrieveContent()
+        content = self.service_instance.RetrieveContent()
         dc = self.get_obj(content, [vim.Datacenter], self.config.dc)
 
         LOG.debug("Checking existence of network {!r} ...".format(self.config.network))
@@ -370,7 +423,7 @@ class CrTplHandler(PpBaseObject):
             "Selecting a SAN based datastore with at least {:0.1f} GiB available "
             "space.").format(self.config.data_size_gb))
 
-        content = self.server_instance.RetrieveContent()
+        content = self.service_instance.RetrieveContent()
         dc = self.get_obj(content, [vim.Datacenter], self.config.dc)
 
         ds_list = []
@@ -416,6 +469,110 @@ class CrTplHandler(PpBaseObject):
 
         return
 
+    # -------------------------------------------------------------------------
+    def create_vm(self):
+
+        LOG.info("Creating VM {!r} ...".format(self.config.template_vm))
+
+        datastore_path = '[' + self.tpl_data_store.summary.name + '] ' + self.config.template_vm
+        LOG.debug("Datastore path: {!r}".format(datastore_path))
+
+        vm_file_info = vim.vm.FileInfo(
+            logDirectory=None, snapshotDirectory=None,
+            suspendDirectory=None, vmPathName=datastore_path)
+
+        dev_changes = []
+
+        # Creating SCSI Controller and disk
+        scsi_ctr_spec = vim.vm.device.VirtualDeviceSpec()
+        scsi_ctr_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
+        scsi_ctr_spec.device = vim.vm.device.VirtualLsiLogicController()
+        scsi_ctr_spec.device.key = 0
+        #scsi_ctr_spec.device.controllerKey = 1000
+        scsi_ctr_spec.device.unitNumber = 1
+        scsi_ctr_spec.device.sharedBus = 'noSharing'
+        controller = scsi_ctr_spec.device
+
+        dev_changes.append(scsi_ctr_spec)
+
+        disk_spec = vim.vm.device.VirtualDeviceSpec()
+        disk_spec.fileOperation = "create"
+        disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
+        disk_spec.device = vim.vm.device.VirtualDisk()
+        disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
+        disk_spec.device.backing.diskMode = 'persistent'
+        disk_spec.device.backing.fileName = '{ds}/{vm}-sda.vmdk'.format(
+            ds=datastore_path, vm=self.config.template_vm)
+        disk_spec.device.unitNumber = 1
+        disk_spec.device.key = 1
+        disk_spec.device.capacityInKB = self.config.data_size_kb
+        disk_spec.device.controllerKey = controller.key
+
+        dev_changes.append(disk_spec)
+
+        # Creating network adapter
+        nic_spec = vim.vm.device.VirtualDeviceSpec()
+        nic_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
+        nic_spec.device = vim.vm.device.VirtualVmxnet3()
+        nic_spec.device.deviceInfo = vim.Description()
+        nic_spec.device.deviceInfo.label = 'eth0'
+        nic_spec.device.deviceInfo.summary = 'Primary network device'
+
+        nic_spec.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
+        nic_spec.device.backing.useAutoDetect = False
+        nic_spec.device.backing.network = self.tpl_network
+        nic_spec.device.backing.deviceName = self.config.network
+
+        nic_spec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
+        nic_spec.device.connectable.startConnected = True
+        nic_spec.device.connectable.allowGuestControl = True
+        nic_spec.device.wakeOnLanEnabled = False
+        nic_spec.device.addressType = 'assigned'
+        nic_spec.device.macAddress = self.config.mac_address
+
+        dev_changes.append(nic_spec)
+
+        # Graphic Card
+        video_spec = vim.vm.device.VirtualDeviceSpec()
+        video_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
+        video_spec.device = vim.vm.device.VirtualVideoCard()
+        video_spec.device.enable3DSupport = False
+        video_spec.device.graphicsMemorySizeInKB = 256 * 1024
+        video_spec.device.numDisplays = 1
+        video_spec.device.use3dRenderer = 'automatic'
+        video_spec.device.videoRamSizeInKB = 32 * 1024
+
+        dev_changes.append(video_spec)
+
+        # Some other flags
+        vm_flags = vim.vm.FlagInfo()
+        vm_flags.diskUuidEnabled = True
+
+        # Some extra options and properties
+        extra_opts = []
+        created_opt = vim.option.OptionValue()
+        created_opt.key = 'created'
+        created_opt.value = int(time.time())
+        extra_opts.append(created_opt)
+
+        config = vim.vm.ConfigSpec(
+            name=self.config.template_vm, deviceChange=dev_changes,
+            flags=vm_flags, extraConfig=extra_opts,
+            memoryMB=self.config.ram_mb, memoryHotAddEnabled=True,
+            numCPUs=self.config.num_cpus, cpuHotAddEnabled=True,
+            files=vm_file_info)
+            #files=vm_file_info, guestId='OracleLinux7_Guest')
+            #files=vm_file_info, guestId='OracleLinux7_Guest', version='oel7-4')
+
+        if self.verbose > 2:
+            LOG.debug("Generated VM config:\n{}".format(pp(config)))
+
+        LOG.debug("Start Creating VM ...")
+        task = self.tpl_vm_folder.CreateVM_Task(
+            config=config, pool=self.tpl_cluster.resourcePool)
+
+        self.wait_for_tasks([task])
+
 
 # =============================================================================