From 01a7b03d5c28a60312befe95393d373d4be560e3 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Fri, 13 Apr 2018 13:33:20 +0200 Subject: [PATCH] Adding rotation of existing templates --- lib/cr_vmware_tpl/handler.py | 133 +++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/lib/cr_vmware_tpl/handler.py b/lib/cr_vmware_tpl/handler.py index 51b9d23..4777a70 100644 --- a/lib/cr_vmware_tpl/handler.py +++ b/lib/cr_vmware_tpl/handler.py @@ -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): -- 2.39.5