]> Frank Brehm's Git Trees - pixelpark/create-terraform.git/commitdiff
Implementing caching of vSphere VMs and templates.
authorFrank Brehm <frank.brehm@pixelpark.com>
Wed, 22 May 2024 15:57:19 +0000 (17:57 +0200)
committerFrank Brehm <frank.brehm@pixelpark.com>
Wed, 22 May 2024 15:57:19 +0000 (17:57 +0200)
lib/create_terraform/handler/__init__.py
lib/create_terraform/handler/vmware.py
lib/create_terraform/slim_vm.py [new file with mode: 0644]

index 4842ae7f6fb3bbae7f17ed4998e103ff3cba5c9b..a249a7cd8928202154a4f8263528f0fb8c353c5e 100644 (file)
@@ -46,7 +46,7 @@ from ..errors import AbortExecution
 
 from ..xlate import XLATOR
 
-__version__ = '3.10.0'
+__version__ = '4.0.0'
 LOG = logging.getLogger(__name__)
 
 _ = XLATOR.gettext
@@ -86,7 +86,7 @@ class CreateTerraformHandler(
 
     steps = (
         'init', 'vmw-init', 'read-yaml', 'pdns-zones', 'vmw-test', 'collect-folders',
-        'vmw-clusters', 'vmw-datastores', 'vmw-ds-clusters', 'vmw-networks', 'vmw-templates',
+        'vmw-clusters', 'vmw-datastores', 'vmw-ds-clusters', 'vmw-vms', 'vmw-networks', 'vmw-templates',
         'validate-yaml', 'validate-storage', 'validate-iface', 'validate-dns',
         'perform-dns', 'project-dir', 'tf-files', 'ensure-vmw-folders',
     )
@@ -98,6 +98,7 @@ class CreateTerraformHandler(
         'vmw-test': _('After testing VSPhere handlers.'),
         'collect-folders': _('After collecting all VMWare and local folders.'),
         'vmw-clusters': _('After collecting all VMWare clusters.'),
+        'vmw-vms': _('After caching all VMWare VMs and templates.'),
         'vmw-datastores': _('After collecting all VMWare datastores.'),
         'vmw-ds-clusters': _('After collecting all VMWare datastore clusters.'),
         'vmw-networks': _('After collecting all VMWare networks.'),
@@ -140,6 +141,8 @@ class CreateTerraformHandler(
 
         self.vsphere_folders = []
 
+        self.vsphere_vm_cache = []
+
         self.vsphere_user = None
         self.vsphere_password = None
 
@@ -387,6 +390,7 @@ class CreateTerraformHandler(
             LOG.info(_("Retrieving information from vSphere."))
 
             self.exec_vmw_clusters()
+            self.exec_cache_vmw_vms()
             self.exec_vmw_datastores()
             self.exec_vmw_ds_clusters()
             self.exec_vmw_networks()
index 0ee2620a13bac87802173f1f10ff17929746c918..4e847cc08f1127692dffdae34d3fe65f5e0e808b 100644 (file)
@@ -29,9 +29,11 @@ from fb_vmware.connect import VsphereConnection
 # Own modules
 from ..errors import AbortExecution
 
+from ..slim_vm import SlimVm
+
 from ..xlate import XLATOR
 
-__version__ = '0.1.3'
+__version__ = '0.2.0'
 LOG = logging.getLogger(__name__)
 
 _ = XLATOR.gettext
@@ -53,14 +55,14 @@ class CrTfHandlerVmwMixin():
         for vname in self.vsphere:
             self.vsphere[vname].get_datacenter()
 
-        LOG.debug(_("Collecting vSphere folders."))
+        LOG.debug(_("Collecting used vSphere folders."))
         self.vsphere_folders = []
         for vm in self.vms:
             if vm.folder:
                 if vm.folder not in self.vsphere_folders:
                     self.vsphere_folders.append(vm.folder)
         self.vsphere_folders.sort(key=str.lower)
-        LOG.debug(_("Collected vSphere folders:") + "\n" + pp(self.vsphere_folders))
+        LOG.debug(_("Collected used vSphere folders:") + "\n" + pp(self.vsphere_folders))
 
         # Set project name and directory
         yfile = Path(yaml_file)
@@ -243,6 +245,25 @@ class CrTfHandlerVmwMixin():
         if self.stop_at_step == 'vmw-clusters':
             raise AbortExecution('vmw-clusters')
 
+    # -------------------------------------------------------------------------·
+    def exec_cache_vmw_vms(self):
+
+        # if self.stop_at_step == 'vmw-vms':
+        #     self.incr_verbosity()
+
+        self.explore_vsphere_vms()
+
+        if self.eval_errors:
+            msg = ngettext(
+                "Found one error in exploring vSphere VMs.",
+                "Found {n} errors in exploring vSphere VMs.",
+                self.eval_errors).format(n=self.eval_errors)
+            raise ExpectedHandlerError(msg)
+
+        LOG.info(_("Finished step {!r}.").format('vmw-vms'))
+        if self.stop_at_step == 'vmw-vms':
+            raise AbortExecution('vmw-vms')
+
     # -------------------------------------------------------------------------·
     def exec_vmw_datastores(self):
 
@@ -428,57 +449,82 @@ class CrTfHandlerVmwMixin():
         if self.stop_at_step == 'ensure-vmw-folders':
             raise AbortExecution('ensure-vmw-folders')
 
+    # -------------------------------------------------------------------------·
+    def explore_vsphere_vms(self):
+
+        LOG.info(_("Exploring and caching all vSphere VMs and templates ..."))
+
+        for vsphere_name in self.vsphere:
+
+            re_vm = re.compile(r'.*')
+            vm_list = self.vsphere[vsphere_name].get_vms(re_vm, as_obj=True, stop_at_found=False)
+
+            for vm in vm_list:
+                slim_vm = SlimVm(vsphere_name, vm.name, vm.path, is_template=vm.template)
+                self.vsphere_vm_cache.append(slim_vm)
+
+        msg = ngettext(
+            'Found one VM or template in vSphere.',
+            'Found {nr} VMs and templates in vSphere.', len(self.vsphere_vm_cache))
+        msg = msg.format(nr=len(self.vsphere_vm_cache))
+        LOG.debug(msg)
+
+        if self.verbose > 1:
+            msg = _("All explored vSphere VMs and templates:")
+            LOG.debug(msg + "\n" + pp(self.vsphere_vm_cache))
+
     # -------------------------------------------------------------------------·
     def explore_vsphere_templates(self):
 
         LOG.info(_("Exploring all vSphere templates ..."))
 
-        for vname in self.vsphere:
+        for vsphere_name in self.vsphere:
 
-            if vname not in self.vsphere_templates:
-                self.vsphere_templates[vname] = {}
+            if vsphere_name not in self.vsphere_templates:
+                self.vsphere_templates[vsphere_name] = {}
 
-            self.config.vsphere[vname].used_templates = []
+            self.config.vsphere[vsphere_name].used_templates = []
 
             for vm in self.vms:
                 template_name = vm.vm_template
                 if template_name:
-                    if template_name not in self.config.vsphere[vname].used_templates:
-                        self.config.vsphere[vname].used_templates.append(template_name)
+                    if template_name not in self.config.vsphere[vsphere_name].used_templates:
+                        self.config.vsphere[vsphere_name].used_templates.append(template_name)
                 else:
                     LOG.error(_("VM {!r} has not template defined.").format(vm.name))
                     self.eval_errors += 1
 
-            msg = _("All {} VSPhere templates to explore:").format(vname)
-            msg += "\n" + pp(self.config.vsphere[vname].used_templates)
+            msg = _("All {} VSPhere templates to explore:").format(vsphere_name)
+            msg += "\n" + pp(self.config.vsphere[vsphere_name].used_templates)
             LOG.debug(msg)
 
-            for template_name in self.config.vsphere[vname].used_templates:
+            found_template = False
+            for template_name in self.config.vsphere[vsphere_name].used_templates:
 
-                if template_name in self.vsphere_templates[vname]:
+                if template_name in self.vsphere_templates[vsphere_name]:
                     continue
 
                 LOG.debug(_("Searching for template {t!r} in VSPhere {v!r} ...").format(
-                    t=template_name, v=vname))
+                    t=template_name, v=vsphere_name))
                 re_vm = re.compile(r'^' + re.escape(template_name) + r'$', re.IGNORECASE)
-                vm_list = self.vsphere[vname].get_vms(re_vm, as_obj=True, stop_at_found=True)
+                vm_list = self.vsphere[vsphere_name].get_vms(re_vm, as_obj=True, stop_at_found=True)
                 if vm_list:
                     vm = vm_list[0]
                     tname = vm.name.lower()
-                    if tname not in self.vsphere_templates[vname]:
-                        self.vsphere_templates[vname][template_name] = vm
+                    if tname not in self.vsphere_templates[vsphere_name]:
+                        self.vsphere_templates[vsphere_name][template_name] = vm
                 else:
                     LOG.error(_("Template {t!r} not found in VSPhere {v!r}.").format(
-                        t=template_name, v=vname))
+                        t=template_name, v=vsphere_name))
                     self.eval_errors += 1
 
         if self.verbose > 2:
             msg = _("All explored vSphere templates:")
             out_dict = {}
-            for vname in self.vsphere_templates:
-                out_dict[vname] = {}
-                for tname in self.vsphere_templates[vname]:
-                    out_dict[vname][tname] = self.vsphere_templates[vname][tname].as_dict()
+            for vsphere_name in self.vsphere_templates:
+                out_dict[vsphere_name] = {}
+                for tname in self.vsphere_templates[vsphere_name]:
+                    out_dict[vsphere_name][tname] = self.vsphere_templates[vsphere_name][tname].as_dict()
             msg += "\n" + pp(out_dict)
             LOG.debug(msg)
 
diff --git a/lib/create_terraform/slim_vm.py b/lib/create_terraform/slim_vm.py
new file mode 100644 (file)
index 0000000..6774a4e
--- /dev/null
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+@author: Frank Brehm
+@contact: frank.brehm@pixelpark.com
+@copyright: © 2024 by Frank Brehm, Berlin
+@summary: A module for providing a slim VM object.
+"""
+from __future__ import absolute_import
+
+# Standard module
+import logging
+
+# Third party modules
+from fb_tools.obj import FbGenericBaseObject
+
+from .xlate import XLATOR
+
+__version__ = '0.1.0'
+LOG = logging.getLogger(__name__)
+
+_ = XLATOR.gettext
+ngettext = XLATOR.ngettext
+
+
+# =============================================================================
+class SlimVm(FbGenericBaseObject):
+    """Class for encapsulation very basic data of a VM or a template in VMWare."""
+
+    # -------------------------------------------------------------------------
+    def __init__(self, vs_name, name, path, is_template=False):
+        """Initialize the SlimVm object."""
+        self._vs_name = None
+        self._name = None
+        self._path = ''
+        self._is_template = False
+
+        self.vs_name = vs_name
+        self.name = name
+        self.path = path
+        self.is_template = is_template
+
+    # -----------------------------------------------------------
+    @property
+    def vs_name(self):
+        """Return the name of the vSphere of the VM or the template."""
+        return self._vs_name
+
+    @vs_name.setter
+    def vs_name(self, value):
+        if value is None:
+            raise TypeError(_('The vSphere name of a VM or template may not be None.'))
+        val = str(value).strip().lower()
+        if val == '':
+            raise ValueError(_('The vSphere name of a VM or template may not be empty.'))
+
+        self._vs_name = val
+
+    # -----------------------------------------------------------
+    @property
+    def name(self):
+        """Return the name of the VM or the template."""
+        return self._name
+
+    @name.setter
+    def name(self, value):
+        if value is None:
+            raise TypeError(_('The name of a VM or template may not be None.'))
+        val = str(value).strip().lower()
+        if val == '':
+            raise ValueError(_('The name of a VM or template may not be empty.'))
+
+        self._name = val
+
+    # -----------------------------------------------------------
+    @property
+    def path(self):
+        """Return the path in VSphere of the VM or the template."""
+        return self._path
+
+    @path.setter
+    def path(self, value):
+        if value is None:
+            self._path = ''
+            return
+        val = str(value).strip()
+        self._path = val
+
+    # -----------------------------------------------------------
+    @property
+    def is_template(self):
+        """Return, whether the object points to a template or a normal VM."""
+        return self._is_template
+
+    @is_template.setter
+    def is_template(self, value):
+        self._is_template = bool(value)
+
+    # -------------------------------------------------------------------------
+    def __repr__(self):
+        """Typecasting into a string for reproduction."""
+        out = '<%s(' % (self.__class__.__name__)
+
+        fields = []
+        fields.append('vs_name={!r}'.format(self.vs_name))
+        fields.append('name={!r}'.format(self.name))
+        fields.append('path={!r}'.format(self.path))
+        fields.append('is_template={!r}'.format(self.is_template))
+
+        out += ', '.join(fields) + ')>'
+        return out
+
+    # -------------------------------------------------------------------------
+    def as_dict(self, short=True):
+        """
+        Transform the elements of the object into a dict.
+
+        @param short: don't include local properties in resulting dict.
+        @type short: bool
+
+        @return: structure as dict
+        @rtype:  dict
+        """
+        res = super(SlimVm, self).as_dict(short=short)
+
+        res['vs_name'] = self.vs_name
+        res['name'] = self.name
+        res['path'] = self.path
+        res['is_template'] = self.is_template
+
+        return res
+
+    # -------------------------------------------------------------------------
+    def __eq__(self, other):
+        """Compare for equality."""
+        if not isinstance(other, SlimVm):
+            return False
+
+        if self.vs_name.lower() != other.vs_name.lower():
+            return False
+
+        if self.name.lower() != other.name.lower():
+            return False
+
+        if self.path != other.PATH:
+            return False
+
+        return True
+
+# =============================================================================
+
+if __name__ == '__main__':
+
+    pass
+
+# =============================================================================
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4