# Abstract base classes for UI classes # # Copyright (C) 2013 Red Hat, Inc. # # This copyrighted material is made available to anyone wishing to use, # modify, copy, or redistribute it subject to the terms and conditions of # the GNU General Public License v.2, or (at your option) any later version. # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY expressed or implied, including the implied warranties of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. You should have received a copy of the # GNU General Public License along with this program; if not, write to the # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. Any Red Hat trademarks that are incorporated in the # source code or documentation are not subject to the GNU General Public # License and may only be used or replicated with the express permission of # Red Hat, Inc. # # This file contains abstract base classes that provide specific functionality # that can be added to another class. The idea is sort-of modelled after Java's # interfaces. An abstract base class cannot be instatiated, and it provides a # contract for classes that inherit from it: any method or property marked as # abstract in the base class must be overriden in the inheriting class. This # allows for cleaner implementation of certain types of mixin-classes: a class # that adds functionality to another class can explicitly require that methods # or properties be provided by the inheriting class or another superclass of # the inheriting class. # # In general, classes that inherit from abstract base classes should place the # abstract base class at the end of the inheritance list. This way any abstract # methods or properties in the abc will be overridden by the base classes # that are first in the inheritance list. For example, an abstract base class # may add a method that reads from Spoke.data: # # class Mixin(object): # __metaclass__ = ABCMeta # # @property # @abstractmethod # def data(self): # pass # # def isHD(self): # return self.data.method == "harddrive" # # The Mixin class will add the method isHD to any class that inherits from it, # and classes that inherit from Mixin must provide a data property. # # class MixedObject(UIObject, Mixin): # .... # # The method resolution order of MixedObject resolves UIObject.data before # Mixin.data, so UIObject.data satisfies the requirment that Mixin.data be # overriden. from abc import ABCMeta, abstractmethod from pyanaconda.core import constants from pyanaconda.core.constants import DRACUT_REPO_DIR from pyanaconda.core.i18n import _ from pyanaconda.core.path import join_paths from pyanaconda.core.product import get_product_name, get_product_version from pyanaconda.core.payload import create_nfs_url, create_hdd_url from pyanaconda.modules.common.structures.payload import RepoConfigurationData from pyanaconda.ui.lib.payload import create_source, set_source, tear_down_sources def get_distribution_text(): return _("%(productName)s %(productVersion)s INSTALLATION") % { "productName": get_product_name().upper(), "productVersion": get_product_version().upper() } class StorageCheckHandler(object, metaclass=ABCMeta): errors = [] warnings = [] class SourceSwitchHandler(object, metaclass=ABCMeta): """ A class that can be used as a mixin handling installation source switching. It will correctly switch to the new method and cleanup any previous method set. """ @property @abstractmethod def payload(self): pass def __init__(self): self._device = None self._current_iso_path = None def _set_source(self, source_proxy): """Set a new installation source.""" tear_down_sources(self.payload.proxy) set_source(self.payload.proxy, source_proxy) def set_source_hdd_iso(self, device_name, iso_path): """ Switch to the HDD ISO install source :param device_name: name of the partition hosting the ISO :type device_name: string :param iso_path: full path to the source ISO file :type iso_path: string """ configuration = RepoConfigurationData() configuration.url = create_hdd_url(device_name, join_paths("/", iso_path)) source_proxy = create_source(constants.SOURCE_TYPE_HDD) source_proxy.Configuration = RepoConfigurationData.to_structure(configuration) self._set_source(source_proxy) def set_source_url(self, url, url_type=constants.URL_TYPE_BASEURL, proxy=None): """ Switch to install source specified by URL """ source_proxy = create_source(constants.SOURCE_TYPE_URL) configuration = RepoConfigurationData() configuration.url = url configuration.type = url_type configuration.proxy = proxy or "" source_proxy.Configuration = RepoConfigurationData.to_structure(configuration) self._set_source(source_proxy) def set_source_nfs(self, server, directory, opts): """ Switch to NFS install source """ configuration = RepoConfigurationData() configuration.url = create_nfs_url(server, directory, opts) source_proxy = create_source(constants.SOURCE_TYPE_NFS) source_proxy.Configuration = RepoConfigurationData.to_structure(configuration) self._set_source(source_proxy) def set_source_cdrom(self): """ Switch to cdrom install source """ source_proxy = create_source(constants.SOURCE_TYPE_CDROM) self._set_source(source_proxy) def set_source_hmc(self): """ Switch to install source via HMC """ hmc_source_proxy = create_source(constants.SOURCE_TYPE_HMC) self._set_source(hmc_source_proxy) def set_source_dracut(self): """ Switch to install source provided by Dracut.""" source_proxy = create_source(constants.SOURCE_TYPE_REPO_PATH) source_proxy.Path = DRACUT_REPO_DIR self._set_source(source_proxy) def set_source_closest_mirror(self, updates_enabled=True): """ Switch to the closest mirror install source """ source_proxy = create_source(constants.SOURCE_TYPE_CLOSEST_MIRROR) source_proxy.UpdatesEnabled = updates_enabled self._set_source(source_proxy) class InputCheck(object): """Handle an input validation check. This class is used by classes that implement InputCheckHandler to manage and manipulate input validation check instances. """ # Use as a return value to indicate a passed check CHECK_OK = None # Treat the check as failed but don't display anything # This can be used, for example, to reject empty input without setting # a big loud error message. CHECK_SILENT = "" # Read-only properties input_obj = property(lambda s: s._input_obj, doc="The input to check.") run_check = property(lambda s: s._run_check, doc="A function to call to perform the input check.") data = property(lambda s: s._data, doc="Optional data associated with the input check.") check_status = property(lambda s: s._check_status, doc="The current status of the check") def __init__(self, parent, input_obj, run_check, data=None): """Create a new input validation check. :param InputCheckHandler parent: The InputCheckHandler object to which this check is being added. :param function input_obj: An object representing the input to check. :param function run_check: A function to call to perform the input check. This function is called with the InputCheck object as a parameter. The return value an object representing the error state, or CHECK_OK if the check succeeds. :param data: Optional data associated with the input check """ self._parent = parent self._input_obj = input_obj self._run_check = run_check self._data = data self._check_status = None self._enabled = True def update_check_status(self): """Run an input validation check.""" if not self.enabled: return self._check_status = self._run_check(self) self._parent.set_status(self) @property def enabled(self): """Whether the check is enabled or not. Disabling a check indicates that the status will not change if the input changes. The value of check_status will be the result of the last time the InputCheck was run when enabled. Disabled checks will not be included in InputCheckHandler.failed_checks. """ return self._enabled @enabled.setter def enabled(self, value): self._enabled = value class InputCheckHandler(object, metaclass=ABCMeta): """Provide a framework for adding input validation checks to a screen. This helper class provides a mean of defining and associating input validation checks with an input screen. Running the checks and acting upon the results is left up to the subclasses. Classes implementing InputCheckHandler should ensure that the checks are run at the appropriate times (e.g., calling InputCheck.update_check_status when input is changed), and that input for the screen is not accepted if self.failed_checks is not empty. See GUIInputCheckHandler and GUISpokeInputCheckHandler for additional functionality. """ def __init__(self): self._check_list = [] @abstractmethod def get_input(self, input_obj): """Return the input string from an input object. :param input_obj: The input object :returns: An input string :rtype: str """ pass @abstractmethod def set_status(self, inputcheck): """Update the status of the window from the input validation results. This function could, for example, set or clear an error on the window, or display a message near an input area with invalid data. :param InputCheck inputcheck: The InputCheck object whose status last changed. """ pass def add_check(self, input_obj, run_check, data=None): """Add an input validation check to this object. :param input_obj: An object representing the input to check. :param function run_check: A function to call to perform the input check. This function is called with the InputCheck object as a parameter. The return value an object representing the error state, or CHECK_OK if the check succeeds. :param data: Optional data associated with the input check :returns: The InputCheck object created. :rtype: InputCheck """ checkRef = InputCheck(self, input_obj, run_check, data) self._check_list.append(checkRef) return checkRef @property def failed_checks(self): """A generator of all failed input checks""" return (c for c in self._check_list \ if c.enabled and c.check_status != InputCheck.CHECK_OK) @property def failed_checks_with_message(self): """A generator of all failed input checks with an error message""" return (c for c in self._check_list \ if c.enabled and c.check_status not in (InputCheck.CHECK_OK, InputCheck.CHECK_SILENT)) @property def checks(self): """An iterator over all input checks""" return self._check_list.__iter__()