From 7e950fa59dede6706a952a77f7ad07f286442514 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Thu, 22 Mar 2018 17:25:33 +0100 Subject: [PATCH] Selecting a datastore to create the temporary VM. --- lib/cr_vmware_tpl/app.py | 7 +-- lib/cr_vmware_tpl/handler.py | 83 +++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/lib/cr_vmware_tpl/app.py b/lib/cr_vmware_tpl/app.py index 6ff4b8b..0e6ed1c 100644 --- a/lib/cr_vmware_tpl/app.py +++ b/lib/cr_vmware_tpl/app.py @@ -32,9 +32,10 @@ from .obj import PpBaseObject from .config import CrTplConfiguration -from .handler import TempVmExistsError, CrTplHandler +from .handler import TempVmExistsError, NoDatastoreFoundError +from .handler import CrTplHandler -__version__ = '0.3.1' +__version__ = '0.3.2' LOG = logging.getLogger(__name__) @@ -432,7 +433,7 @@ class CrTplApplication(PpBaseObject): try: self.handler() - except TempVmExistsError as e: + except (TempVmExistsError, NoDatastoreFoundError) as e: self.handle_error(str(e), "Temporary VM") self.exit(5) diff --git a/lib/cr_vmware_tpl/handler.py b/lib/cr_vmware_tpl/handler.py index 60eb9ac..aed1ee3 100644 --- a/lib/cr_vmware_tpl/handler.py +++ b/lib/cr_vmware_tpl/handler.py @@ -13,6 +13,8 @@ import sys import os import logging import ssl +import re +import random # Third party modules import six @@ -28,7 +30,7 @@ from .obj import PpBaseObject from .config import CrTplConfiguration -__version__ = '0.3.1' +__version__ = '0.3.2' LOG = logging.getLogger(__name__) @@ -56,6 +58,29 @@ class TempVmExistsError(HandlerError): return msg +# ============================================================================= +class NoDatastoreFoundError(HandlerError): + """Special error class for the case, that no SAN based data store was with + enogh free space was found.""" + + + # ------------------------------------------------------------------------- + def __init__(self, needed_bytes): + + self.needed_bytes = int(needed_bytes) + + # ------------------------------------------------------------------------- + def __str__(self): + + mb = float(self.needed_bytes) / 1024.0 / 1024.0 + gb = mb / 1024.0 + + msg = ( + "No SAN based datastore found with at least {m:0.0f} MiB == {g:0.1f} GiB " + "available space found.").format(m=mb, g=gb) + return msg + + # ============================================================================= class CannotConnectError(HandlerError): """Special error class for the case, it cannot connect @@ -83,6 +108,7 @@ class CrTplHandler(PpBaseObject): """ max_depth = 10 + re_local_ds = re.compile(r'^local[_-]', re.IGNORECASE) # ------------------------------------------------------------------------- def __init__( @@ -100,6 +126,7 @@ class CrTplHandler(PpBaseObject): self.config = config self.server_instance = None self.tpl_vm_folder = None + self.tpl_data_store = None if initialized: self.initialized = True @@ -137,6 +164,7 @@ class CrTplHandler(PpBaseObject): try: self.ensure_vm_folder() self.check_for_temp_tpl_vm() + self.select_data_store() finally: LOG.debug("Disconnecting from vSphere host {h}:{p} ...".format( h=self.config.vsphere_host, p=self.config.vsphere_port)) @@ -246,6 +274,59 @@ class CrTplHandler(PpBaseObject): return None + # ------------------------------------------------------------------------- + def select_data_store(self): + + LOG.debug(( + "Selecting a SAN based datastore with at least {:0.1f} GiB available " + "space.").format(self.config.data_size_gb)) + + content = self.server_instance.RetrieveContent() + dc = self.get_obj(content, [vim.Datacenter], self.config.dc) + + ds_list = [] + + for child in dc.datastoreFolder.childEntity: + self._get_data_stores(child, ds_list) + + if not len(ds_list): + raise NoDatastoreFoundError(self.config.data_size) + + LOG.debug("Found {} usable datastores.".format(len(ds_list))) + self.tpl_data_store = random.choice(ds_list) + LOG.info("Using datastore {!r} for volume of temporary VM to create.".format( + self.tpl_data_store.summary.name)) + + # ------------------------------------------------------------------------- + def _get_data_stores(self, child, ds_list, depth=1): + + if hasattr(child, 'childEntity'): + if depth > self.max_depth: + return + for sub_child in child.childEntity: + self._get_data_stores(sub_child, ds_list, depth + 1) + return + + if isinstance(child, vim.Datastore): + #if not child.summary.multipleHostAccess: + if self.re_local_ds.match(child.summary.name): + if self.verbose > 2: + LOG.debug("Datastore {!r} seems to be local.".format(child.summary.name)) + return + free_bytes = child.summary.freeSpace + free_gbytes = float(free_bytes) / 1024.0 / 1024.0 / 1024.0 + if free_bytes >= self.config.data_size: + if self.verbose > 2: + LOG.debug("Found datastore {n!r} with {f:0.1f} GiB free space.".format( + n=child.summary.name, f=free_gbytes)) + ds_list.append(child) + return + + LOG.debug("Datastore {n!r} has too less space ({f:0.1f} GiB available).".format( + n=child.summary.name, f=free_gbytes)) + + return + # ============================================================================= -- 2.39.5