]> Frank Brehm's Git Trees - pixelpark/create-vmware-tpl.git/commitdiff
Adding rotation of existing templates
authorFrank Brehm <frank.brehm@pixelpark.com>
Fri, 13 Apr 2018 11:33:20 +0000 (13:33 +0200)
committerFrank Brehm <frank.brehm@pixelpark.com>
Fri, 13 Apr 2018 11:33:20 +0000 (13:33 +0200)
lib/cr_vmware_tpl/handler.py

index 51b9d234e297da7cd9550457c63b9e1998a4feb3..4777a700d64db535ffb1751587b667a120aa6363 100644 (file)
@@ -38,7 +38,7 @@ from .obj import PpBaseObject
 
 from .config import CrTplConfiguration
 
-__version__ = '0.7.3'
+__version__ = '0.7.4'
 LOG = logging.getLogger(__name__)
 TZ = pytz.timezone('Europe/Berlin')
 
@@ -171,6 +171,7 @@ class CrTplHandler(PpBaseObject):
         self.ssh_user = 'root'
         self.ssh_timeout = 30
         self.max_wait_for_shutdown = 600
+        self.rotate_only = True
 
         if initialized:
             self.initialized = True
@@ -210,17 +211,20 @@ class CrTplHandler(PpBaseObject):
             self.ensure_vm_folder()
             self.check_for_temp_tpl_vm()
             self.select_data_store()
-            self.check_network()
-            self.create_vm()
-            self.tpl_vm = self.get_temp_tpl_vm()
-            if not self.tpl_vm:
-                raise HandlerError(
-                    "Could not find VM after creating.")
-            self.poweron_vm()
-            self.wait_for_finish_install()
-            self.post_install_tasks_ssh()
-            self.poweroff_vm()
-            self.rename_and_change_vm()
+            if not self.rotate_only:
+                self.check_network()
+                self.create_vm()
+                self.tpl_vm = self.get_temp_tpl_vm()
+                if not self.tpl_vm:
+                    raise HandlerError(
+                        "Could not find VM after creating.")
+                self.poweron_vm()
+                self.wait_for_finish_install()
+                self.post_install_tasks_ssh()
+                self.poweroff_vm()
+            self.rotate_templates()
+            if not self.rotate_only:
+                self.rename_and_change_vm()
         finally:
             LOG.debug("Disconnecting from vSphere host {h}:{p} ...".format(
                 h=self.config.vsphere_host, p=self.config.vsphere_port))
@@ -409,6 +413,44 @@ class CrTplHandler(PpBaseObject):
 
         return None
 
+    # -------------------------------------------------------------------------
+    def get_templates(self):
+
+        templates = []
+
+        content = self.service_instance.RetrieveContent()
+        dc = self.get_obj(content, [vim.Datacenter], self.config.dc)
+
+        for child in dc.vmFolder.childEntity:
+            tpl_list = self._get_templates(child)
+            if tpl_list and len(tpl_list):
+                templates.extend(tpl_list)
+
+        return templates
+
+    # -------------------------------------------------------------------------
+    def _get_templates(self, child, depth=1):
+
+        tpl_list = []
+
+        if hasattr(child, 'childEntity'):
+            if depth > self.max_depth:
+                return None
+            templates = []
+            for sub_child in child.childEntity:
+                tpl_list = self._get_templates(sub_child, depth + 1)
+                if tpl_list and len(tpl_list):
+                    templates.extend(tpl_list)
+            return templates
+
+        if hasattr(child, 'summary'):
+            summary = child.summary
+            vm_name = summary.config.name
+            if summary.config.template and vm_name.startswith(self.config.template_name):
+                tpl_list.append(child)
+
+        return tpl_list
+
     # -------------------------------------------------------------------------
     def check_network(self):
 
@@ -883,6 +925,73 @@ class CrTplHandler(PpBaseObject):
             "VM {h!r} was not shut down after {t:0.1f} seconds, current state is {s!r}.".format(
                 h=self.config.template_vm, t=cur_diff, s=guest_state))
 
+    # -------------------------------------------------------------------------
+    def rotate_templates(self):
+
+        LOG.info("Searching for existing templates and rotate them ...")
+        re_is_numeric = re.compile(r'^\s*(\d+)\s*$')
+
+        templates = self.get_templates()
+        templates_ts = {}
+        templates_sorted = []
+
+        if not templates:
+            LOG.info("Did not found any existing templates.")
+            return
+
+        LOG.debug("Found {} existing templates.".format(len(templates)))
+        for template in templates:
+            tpl_name = template.summary.config.name
+            val_map = {}
+            for extra_cfg in template.config.extraConfig:
+                key = extra_cfg.key
+                value = extra_cfg.value
+                val_map[key] = value
+            created = time.time()
+            if val_map['created'] and re_is_numeric.match(val_map['created']):
+                created = float(val_map['created'])
+            ts_created = datetime.datetime.fromtimestamp(created, tz=TZ)
+            LOG.debug("Found template {n!r}, created: {ts}.".format(
+                n=tpl_name, ts=ts_created.isoformat(' ')))
+            if self.verbose > 2:
+                LOG.debug("Template Summary Config:\n{}".format(template.summary.config))
+                LOG.debug("Template Extra Config:\n{}".format(pp(val_map)))
+
+            templates_ts[tpl_name] = created
+
+        for tpl_name in sorted(templates_ts.keys(), key=lambda tpl: templates_ts[tpl]):
+            templates_sorted.append(tpl_name)
+
+        LOG.debug("Templates sorted by creation date:\n{}".format(
+            pp(templates_sorted)))
+        templates_sorted.reverse()
+        templates_to_remove = []
+        i = 0
+        for tpl_name in templates_sorted:
+            if i > self.config.max_nr_templates_stay - 2:
+                templates_to_remove.append(tpl_name)
+            i += 1
+        templates_to_remove.reverse()
+        LOG.debug("Templates to remove:\n{}".format(pp(templates_to_remove)))
+
+        for template in templates:
+            tpl_name = template.summary.config.name
+            if tpl_name in templates_to_remove:
+                LOG.info("Removing template {!r} ...".format(tpl_name))
+                task = template.Destroy_Task()
+                self.wait_for_tasks([task])
+                LOG.debug("Successful removed template {!r}.".format(tpl_name))
+                continue
+            if tpl_name.strip().lower() == self.config.template_name.strip().lower():
+                created = templates_ts[tpl_name]
+                ts_created = datetime.datetime.fromtimestamp(created, tz=TZ)
+                new_name = tpl_name + '.' + ts_created.strftime('%Y-%m-%d_%H-%M-%S')
+                LOG.info("Renaming template {o!r} => {n!r} ...".format(
+                            o=tpl_name, n=new_name))
+                task = template.Rename_Task(new_name)
+                self.wait_for_tasks([task])
+                LOG.debug("Successful renamed template into {!r}.".format(new_name))
+
     # -------------------------------------------------------------------------
     def rename_and_change_vm(self):