# Own modules
from . import __version__ as GLOBAL_VERSION
-from .errors import PpAppError
+import fb_tools
+from fb_tools.app import BaseApplication
+from fb_tools.errors import FbAppError, ExpectedHandlerError, CommandNotFoundError
-from .common import terminal_can_colors
-from .common import caller_search_path
+from fb_tools.common import caller_search_path
-from .colored import ColoredFormatter, colorstr
+#from .colored import ColoredFormatter, colorstr
-from .obj import PpBaseObject
+#from .obj import PpBaseObject
from .config import CrTplConfiguration
-from .handler import ExpectedHandlerError, CrTplHandler
+#from .handler import ExpectedHandlerError, CrTplHandler
+from .handler import CrTplHandler
__version__ = '0.6.4'
LOG = logging.getLogger(__name__)
# =============================================================================
-class CrTplApplication(PpBaseObject):
+class CrTplAppError(FbAppError):
+ """ Base exception class for all exceptions in this application."""
+ pass
+
+# =============================================================================
+class CrTplApplication(BaseApplication):
"""
Class for the application objects.
"""
initialized=False, usage=None, description=None,
argparse_epilog=None, argparse_prefix_chars='-', env_prefix=None):
- self.arg_parser = None
- """
- @ivar: argparser object to parse commandline parameters
- @type: argparse.ArgumentParser
- """
-
- self.args = None
- """
- @ivar: an object containing all commandline parameters
- after parsing them
- @type: Namespace
- """
-
- self._exit_value = 0
- """
- @ivar: return value of the application for exiting with sys.exit().
- @type: int
- """
-
- self._usage = usage
- """
- @ivar: usage text used on argparse
- @type: str
- """
-
- self._description = description
- """
- @ivar: a short text describing the application
- @type: str
- """
-
- self._argparse_epilog = argparse_epilog
- """
- @ivar: an epilog displayed at the end of the argparse help screen
- @type: str
- """
-
- self._argparse_prefix_chars = argparse_prefix_chars
- """
- @ivar: The set of characters that prefix optional arguments.
- @type: str
- """
-
- self._terminal_has_colors = False
- """
- @ivar: flag, that the current terminal understands color ANSI codes
- @type: bool
- """
-
- self._quiet = False
-
- self.env = {}
- """
- @ivar: a dictionary with all application specifiv environment variables,
- they will detected by the env_prefix property of this object,
- and their names will transformed before saving their values in
- self.env by removing the env_prefix from the variable name.
- @type: dict
- """
-
- self._env_prefix = None
- """
- @ivar: a prefix for environment variables to detect them and to assign
- their transformed names and their values in self.env
- @type: str
- """
+ desc = textwrap.dedent("""\
+ Creates in the given vSphere environment and cluster a template object,
+ which can be used to spawn different virtual machines.
+ """).strip()
super(CrTplApplication, self).__init__(
appname=appname,
verbose=verbose,
version=version,
base_dir=base_dir,
+ description=desc,
initialized=False,
)
- if env_prefix:
- ep = str(env_prefix).strip()
- if not ep:
- msg = "Invalid env_prefix {!r} given - it may not be empty.".format(env_prefix)
- raise PpAppError(msg)
- match = self.re_prefix.search(ep)
- if not match:
- msg = (
- "Invalid characters found in env_prefix {!r}, only "
- "alphanumeric characters and digits and underscore "
- "(this not as the first character) are allowed.").format(env_prefix)
- raise PpAppError(msg)
- self._env_prefix = ep
- else:
- ep = self.appname.upper() + '_'
- self._env_prefix = self.re_anum.sub('_', ep)
-
- self._description = textwrap.dedent('''\
- Creates in the given vSphere environment a template object,
- which can be used to spawn different virtual machines.
- ''').strip()
-
- self._init_arg_parser()
- self._perform_arg_parser()
-
- self._init_env()
- self._perform_env()
-
- self.config = CrTplConfiguration(
- appname=self.appname, verbose=self.verbose, base_dir=self.base_dir)
-
- self.handler = CrTplHandler(
- appname=self.appname, verbose=self.verbose, base_dir=self.base_dir)
-
- self.post_init()
-
- # -----------------------------------------------------------
- @property
- def exit_value(self):
- """The return value of the application for exiting with sys.exit()."""
- return self._exit_value
-
- @exit_value.setter
- def exit_value(self, value):
- v = int(value)
- if v >= 0:
- self._exit_value = v
- else:
- LOG.warn("Wrong exit_value {!r}, must be >= 0".format(value))
-
- # -----------------------------------------------------------
- @property
- def exitvalue(self):
- """The return value of the application for exiting with sys.exit()."""
- return self._exit_value
-
- @exitvalue.setter
- def exitvalue(self, value):
- self.exit_value = value
-
- # -----------------------------------------------------------
- @property
- def usage(self):
- """The usage text used on argparse."""
- return self._usage
-
- # -----------------------------------------------------------
- @property
- def description(self):
- """A short text describing the application."""
- return self._description
-
- # -----------------------------------------------------------
- @property
- def argparse_epilog(self):
- """An epilog displayed at the end of the argparse help screen."""
- return self._argparse_epilog
-
- # -----------------------------------------------------------
- @property
- def argparse_prefix_chars(self):
- """The set of characters that prefix optional arguments."""
- return self._argparse_prefix_chars
-
- # -----------------------------------------------------------
- @property
- def terminal_has_colors(self):
- """A flag, that the current terminal understands color ANSI codes."""
- return self._terminal_has_colors
-
- # -----------------------------------------------------------
- @property
- def env_prefix(self):
- """A prefix for environment variables to detect them."""
- return self._env_prefix
-
- # -----------------------------------------------------------
- @property
- def usage_term(self):
- """The localized version of 'usage: '"""
- return 'Usage: '
-
- # -----------------------------------------------------------
- @property
- def usage_term_len(self):
- """The length of the localized version of 'usage: '"""
- return len(self.usage_term)
-
- # -----------------------------------------------------------
- @property
- def quiet(self):
- """Quiet execution of the application,
- only warnings and errors are emitted."""
- return self._quiet
-
- @quiet.setter
- def quiet(self, value):
- self._quiet = bool(value)
-
- # -------------------------------------------------------------------------
- def exit(self, retval=-1, msg=None, trace=False):
- """
- Universal method to call sys.exit(). If fake_exit is set, a
- FakeExitError exception is raised instead (useful for unittests.)
-
- @param retval: the return value to give back to theoperating system
- @type retval: int
- @param msg: a last message, which should be emitted before exit.
- @type msg: str
- @param trace: flag to output a stack trace before exiting
- @type trace: bool
+ #self.initialized = False
- @return: None
+ #self.config = CrTplConfiguration(
+ # appname=self.appname, verbose=self.verbose, base_dir=self.base_dir)
- """
+ #self.handler = CrTplHandler(
+ # appname=self.appname, verbose=self.verbose, base_dir=self.base_dir)
- retval = int(retval)
- trace = bool(trace)
-
- root_logger = logging.getLogger()
- has_handlers = False
- if root_logger.handlers:
- has_handlers = True
-
- if msg:
- if has_handlers:
- if retval:
- LOG.error(msg)
- else:
- LOG.info(msg)
- if not has_handlers:
- if hasattr(sys.stderr, 'buffer'):
- sys.stderr.buffer.write(str(msg) + "\n")
- else:
- sys.stderr.write(str(msg) + "\n")
-
- if trace:
- if has_handlers:
- if retval:
- LOG.error(traceback.format_exc())
- else:
- LOG.info(traceback.format_exc())
- else:
- traceback.print_exc()
-
- sys.exit(retval)
+ self.initialized = True
# -------------------------------------------------------------------------
def as_dict(self, short=True):
"""
res = super(CrTplApplication, self).as_dict(short=short)
- res['exit_value'] = self.exit_value
- res['usage'] = self.usage
- res['quiet'] = self.quiet
- res['description'] = self.description
- res['argparse_epilog'] = self.argparse_epilog
- res['argparse_prefix_chars'] = self.argparse_prefix_chars
- res['terminal_has_colors'] = self.terminal_has_colors
- res['env_prefix'] = self.env_prefix
return res
- # -------------------------------------------------------------------------
- def init_logging(self):
- """
- Initialize the logger object.
- It creates a colored loghandler with all output to STDERR.
- Maybe overridden in descendant classes.
-
- @return: None
- """
-
- log_level = logging.INFO
- if self.verbose:
- log_level = logging.DEBUG
- elif self.quiet:
- log_level = logging.WARNING
-
- root_logger = logging.getLogger()
- root_logger.setLevel(log_level)
-
- # create formatter
- format_str = ''
- if self.verbose:
- format_str = '[%(asctime)s]: '
- format_str += self.appname + ': '
- if self.verbose:
- if self.verbose > 1:
- format_str += '%(name)s(%(lineno)d) %(funcName)s() '
- else:
- format_str += '%(name)s '
- format_str += '%(levelname)s - %(message)s'
- formatter = None
- if self.terminal_has_colors:
- formatter = ColoredFormatter(format_str)
- else:
- formatter = logging.Formatter(format_str)
-
- # create log handler for console output
- lh_console = logging.StreamHandler(sys.stderr)
- lh_console.setLevel(log_level)
- lh_console.setFormatter(formatter)
-
- root_logger.addHandler(lh_console)
-
- if self.verbose < 3:
- paramiko_logger = logging.getLogger('paramiko.transport')
- if self.verbose < 1:
- paramiko_logger.setLevel(logging.WARNING)
- else:
- paramiko_logger.setLevel(logging.INFO)
-
- return
-
- # -------------------------------------------------------------------------
- def terminal_can_color(self):
- """
- Method to detect, whether the current terminal (stdout and stderr)
- is able to perform ANSI color sequences.
-
- @return: both stdout and stderr can perform ANSI color sequences
- @rtype: bool
-
- """
-
- term_debug = False
- if self.verbose > 3:
- term_debug = True
- return terminal_can_colors(debug=term_debug)
+# # -------------------------------------------------------------------------
+# def init_logging(self):
+# """
+# Initialize the logger object.
+# It creates a colored loghandler with all output to STDERR.
+# Maybe overridden in descendant classes.
+#
+# @return: None
+# """
+#
+# log_level = logging.INFO
+# if self.verbose:
+# log_level = logging.DEBUG
+# elif self.quiet:
+# log_level = logging.WARNING
+#
+# root_logger = logging.getLogger()
+# root_logger.setLevel(log_level)
+#
+# # create formatter
+# format_str = ''
+# if self.verbose:
+# format_str = '[%(asctime)s]: '
+# format_str += self.appname + ': '
+# if self.verbose:
+# if self.verbose > 1:
+# format_str += '%(name)s(%(lineno)d) %(funcName)s() '
+# else:
+# format_str += '%(name)s '
+# format_str += '%(levelname)s - %(message)s'
+# formatter = None
+# if self.terminal_has_colors:
+# formatter = ColoredFormatter(format_str)
+# else:
+# formatter = logging.Formatter(format_str)
+#
+# # create log handler for console output
+# lh_console = logging.StreamHandler(sys.stderr)
+# lh_console.setLevel(log_level)
+# lh_console.setFormatter(formatter)
+#
+# root_logger.addHandler(lh_console)
+#
+# if self.verbose < 3:
+# paramiko_logger = logging.getLogger('paramiko.transport')
+# if self.verbose < 1:
+# paramiko_logger.setLevel(logging.WARNING)
+# else:
+# paramiko_logger.setLevel(logging.INFO)
+#
+# return
# -------------------------------------------------------------------------
def post_init(self):
"""
self.init_logging()
- self.config.read()
- if self.config.verbose > self.verbose:
- self.verbose = self.config.verbose
+# self.config.read()
+# if self.config.verbose > self.verbose:
+# self.verbose = self.config.verbose
self.perform_arg_parser()
- if not self.config.password:
- prompt = 'Enter password for host {h!r} and user {u!r}: '.format(
- 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.initialized = True
- self.initialized = True
-
- # -------------------------------------------------------------------------
- def pre_run(self):
- """
- Dummy function to run before the main routine.
- Could be overwritten by descendant classes.
-
- """
-
- pass
-
- # -------------------------------------------------------------------------
- def _run(self):
- """
- Dummy function as main routine.
-
- MUST be overwritten by descendant classes.
-
- """
-
- LOG.info("Starting {a!r}, version {v!r} ...".format(
- a=self.appname, v=self.version))
-
- try:
- ret = self.handler()
- self.exit(ret)
- except ExpectedHandlerError as e:
- self.handle_error(str(e), "Temporary VM")
- self.exit(5)
-
- # -------------------------------------------------------------------------
- def __call__(self):
- """
- Helper method to make the resulting object callable, e.g.::
-
- app = PBApplication(...)
- app()
-
- @return: None
-
- """
-
- self.run()
-
- # -------------------------------------------------------------------------
- def run(self):
- """
- The visible start point of this object.
-
- @return: None
-
- """
-
- if not self.initialized:
- self.handle_error(
- "The application is not completely initialized.", '', True)
- self.exit(9)
-
- try:
- self.pre_run()
- except Exception as e:
- self.handle_error(str(e), e.__class__.__name__, True)
- self.exit(98)
-
- if not self.initialized:
- raise PpAppError(
- "Object {!r} seems not to be completely initialized.".format(
- self.__class__.__name__))
-
- try:
- self._run()
- except Exception as e:
- self.handle_error(str(e), e.__class__.__name__, True)
- self.exit_value = 99
-
- if self.verbose > 1:
- LOG.info("Ending.")
+# if not self.config.password:
+# prompt = 'Enter password for host {h!r} and user {u!r}: '.format(
+# h=self.config.vsphere_host, u=self.config.vsphere_user)
+# self.config.password = getpass.getpass(prompt=prompt)
- try:
- self.post_run()
- except Exception as e:
- self.handle_error(str(e), e.__class__.__name__, True)
- self.exit_value = 97
-
- self.exit(self.exit_value)
-
- # -------------------------------------------------------------------------
- def post_run(self):
- """
- Dummy function to run after the main routine.
- Could be overwritten by descendant classes.
-
- """
+# self.handler.config = self.config
+# if self.args.rotate:
+# self.handler.rotate_only = True
+# if self.args.abort:
+# self.handler.abort = True
- if self.verbose > 1:
- LOG.info("executing post_run() ...")
-
- # -------------------------------------------------------------------------
- def _init_arg_parser(self):
- """
- Local called method to initiate the argument parser.
-
- @raise PBApplicationError: on some errors
-
- """
-
- self.arg_parser = argparse.ArgumentParser(
- prog=self.appname,
- description=self.description,
- usage=self.usage,
- epilog=self.argparse_epilog,
- prefix_chars=self.argparse_prefix_chars,
- add_help=False,
- )
-
- self.init_arg_parser()
-
- general_group = self.arg_parser.add_argument_group('General options')
- general_group.add_argument(
- '--color',
- action="store",
- dest='color',
- const='yes',
- default='auto',
- nargs='?',
- choices=['yes', 'no', 'auto'],
- help="Use colored output for messages.",
- )
-
- verbose_group = general_group.add_mutually_exclusive_group()
-
- verbose_group.add_argument(
- "-v", "--verbose",
- action="count",
- dest='verbose',
- help='Increase the verbosity level',
- )
-
- verbose_group.add_argument(
- "-q", "--quiet",
- action="store_true",
- dest='quiet',
- help='Silent execution, only warnings and errors are emitted.',
- )
-
- general_group.add_argument(
- "-h", "--help",
- action='help',
- dest='help',
- help='Show this help message and exit'
- )
- general_group.add_argument(
- "--usage",
- action='store_true',
- dest='usage',
- help="Display brief usage message and exit"
- )
- general_group.add_argument(
- "-V", '--version',
- action='version',
- version='Version of %(prog)s: {}'.format(self.version),
- help="Show program's version number and exit"
- )
+# self.handler.initialized = True
+# self.initialized = True
# -------------------------------------------------------------------------
def init_arg_parser(self):
"""
Public available method to initiate the argument parser.
-
- Note::
- avoid adding the general options '--verbose', '--help', '--usage'
- and '--version'. These options are allways added after executing
- this method.
-
- Descendant classes may override this method.
-
"""
+ super(CrTplApplication, self).init_arg_parser()
+
self.arg_parser.add_argument(
'-A', '--abort', dest='abort', action='store_true',
help="Abort creation of VMWare template after successsful creation of template VM.",
help="Execute rortation of existing templates only, don't create a new one."
)
- # -------------------------------------------------------------------------
- def _perform_arg_parser(self):
- """
- Underlaying method for parsing arguments.
- """
-
- self.args = self.arg_parser.parse_args()
-
- if self.args.usage:
- self.arg_parser.print_usage(sys.stdout)
- self.exit(0)
-
- if self.args.verbose is not None and self.args.verbose > self.verbose:
- self.verbose = self.args.verbose
-
- if self.args.quiet:
- self.quiet = self.args.quiet
-
- if self.args.color == 'yes':
- self._terminal_has_colors = True
- elif self.args.color == 'no':
- self._terminal_has_colors = False
- else:
- self._terminal_has_colors = self.terminal_can_color()
-
# -------------------------------------------------------------------------
def perform_arg_parser(self):
"""
Public available method to execute some actions after parsing
the command line parameters.
-
- Descendant classes may override this method.
- """
-
- 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
-
- # -------------------------------------------------------------------------
- def _init_env(self):
- """
- Initialization of self.env by application specific environment
- variables.
-
- It calls self.init_env(), after it has done his job.
-
- """
-
- for (key, value) in list(os.environ.items()):
-
- if not key.startswith(self.env_prefix):
- continue
-
- newkey = key.replace(self.env_prefix, '', 1)
- self.env[newkey] = value
-
- self.init_env()
-
- # -------------------------------------------------------------------------
- def init_env(self):
- """
- Public available method to initiate self.env additional to the implicit
- initialization done by this module.
- Maybe it can be used to import environment variables, their
- names not starting with self.env_prefix.
-
- Currently a dummy method, which ca be overriden by descendant classes.
-
"""
pass
- # -------------------------------------------------------------------------
- def _perform_env(self):
- """
- Method to do some useful things with the found environment.
-
- It calls self.perform_env(), after it has done his job.
-
- """
-
- # try to detect verbosity level from environment
- if 'VERBOSE' in self.env and self.env['VERBOSE']:
- v = 0
- try:
- v = int(self.env['VERBOSE'])
- except ValueError:
- v = 1
- if v > self.verbose:
- self.verbose = v
-
- self.perform_env()
+# 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
# -------------------------------------------------------------------------
- def perform_env(self):
- """
- Public available method to perform found environment variables after
- initialization of self.env.
-
- Currently a dummy method, which ca be overriden by descendant classes.
-
- """
-
- pass
-
- # -------------------------------------------------------------------------
- def colored(self, msg, color):
- """
- Wrapper function to colorize the message. Depending, whether the current
- terminal can display ANSI colors, the message is colorized or not.
-
- @param msg: The message to colorize
- @type msg: 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
-
+ def _run(self):
"""
+ Dummy function as main routine.
- if not self.terminal_has_colors:
- return msg
- return colorstr(msg, color)
+ MUST be overwritten by descendant classes.
- # -------------------------------------------------------------------------
- def get_command(self, cmd, quiet=False):
"""
- Searches the OS search path for the given command and gives back the
- normalized position of this command.
- If the command is given as an absolute path, it check the existence
- of this command.
-
- @param cmd: the command to search
- @type cmd: str
- @param quiet: No warning message, if the command could not be found,
- only a debug message
- @type quiet: bool
- @return: normalized complete path of this command, or None,
- if not found
- @rtype: str or None
+ LOG.info("Starting {a!r}, version {v!r} ...".format(
+ a=self.appname, v=self.version))
- """
+# try:
+# ret = self.handler()
+# self.exit(ret)
+# except ExpectedHandlerError as e:
+# self.handle_error(str(e), "Temporary VM")
+# self.exit(5)
- if self.verbose > 2:
- LOG.debug("Searching for command {!r} ...".format(cmd))
-
- # Checking an absolute path
- if os.path.isabs(cmd):
- if not os.path.exists(cmd):
- LOG.warning("Command {!r} doesn't exists.".format(cmd))
- return None
- if not os.access(cmd, os.X_OK):
- msg = "Command {!r} is not executable.".format(cmd)
- LOG.warning(msg)
- return None
- return os.path.normpath(cmd)
-
- # Checking a relative path
- for d in caller_search_path():
- if self.verbose > 3:
- LOG.debug("Searching command in {!r} ...".format(d))
- p = os.path.join(d, cmd)
- if os.path.exists(p):
- if self.verbose > 2:
- LOG.debug("Found {!r} ...".format(p))
- if os.access(p, os.X_OK):
- return os.path.normpath(p)
- else:
- LOG.debug("Command {!r} is not executable.".format(p))
-
- # command not found, sorry
- if quiet:
- if self.verbose > 2:
- LOG.debug("Command {!r} not found.".format(cmd))
- else:
- LOG.warning("Command {!r} not found.".format(cmd))
-
- return None
# =============================================================================