Source code for COT.submodule

#!/usr/bin/env python
#
# submodule.py - Abstract interface for COT 'command' submodules.
#
# December 2014, Glenn F. Matthews
# Copyright (c) 2014-2016 the COT project developers.
# See the COPYRIGHT.txt file at the top-level directory of this distribution
# and at https://github.com/glennmatthews/cot/blob/master/COPYRIGHT.txt.
#
# This file is part of the Common OVF Tool (COT) project.
# It is subject to the license terms in the LICENSE.txt file found in the
# top-level directory of this distribution and at
# https://github.com/glennmatthews/cot/blob/master/LICENSE.txt. No part
# of COT, including this file, may be copied, modified, propagated, or
# distributed except according to the terms contained in the LICENSE.txt file.

"""Parent classes for implementing COT subcommands.

**Classes**

.. autosummary::
  :nosignatures:

  COTGenericSubmodule
  COTReadOnlySubmodule
  COTSubmodule
"""

import os.path
import logging

from .data_validation import InvalidInputError
from .vm_factory import VMFactory

logger = logging.getLogger(__name__)


[docs]class COTGenericSubmodule(object): """Abstract interface for COT command submodules. Attributes: :attr:`vm`, :attr:`ui` .. note :: Generally a command should not inherit directly from this class, but should instead subclass :class:`COTReadOnlySubmodule` or :class:`COTSubmodule` as appropriate. """
[docs] def __init__(self, ui): """Instantiate this submodule with the given UI. Args: ui (UI): User interface instance. """ self.vm = None """Virtual machine description (:class:`VMDescription`).""" self.ui = ui """User interface instance (:class:`~ui_shared.UI` or subclass)."""
[docs] def ready_to_run(self): # pylint: disable=no-self-use """Check whether the module is ready to :meth:`run`. Returns: tuple: ``(True, ready_message)`` or ``(False, reason_why_not)`` """ return True, "Ready to go!"
[docs] def run(self): """Do the actual work of this submodule. Raises: InvalidInputError: if :meth:`ready_to_run` reports ``False`` """ (ready, reason) = self.ready_to_run() if not ready: raise InvalidInputError(reason)
# Do the work now...
[docs] def finished(self): """Do any final actions before being destroyed. This class does nothing; subclasses may choose to do things like write their VM state out to a file. """ pass
[docs] def destroy(self): """Destroy any VM associated with this submodule.""" if self.vm is not None: self.vm.destroy() self.vm = None
[docs] def create_subparser(self): """Add subparser for the CLI of this submodule.""" pass
[docs]class COTReadOnlySubmodule(COTGenericSubmodule): """Class for submodules that do not modify the OVF, such as 'deploy'. Inherited attributes: :attr:`vm`, :attr:`ui` Attributes: :attr:`package` """
[docs] def __init__(self, ui): """Instantiate this submodule with the given UI. Args: ui (UI): User interface instance. """ super(COTReadOnlySubmodule, self).__init__(ui) self._package = None
@property def package(self): """VM description file to read from. Calls :meth:`COT.vm_factory.VMFactory.create` to instantiate :attr:`self.vm` from the provided file. Raises: InvalidInputError: if the file does not exist. """ return self._package @package.setter def package(self, value): if value is not None and not os.path.exists(value): raise InvalidInputError("Specified package {0} does not exist!" .format(value)) if self.vm is not None: self.vm.destroy() self.vm = None if value is not None: self.vm = VMFactory.create(value, None) self._package = value
[docs] def ready_to_run(self): """Check whether the module is ready to :meth:`run`. Returns: tuple: ``(True, ready_message)`` or ``(False, reason_why_not)`` """ if self.package is None: return False, "PACKAGE is a mandatory argument!" return super(COTReadOnlySubmodule, self).ready_to_run()
[docs]class COTSubmodule(COTGenericSubmodule): """Class for submodules that read and write the OVF. Inherited attributes: :attr:`vm`, :attr:`ui` Attributes: :attr:`package`, :attr:`output` """
[docs] def __init__(self, ui): """Instantiate this submodule with the given UI. Args: ui (UI): User interface instance. """ super(COTSubmodule, self).__init__(ui) self._package = None # Default to an unspecified output rather than no output self._output = ""
@property def package(self): """VM description file to read (and possibly write). Calls :meth:`COT.vm_factory.VMFactory.create` to instantiate :attr:`self.vm` from the provided file. Raises: InvalidInputError: if the file does not exist. """ return self._package @package.setter def package(self, value): if value is not None and not os.path.exists(value): raise InvalidInputError("Specified package {0} does not exist!" .format(value)) if self.vm is not None: self.vm.destroy() self.vm = None if value is not None: self.vm = VMFactory.create(value, self.output) self._package = value @property def output(self): """Output file for this submodule. If the specified file already exists, will prompt the user (:meth:`~COT.ui_shared.UI.confirm_or_die`) to confirm overwriting the existing file. """ return self._output @output.setter def output(self, value): if value and value != self._output and os.path.exists(value): self.ui.confirm_or_die("Overwrite existing file {0}?" .format(value)) self._output = value if self.vm is not None: self.vm.output_file = value
[docs] def ready_to_run(self): """Check whether the module is ready to :meth:`run`. Returns: tuple: ``(True, ready_message)`` or ``(False, reason_why_not)`` """ if self.package is None: return False, "PACKAGE is a mandatory argument!" return super(COTSubmodule, self).ready_to_run()
[docs] def run(self): """Do the actual work of this submodule. If :attr:`output` was not previously set, automatically sets it to the value of :attr:`PACKAGE`. Raises: InvalidInputError: if :meth:`ready_to_run` reports ``False`` """ super(COTSubmodule, self).run() if not self.output: self.output = self.package
# Do the work now...
[docs] def finished(self): """Write the current VM state out to disk if requested.""" # do any submodule-specific work here, then: if self.vm is not None: self.vm.write() super(COTSubmodule, self).finished()