From 7fe95003b5cd8cacef38a2eaab696392294b80ba Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Tue, 23 Oct 2018 15:46:52 +0200 Subject: [PATCH] Removing some unused modules, Changing handler class to another parent class --- lib/cr_vmware_tpl/app.py | 123 ++++++++----- lib/cr_vmware_tpl/colored.py | 215 ----------------------- lib/cr_vmware_tpl/config.py | 2 + lib/cr_vmware_tpl/handler.py | 88 +--------- lib/cr_vmware_tpl/obj.py | 328 ----------------------------------- python_fb_tools | 2 +- 6 files changed, 88 insertions(+), 670 deletions(-) delete mode 100644 lib/cr_vmware_tpl/colored.py delete mode 100644 lib/cr_vmware_tpl/obj.py diff --git a/lib/cr_vmware_tpl/app.py b/lib/cr_vmware_tpl/app.py index 542c9c2..51c14ae 100644 --- a/lib/cr_vmware_tpl/app.py +++ b/lib/cr_vmware_tpl/app.py @@ -17,6 +17,7 @@ import traceback import textwrap import argparse import getpass +import argparse # Third party modules @@ -29,13 +30,8 @@ from fb_tools.errors import FbAppError, ExpectedHandlerError, CommandNotFoundErr from fb_tools.common import caller_search_path -#from .colored import ColoredFormatter, colorstr - -#from .obj import PpBaseObject - from .config import CrTplConfiguration -#from .handler import ExpectedHandlerError, CrTplHandler from .handler import CrTplHandler __version__ = '0.6.4' @@ -47,6 +43,32 @@ class CrTplAppError(FbAppError): """ Base exception class for all exceptions in this application.""" pass +# ============================================================================= +class NrTemplatesOptionAction(argparse.Action): + + # ------------------------------------------------------------------------- + def __init__(self, option_strings, max_val, *args, **kwargs): + + self._max = max_val + + super(NrTemplatesOptionAction, self).__init__( + option_strings=option_strings, *args, **kwargs) + + # ------------------------------------------------------------------------- + def __call__(self, parser, namespace, values, option_string=None): + + if values < 1: + msg = "Value must be at least 1, {} was given.".format(values) + raise argparse.ArgumentError(self, msg) + + if values >= self._max: + msg = "Value must be at most {m} - {v} was given.".format( + m=self._max - 1, v=values) + raise argparse.ArgumentError(self, msg) + + setattr(namespace, self.dest, values) + + # ============================================================================= class CrTplApplication(BaseApplication): """ @@ -78,11 +100,6 @@ class CrTplApplication(BaseApplication): initialized=False, ) - #self.initialized = False - - #self.handler = CrTplHandler( - # appname=self.appname, verbose=self.verbose, base_dir=self.base_dir) - self.initialized = True # ------------------------------------------------------------------------- @@ -185,13 +202,16 @@ class CrTplApplication(BaseApplication): # h=self.config.vsphere_host, u=self.config.vsphere_user) # self.config.password = getpass.getpass(prompt=prompt) -# self.handler.config = self.config -# if self.args.rotate: -# self.handler.rotate_only = True -# if self.args.abort: -# self.handler.abort = True + self.handler = CrTplHandler( + appname=self.appname, verbose=self.verbose, base_dir=self.base_dir) + + self.handler.config = self.config + if self.args.rotate: + self.handler.rotate_only = True + if self.args.abort: + self.handler.abort = True -# self.handler.initialized = True + self.handler.initialized = True self.initialized = True # ------------------------------------------------------------------------- @@ -238,6 +258,12 @@ class CrTplApplication(BaseApplication): CrTplConfiguration.default_folder) ) + vmware_group.add_argument( + '-C', '--cluster', dest='cluster', + help="Host cluster in vSphere, where to create the template (Default: {!r}).".format( + CrTplConfiguration.default_vsphere_cluster) + ) + vmware_group.add_argument( '-M', '--vm', dest='vm', help=( @@ -253,12 +279,13 @@ class CrTplApplication(BaseApplication): ) vmware_group.add_argument( - '-N', '--number', '--number-templates', dest='number', - type=int, metavar='INT', + '-N', '--number', '--number-templates', dest='number', type=int, metavar='INT', + action=NrTemplatesOptionAction, max_val=CrTplConfiguration.limit_max_nr_templates_stay, help=( "Maximum number of templates to stay in templates folder (" - "1 <= x < 100, Default: {}).".format( - CrTplConfiguration.default_max_nr_templates_stay)) + "1 <= x < {max_nr}, Default: {def_nr}).".format( + max_nr=CrTplConfiguration.limit_max_nr_templates_stay, + def_nr=CrTplConfiguration.default_max_nr_templates_stay)) ) vmware_group.add_argument( @@ -275,33 +302,35 @@ class CrTplApplication(BaseApplication): pass -# if self.args.host: -# self.config.vsphere_host = self.args.host -# if self.args.port: -# self.config.vsphere_port = self.args.port -# if self.args.user: -# self.config.vsphere_user = self.args.user -# if self.args.password: -# self.config.password = self.args.password -# if self.args.folder: -# self.config.folder = self.args.folder -# if self.args.vm: -# self.config.template_vm = self.args.vm -# if self.args.template: -# self.config.template_name = self.args.template -# -# if self.args.number is not None: -# v = self.args.number -# if v < 1: -# LOG.error(( -# "Wrong number {} of templates to stay in templates folder, " -# "must be greater than zero.").format(v)) -# elif v >= 100: -# LOG.error(( -# "Wrong number {} of templates to stay in templates folder, " -# "must be less than 100.").format(v)) -# else: -# self.config.max_nr_templates_stay = v + if self.args.host: + self.config.vsphere_host = self.args.host + if self.args.port: + self.config.vsphere_port = self.args.port + if self.args.user: + self.config.vsphere_user = self.args.user + if self.args.password: + self.config.password = self.args.password + if self.args.cluster: + self.config.vsphere_cluster = self.args.cluster + if self.args.folder: + self.config.folder = self.args.folder + if self.args.vm: + self.config.template_vm = self.args.vm + if self.args.template: + self.config.template_name = self.args.template + + if self.args.number is not None: + v = self.args.number + if v < 1: + LOG.error(( + "Wrong number {} of templates to stay in templates folder, " + "must be greater than zero.").format(v)) + elif v >= 100: + LOG.error(( + "Wrong number {} of templates to stay in templates folder, " + "must be less than 100.").format(v)) + else: + self.config.max_nr_templates_stay = v # ------------------------------------------------------------------------- def _run(self): diff --git a/lib/cr_vmware_tpl/colored.py b/lib/cr_vmware_tpl/colored.py deleted file mode 100644 index 97bbfe6..0000000 --- a/lib/cr_vmware_tpl/colored.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@summary: additional logging formatter for colored output via console -""" - -# Standard modules -import logging -import copy - -# Third party modules - -# Own modules - -__version__ = '0.1.1' - -# ============================================================================= -# Color coding module variables and helper functions - -COLOR_CODE = { - 'ENDC': 0, # RESET COLOR - 'BOLD': 1, - 'UNDERLINE': 4, - 'BLINK': 5, - 'INVERT': 7, - 'CONCEALD': 8, - 'STRIKE': 9, - 'GREY30': 90, - 'GREY40': 2, - 'GREY65': 37, - 'GREY70': 97, - 'GREY20_BG': 40, - 'GREY33_BG': 100, - 'GREY80_BG': 47, - 'GREY93_BG': 107, - 'DARK_RED': 31, - 'RED': 91, - 'RED_BG': 41, - 'LIGHT_RED_BG': 101, - 'DARK_YELLOW': 33, - 'YELLOW': 93, - 'YELLOW_BG': 43, - 'LIGHT_YELLOW_BG': 103, - 'DARK_BLUE': 34, - 'BLUE': 94, - 'BLUE_BG': 44, - 'LIGHT_BLUE_BG': 104, - 'DARK_MAGENTA': 35, - 'PURPLE': 95, - 'MAGENTA_BG': 45, - 'LIGHT_PURPLE_BG': 105, - 'DARK_CYAN': 36, - 'AUQA': 96, - 'AQUA': 96, - 'CYAN_BG': 46, - 'LIGHT_AUQA_BG': 106, - 'LIGHT_AQUA_BG': 106, - 'DARK_GREEN': 32, - 'GREEN': 92, - 'GREEN_BG': 42, - 'LIGHT_GREEN_BG': 102, - 'BLACK': 30, -} - - -# ----------------------------------------------------------------------------- -def termcode(num): - """ - Output of an ANSII terminal code. - """ - - return('\033[%sm' % (num)) - - -# ----------------------------------------------------------------------------- -def colorstr(message, color): - """ - Wrapper function to colorize the message. - - @param message: The message to colorize - @type message: str - @param color: The color to use, must be one of the keys of COLOR_CODE - @type color: str - - @return: the colorized message - @rtype: str - - """ - - tcode = '' - if isinstance(color, (list, tuple)): - for clr in color: - tcode += termcode(COLOR_CODE[clr]) - else: - tcode = termcode(COLOR_CODE[color]) - - return tcode + message + termcode(COLOR_CODE['ENDC']) - - -# ---------------------------------------- -LOG = logging.getLogger(__name__) - - -# ============================================================================= -class ColoredFormatter(logging.Formatter): - """ - A variant of code found at: - http://stackoverflow.com/questions/384076/how-can-i-make-the-python-logging-output-to-be-colored - """ - - LEVEL_COLOR = { - 'DEBUG': None, - 'INFO': 'GREEN', - 'WARNING': 'YELLOW', - 'ERROR': ('BOLD', 'RED'), - 'CRITICAL': 'RED_BG', - } - - # ------------------------------------------------------------------------- - def __init__(self, fmt=None, datefmt=None): - """ - Initialize the formatter with specified format strings. - - Initialize the formatter either with the specified format string, or a - default. Allow for specialized date formatting with the optional - datefmt argument (if omitted, you get the ISO8601 format). - """ - - logging.Formatter.__init__(self, fmt, datefmt) - - # ----------------------------------------------------------- - @property - def color_debug(self): - """The color used to output debug messages.""" - return self.LEVEL_COLOR['DEBUG'] - - @color_debug.setter - def color_debug(self, value): - self.LEVEL_COLOR['DEBUG'] = value - - # ----------------------------------------------------------- - @property - def color_info(self): - """The color used to output info messages.""" - return self.LEVEL_COLOR['INFO'] - - @color_info.setter - def color_info(self, value): - self.LEVEL_COLOR['INFO'] = value - - # ----------------------------------------------------------- - @property - def color_warning(self): - """The color used to output warning messages.""" - return self.LEVEL_COLOR['WARNING'] - - @color_warning.setter - def color_warning(self, value): - self.LEVEL_COLOR['WARNING'] = value - - # ----------------------------------------------------------- - @property - def color_error(self): - """The color used to output error messages.""" - return self.LEVEL_COLOR['ERROR'] - - @color_error.setter - def color_error(self, value): - self.LEVEL_COLOR['ERROR'] = value - - # ----------------------------------------------------------- - @property - def color_critical(self): - """The color used to output critical messages.""" - return self.LEVEL_COLOR['CRITICAL'] - - @color_critical.setter - def color_critical(self, value): - self.LEVEL_COLOR['CRITICAL'] = value - - # ------------------------------------------------------------------------- - def format(self, record): - """ - Format the specified record as text. - """ - - record = copy.copy(record) - levelname = record.levelname - - if levelname in self.LEVEL_COLOR: - - record.name = colorstr(record.name, 'BOLD') - record.filename = colorstr(record.filename, 'BOLD') - record.module = colorstr(record.module, 'BOLD') - record.funcName = colorstr(record.funcName, 'BOLD') - record.pathname = colorstr(record.pathname, 'BOLD') - record.processName = colorstr(record.processName, 'BOLD') - record.threadName = colorstr(record.threadName, 'BOLD') - - if self.LEVEL_COLOR[levelname] is not None: - record.levelname = colorstr( - levelname, self.LEVEL_COLOR[levelname]) - record.msg = colorstr(record.msg, self.LEVEL_COLOR[levelname]) - - return logging.Formatter.format(self, record) - - -# ============================================================================= - -if __name__ == "__main__": - pass - -# ============================================================================= - -# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/lib/cr_vmware_tpl/config.py b/lib/cr_vmware_tpl/config.py index 048e29e..f2e2785 100644 --- a/lib/cr_vmware_tpl/config.py +++ b/lib/cr_vmware_tpl/config.py @@ -55,6 +55,7 @@ class CrTplConfiguration(BaseConfiguration): default_max_nr_templates_stay = 4 min_max_wait_for_finish_install = 3 * 60 max_max_wait_for_finish_install = 24 * 60 * 60 + limit_max_nr_templates_stay = 100 mac_address_template = "00:16:3e:53:{:02x}:{:02x}" @@ -149,6 +150,7 @@ class CrTplConfiguration(BaseConfiguration): res['default_mac_address'] = self.default_mac_address res['default_max_wait_for_finish_install'] = self.default_max_wait_for_finish_install res['default_max_nr_templates_stay'] = self.default_max_nr_templates_stay + res['limit_max_nr_templates_stay'] = self.limit_max_nr_templates_stay res['min_max_wait_for_finish_install'] = self.min_max_wait_for_finish_install res['max_max_wait_for_finish_install'] = self.max_max_wait_for_finish_install res['mac_address_template'] = self.mac_address_template diff --git a/lib/cr_vmware_tpl/handler.py b/lib/cr_vmware_tpl/handler.py index 23b7d04..ad7fa4a 100644 --- a/lib/cr_vmware_tpl/handler.py +++ b/lib/cr_vmware_tpl/handler.py @@ -28,34 +28,22 @@ from pyVmomi import vim, vmodl from pyVim.connect import SmartConnect, Disconnect # Own modules -from .common import pp, to_str -from .errors import PpError +from fb_tools.common import pp, to_str -from .obj import PpBaseObject +from fb_tools.errors import HandlerError, ExpectedHandlerError, CommandNotFoundError +from fb_tools.errors import TerraformVmError, TerraformVmDefinitionError, NetworkNotExistingError +from fb_tools.errors import CannotConnectVsphereError, NoDatastoreFoundError + +from fb_tools.handler import BaseHandler from .config import CrTplConfiguration -__version__ = '0.9.4' +__version__ = '0.10.1' LOG = logging.getLogger(__name__) TZ = pytz.timezone('Europe/Berlin') -# ============================================================================= -class HandlerError(PpError, RuntimeError): - """Base error class for all exceptions happened during - execution this handler""" - - pass - - -# ============================================================================= -class ExpectedHandlerError(HandlerError): - """Base class for all errors, which could be expected in application object - and displayed without stack trace.""" - - pass - # ============================================================================= class TempVmExistsError(ExpectedHandlerError): """Special error class for the case, if the temporary VM is already existing.""" @@ -73,65 +61,7 @@ class TempVmExistsError(ExpectedHandlerError): # ============================================================================= -class NoDatastoreFoundError(ExpectedHandlerError): - """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 NetworkNotExistingError(ExpectedHandlerError): - """Special error class for the case, if the expected network is not existing.""" - - # ------------------------------------------------------------------------- - def __init__(self, net_name): - - self.net_name = net_name - - # ------------------------------------------------------------------------- - def __str__(self): - - msg = "The network {!r} is not existing.".format(self.net_name) - return msg - - -# ============================================================================= -class CannotConnectError(ExpectedHandlerError): - """Special error class for the case, it cannot connect - to the given vSphere server.""" - - # ------------------------------------------------------------------------- - def __init__(self, host, port, user): - - self.host = host - self.port = port - self.user = user - - # ------------------------------------------------------------------------- - def __str__(self): - - msg = "Could not connect to the vSphere host {h}:{p} as user {u!r}.".format( - h=self.host, p=self.port, u=self.user) - return msg - - -# ============================================================================= -class CrTplHandler(PpBaseObject): +class CrTplHandler(BaseHandler): """ A handler class for creating a vSphere template. """ @@ -203,7 +133,7 @@ class CrTplHandler(PpBaseObject): sslContext=ssl_context) if not self.service_instance: - raise CannotConnectError( + raise CannotConnectVsphereError( host=self.config.vsphere_host, port=self.config.vsphere_port, user=self.config.vsphere_user) diff --git a/lib/cr_vmware_tpl/obj.py b/lib/cr_vmware_tpl/obj.py deleted file mode 100644 index 8208c98..0000000 --- a/lib/cr_vmware_tpl/obj.py +++ /dev/null @@ -1,328 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@author: Frank Brehm -@contact: frank.brehm@pixelpark.com -@copyright: © 2018 by Frank Brehm, Publicies Pixelpark GmbH, Berlin -""" -from __future__ import absolute_import - -# Standard modules -import sys -import os -import logging -import datetime -import traceback - -# Third party modules - -# Own modules -from .common import pp, to_bytes - -from .errors import PpError - -__version__ = '0.2.4' - -LOG = logging.getLogger(__name__) - - -# ============================================================================= -class PpBaseObjectError(PpError): - """ - Base error class useable by all descendand objects. - """ - - pass - - -# ============================================================================= -class PpBaseObject(object): - """ - Base class for all objects. - """ - - # ------------------------------------------------------------------------- - def __init__( - self, appname=None, verbose=0, version=__version__, base_dir=None, - initialized=False): - """ - Initialisation of the base object. - - Raises an exception on a uncoverable error. - """ - - self._appname = None - """ - @ivar: name of the current running application - @type: str - """ - if appname: - v = str(appname).strip() - if v: - self._appname = v - if not self._appname: - self._appname = os.path.basename(sys.argv[0]) - - self._version = version - """ - @ivar: version string of the current object or application - @type: str - """ - - self._verbose = int(verbose) - """ - @ivar: verbosity level (0 - 9) - @type: int - """ - if self._verbose < 0: - msg = "Wrong verbose level {!r}, must be >= 0".format(verbose) - raise ValueError(msg) - - self._initialized = False - """ - @ivar: initialisation of this object is complete - after __init__() of this object - @type: bool - """ - - self._base_dir = base_dir - """ - @ivar: base directory used for different purposes, must be an existent - directory. Defaults to directory of current script daemon.py. - @type: str - """ - if base_dir: - if not os.path.exists(base_dir): - msg = "Base directory {!r} does not exists.".format(base_dir) - self.handle_error(msg) - self._base_dir = None - elif not os.path.isdir(base_dir): - msg = "Base directory {!r} is not a directory.".format(base_dir) - self.handle_error(msg) - self._base_dir = None - if not self._base_dir: - self._base_dir = os.path.dirname(os.path.abspath(sys.argv[0])) - - self._initialized = bool(initialized) - - # ----------------------------------------------------------- - @property - def appname(self): - """The name of the current running application.""" - if hasattr(self, '_appname'): - return self._appname - return os.path.basename(sys.argv[0]) - - @appname.setter - def appname(self, value): - if value: - v = str(value).strip() - if v: - self._appname = v - - # ----------------------------------------------------------- - @property - def version(self): - """The version string of the current object or application.""" - return getattr(self, '_version', __version__) - - # ----------------------------------------------------------- - @property - def verbose(self): - """The verbosity level.""" - return getattr(self, '_verbose', 0) - - @verbose.setter - def verbose(self, value): - v = int(value) - if v >= 0: - self._verbose = v - else: - LOG.warn("Wrong verbose level {!r}, must be >= 0".format(value)) - - # ----------------------------------------------------------- - @property - def initialized(self): - """The initialisation of this object is complete.""" - return getattr(self, '_initialized', False) - - @initialized.setter - def initialized(self, value): - self._initialized = bool(value) - - # ----------------------------------------------------------- - @property - def base_dir(self): - """The base directory used for different purposes.""" - return self._base_dir - - @base_dir.setter - def base_dir(self, value): - if value.startswith('~'): - value = os.path.expanduser(value) - if not os.path.exists(value): - msg = "Base directory {!r} does not exists.".format(value) - LOG.error(msg) - elif not os.path.isdir(value): - msg = "Base directory {!r} is not a directory.".format(value) - LOG.error(msg) - else: - self._base_dir = value - - # ------------------------------------------------------------------------- - def __str__(self): - """ - Typecasting function for translating object structure - into a string - - @return: structure as string - @rtype: str - """ - - return pp(self.as_dict(short=True)) - - # ------------------------------------------------------------------------- - def __repr__(self): - """Typecasting into a string for reproduction.""" - - out = "<%s(" % (self.__class__.__name__) - - fields = [] - fields.append("appname={!r}".format(self.appname)) - fields.append("verbose={!r}".format(self.verbose)) - fields.append("version={!r}".format(self.version)) - fields.append("base_dir={!r}".format(self.base_dir)) - fields.append("initialized={!r}".format(self.initialized)) - - out += ", ".join(fields) + ")>" - return out - - # ------------------------------------------------------------------------- - def as_dict(self, short=True): - """ - Transforms 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 = self.__dict__ - res = {} - for key in self.__dict__: - if short and key.startswith('_') and not key.startswith('__'): - continue - val = self.__dict__[key] - if isinstance(val, PpBaseObject): - res[key] = val.as_dict(short=short) - else: - res[key] = val - res['__class_name__'] = self.__class__.__name__ - res['appname'] = self.appname - res['version'] = self.version - res['verbose'] = self.verbose - res['initialized'] = self.initialized - res['base_dir'] = self.base_dir - - return res - - # ------------------------------------------------------------------------- - def handle_error( - self, error_message=None, exception_name=None, do_traceback=False): - """ - Handle an error gracefully. - - Print a traceback and continue. - - @param error_message: the error message to display - @type error_message: str - @param exception_name: name of the exception class - @type exception_name: str - @param do_traceback: allways show a traceback - @type do_traceback: bool - - """ - - msg = 'Exception happened: ' - if exception_name is not None: - exception_name = exception_name.strip() - if exception_name: - msg = exception_name + ': ' - else: - msg = '' - if error_message: - msg += str(error_message) - else: - msg += 'undefined error.' - - root_log = logging.getLogger() - has_handlers = False - if root_log.handlers: - has_handlers = True - - if has_handlers: - LOG.error(msg) - if do_traceback: - LOG.error(traceback.format_exc()) - else: - curdate = datetime.datetime.now() - curdate_str = "[" + curdate.isoformat(' ') + "]: " - msg = curdate_str + msg + "\n" - if hasattr(sys.stderr, 'buffer'): - sys.stderr.buffer.write(to_bytes(msg)) - else: - sys.stderr.write(msg) - if do_traceback: - traceback.print_exc() - - return - - # ------------------------------------------------------------------------- - def handle_info(self, message, info_name=None): - """ - Shows an information. This happens both to STDERR and to all - initialized log handlers. - - @param message: the info message to display - @type message: str - @param info_name: Title of information - @type info_name: str - - """ - - msg = '' - if info_name is not None: - info_name = info_name.strip() - if info_name: - msg = info_name + ': ' - msg += str(message).strip() - - root_log = logging.getLogger() - has_handlers = False - if root_log.handlers: - has_handlers = True - - if has_handlers: - LOG.info(msg) - else: - curdate = datetime.datetime.now() - curdate_str = "[" + curdate.isoformat(' ') + "]: " - msg = curdate_str + msg + "\n" - if hasattr(sys.stderr, 'buffer'): - sys.stderr.buffer.write(to_bytes(msg)) - else: - sys.stderr.write(msg) - - return - -# ============================================================================= - -if __name__ == "__main__": - - pass - -# ============================================================================= - -# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/python_fb_tools b/python_fb_tools index 6411197..98a58ec 160000 --- a/python_fb_tools +++ b/python_fb_tools @@ -1 +1 @@ -Subproject commit 64111976cd55a439b5b63de14945acc75041b90c +Subproject commit 98a58ece2e8cb15223d584130e71ef59986cdf26 -- 2.39.5