Esempio n. 1
0
class CheckResult(object):
    """Result of an input check."""
    def __init__(self):
        self._success = False
        self._error_message = ""
        self.error_message_changed = Signal()

    @property
    def success(self):
        return self._success

    @success.setter
    def success(self, value):
        self._success = value

    @property
    def error_message(self):
        """Optional error message describing why the input is not valid.

        :returns: why the input is bad (provided it is bad) or None
        :rtype: str or None
        """
        return self._error_message

    @error_message.setter
    def error_message(self, new_error_message):
        self._error_message = new_error_message
        self.error_message_changed.emit(new_error_message)
Esempio n. 2
0
 def __init__(self):
     self._lock = RLock()
     self._modules = set()
     self._all_modules_added = False
     self.init_done = Signal()
     self._init_done_triggered = False
     self._added_module_count = 0
Esempio n. 3
0
class InputField(object):
    """An input field containing data to be checked.

    The input field can have an initial value that can be
    monitored for change via signals.
    """
    def __init__(self, initial_content):
        self._initial_content = initial_content
        self._content = initial_content
        self.changed = Signal()
        self._initial_change_signal_fired = False
        self.changed_from_initial_state = Signal()

    @property
    def content(self):
        return self._content

    @content.setter
    def content(self, new_content):
        old_content = self._content
        self._content = new_content
        # check if the input changed from the initial state
        if old_content != new_content:
            self.changed.emit()
            # also fire the changed-from-initial-state signal if required
            if not self._initial_change_signal_fired and new_content != self._initial_content:
                self.changed_from_initial_state.emit()
                self._initial_change_signal_fired = True
Esempio n. 4
0
    def __init__(self):
        self._dasds = []

        self._can_format_unformatted = True
        self._can_format_ldl = True

        self._report = Signal()
        self._report.connect(log.debug)
Esempio n. 5
0
 def __init__(self, name):
     self._name = name
     self._done = False
     self._running = False
     self._lock = RLock()
     self._parent = None
     self._start_timestamp = None
     self._done_timestamp = None
     self.started = Signal()
     self.completed = Signal()
Esempio n. 6
0
 def __init__(self):
     self._lock = RLock()
     self._modules = set()
     self._all_modules_added = False
     self.init_done = Signal()
     self._init_done_triggered = False
     self._added_module_count = 0
Esempio n. 7
0
    def __init__(self, name, status_message=None):
        super(TaskQueue, self).__init__(name=name)
        self._status_message = status_message
        self._current_task_number = None
        self._current_queue_number = None
        # the list backing this TaskQueue instance
        self._list = []
        # triggered if a TaskQueue contained in this one was started/completed
        self.queue_started = Signal()
        self.queue_completed = Signal()
        # triggered when a task is started
        self.task_started = Signal()
        self.task_completed = Signal()

        # connect to the task & queue started signals for
        # progress reporting purposes
        self.queue_started.connect(self._queue_started_cb)
        self.task_started.connect(self._task_started_cb)
Esempio n. 8
0
 def __init__(self):
     super().__init__()
     self._check_request = None
     self._password_score = 0
     self.password_score_changed = Signal()
     self._status_text = ""
     self.status_text_changed = Signal()
     self._password_quality = 0
     self.password_quality_changed = Signal()
     self._length_ok = False
     self.length_ok_changed = Signal()
Esempio n. 9
0
    def __init__(self, dbus_modul_path):
        super().__init__()
        self._progress = (0, "")

        self.__cancel_lock = Lock()
        self.__cancel = False

        self._started_signal = Signal()
        self._stopped_signal = Signal()
        self._progress_changed_signal = Signal()
        self._error_raised_signal = Signal()

        self._thread_name = "{}-{}".format(THREAD_DBUS_TASK, self.name)
Esempio n. 10
0
    def __init__(self, storage, payload, instclass):
        """Create a new Spoke instance.

           The arguments this base class accepts defines the API that spokes
           have to work with.  A Spoke does not get free reign over everything
           in the anaconda class, as that would be a big mess.  Instead, a
           Spoke may count on the following:

           data         -- An instance of a pykickstart Handler object.  The
                           Spoke uses this to populate its UI with defaults
                           and to pass results back after it has run. The data
                           property must be implemented by classes inherting
                           from Spoke.
           storage      -- An instance of storage.Storage.  This is useful for
                           determining what storage devices are present and how
                           they are configured.
           payload      -- An instance of a packaging.Payload subclass.  This
                           is useful for displaying and selecting packages to
                           install, and in carrying out the actual installation.
           instclass    -- An instance of a BaseInstallClass subclass.  This
                           is useful for determining distribution-specific
                           installation information like default package
                           selections and default partitioning.
        """
        self._storage = storage
        self.payload = payload
        self.instclass = instclass
        self.applyOnSkip = False

        self.visitedSinceApplied = True

        # entry and exit signals
        # - get the hub instance as a single argument
        self.entered = Signal()
        self.exited = Signal()

        # connect default callbacks for the signals
        self.entered.connect(self.entry_logger)
        self.entered.connect(self._mark_screen_visited)
        self.exited.connect(self.exit_logger)
Esempio n. 11
0
    def __init__(self, storage, payload, instclass):
        """Create a new Spoke instance.

           The arguments this base class accepts defines the API that spokes
           have to work with.  A Spoke does not get free reign over everything
           in the anaconda class, as that would be a big mess.  Instead, a
           Spoke may count on the following:

           data         -- An instance of a pykickstart Handler object.  The
                           Spoke uses this to populate its UI with defaults
                           and to pass results back after it has run. The data
                           property must be implemented by classes inherting
                           from Spoke.
           storage      -- An instance of storage.Storage.  This is useful for
                           determining what storage devices are present and how
                           they are configured.
           payload      -- An instance of a payload.Payload subclass.  This
                           is useful for displaying and selecting packages to
                           install, and in carrying out the actual installation.
           instclass    -- An instance of a BaseInstallClass subclass.  This
                           is useful for determining distribution-specific
                           installation information like default package
                           selections and default partitioning.
        """
        self._storage = storage
        self.payload = payload
        self.instclass = instclass
        self.applyOnSkip = False

        self.visitedSinceApplied = True

        # entry and exit signals
        # - get the hub instance as a single argument
        self.entered = Signal()
        self.exited = Signal()

        # connect default callbacks for the signals
        self.entered.connect(self.entry_logger)
        self.entered.connect(self._mark_screen_visited)
        self.exited.connect(self.exit_logger)
Esempio n. 12
0
    def __init__(self, initial_password_content,
                 initial_password_confirmation_content, policy):
        self._password = InputField(initial_password_content)
        self._password_confirmation = InputField(
            initial_password_confirmation_content)
        self._checks = []
        self._success = False
        self._error_message = ""
        self._failed_checks = []
        self._successful_checks = []
        self._policy = policy
        self._username = None
        self._fullname = ""
        # connect to the password field signals
        self.password.changed.connect(self.run_checks)
        self.password_confirmation.changed.connect(self.run_checks)

        # password naming (for use in status/error messages)
        self._name_of_password = _(constants.NAME_OF_PASSWORD)
        self._name_of_password_plural = _(constants.NAME_OF_PASSWORD_PLURAL)

        # signals
        self.checks_done = Signal()
Esempio n. 13
0
    def __init__(self, name, status_message=None):
        super(TaskQueue, self).__init__(name=name)
        self._status_message = status_message
        self._current_task_number = None
        self._current_queue_number = None
        # the list backing this TaskQueue instance
        self._list = []
        # triggered if a TaskQueue contained in this one was started/completed
        self.queue_started = Signal()
        self.queue_completed = Signal()
        # triggered when a task is started
        self.task_started = Signal()
        self.task_completed = Signal()

        # connect to the task & queue started signals for
        # progress reporting purposes
        self.queue_started.connect(self._queue_started_cb)
        self.task_started.connect(self._task_started_cb)
Esempio n. 14
0
 def __init__(self):
     self._success = False
     self._error_message = ""
     self.error_message_changed = Signal()
Esempio n. 15
0
 def lambda_test(self):
     """Test if a lambda can be correctly connected to a signal."""
     foo = FooClass()
     signal = Signal()
     self.assertIsNone(foo.var)
     # connect the signal
     # pylint: disable=unnecessary-lambda
     lambda_instance = lambda x: foo.set_var(x)
     signal.connect(lambda_instance)
     # trigger the signal
     signal.emit("bar")
     # check if the callback triggered correctly
     self.assertEqual(foo.var, "bar")
     # try to trigger the signal again
     signal.emit("baz")
     self.assertEqual(foo.var, "baz")
     # now try to disconnect the signal
     signal.disconnect(lambda_instance)
     # check that calling the signal again
     # no longer triggers the callback
     signal.emit("anaconda")
     self.assertEqual(foo.var, "baz")
Esempio n. 16
0
class TaskQueue(BaseTask):
    """TaskQueue represents a queue of TaskQueues or Tasks.

    TaskQueues and Tasks can be mixed in a single TaskQueue.
    """
    def __init__(self, name, status_message=None):
        super(TaskQueue, self).__init__(name=name)
        self._status_message = status_message
        self._current_task_number = None
        self._current_queue_number = None
        # the list backing this TaskQueue instance
        self._list = []
        # triggered if a TaskQueue contained in this one was started/completed
        self.queue_started = Signal()
        self.queue_completed = Signal()
        # triggered when a task is started
        self.task_started = Signal()
        self.task_completed = Signal()

        # connect to the task & queue started signals for
        # progress reporting purposes
        self.queue_started.connect(self._queue_started_cb)
        self.task_started.connect(self._task_started_cb)

    @synchronized
    def _queue_started_cb(self, *args):
        self._current_queue_number += 1

    @synchronized
    def _task_started_cb(self, *args):
        self._current_task_number += 1

    @property
    def status_message(self):
        """A status message describing the Queue is trying to achieve.

        Eq. "Converting all foo into bar."

        The current main usecase is to set the ProgressHub status message when
        a TaskQueue is started.

        :returns: a status message
        :rtype: str
        """
        return self._status_message

    @property
    @synchronized
    def queue_count(self):
        """Returns number of TaskQueues contained in this and all nested TaskQueues.

        :returns: number of queues
        :rtype: int
        """
        queue_count = 0
        for item in self:
            # count only queues
            if isinstance(item, TaskQueue):
                # count the queue itself
                queue_count += 1
                # and its contents
                queue_count += item.queue_count
        return queue_count

    @property
    @synchronized
    def task_count(self):
        """Returns number of tasks contained in this and all nested TaskQueues.

        :returns: number of tasks
        :rtype: int
        """
        task_count = 0
        for item in self:
            if isinstance(item, Task):
                # count tasks
                task_count += 1
            elif isinstance(item, TaskQueue):
                # count tasks in nested queues
                task_count += item.task_count
        return task_count

    @property
    @synchronized
    def current_task_number(self):
        """Number of the currently running task (if any).

        :returns: number of the currently running task (if any)
        :rtype: int or None if no task is currently running
        """
        return self._current_task_number

    @property
    @synchronized
    def current_queue_number(self):
        """Number of the currently running task queue (if any).

        :returns: number of the currently running task queue (if any)
        :rtype: int or None if no task queue is currently running
        """
        return self._current_queue_number

    @property
    @synchronized
    def progress(self):
        """Task queue processing progress.

        The progress is reported as a floating point number from 0.0 to 1.0.
        :returns: task queue processing progress
        :rtype: float
        """
        if self.current_task_number:
            return self.task_count / self.current_task_number
        else:
            return 0.0

    @property
    @synchronized
    def summary(self):
        """Return a multi-line summary of the contents of the task queue.

        :returns: summary of task queue contents
        :rtype: str
        """
        if self.parent is None:
            message = "Top-level task queue: %s\n" % self.name
            # this is the top-level queue, so add some "global" stats
            message += "Number of task queues: %d\n" % self.queue_count
            message += "Number of tasks: %d\n" % self.task_count
            message += "Task & task group listing:\n"
        else:
            message = "Task queue: %s\n" % self.name
        for item in self:
            for line in item.summary.splitlines():
                message += " %s\n" % line
        # remove trailing newlines from the top level message
        if self.parent is None and message[-1] == "\n":
            message = message.rstrip("\n")
        return message

    @property
    @synchronized
    def parent(self):
        """The parent task queue of this task queue (if any).

        :returns: parent of this task queue (if any)
        :rtype: TaskQueue instance or None
        """
        return self._parent

    @parent.setter
    @synchronized
    def parent(self, parent_item):
        # check if a parent is already set
        if self._parent is not None:
            # disconnect from the previous parent first
            self.started.disconnect(self._parent.queue_started.emit)
            self.completed.disconnect(self._parent.queue_completed.emit)
            self.queue_started.disconnect(self._parent.queue_started.emit)
            self.queue_completed.disconnect(self._parent.queue_completed.emit)
            self.task_started.disconnect(self._parent.task_started.emit)
            self.task_completed.disconnect(self._parent.task_completed.emit)
        # set the parent
        self._parent = parent_item
        # Connect own signals "up" to the parent,
        # so that it is possible to monitor how all nested TaskQueues and Tasks
        # are running from the top-level element.

        # connect own start/completion signal to parents queue start/completion signal
        self.started.connect(self._parent.queue_started.emit)
        self.completed.connect(self._parent.queue_completed.emit)

        # propagate start/completion signals from nested queues/tasks
        self.queue_started.connect(self._parent.queue_started.emit)
        self.queue_completed.connect(self._parent.queue_completed.emit)
        self.task_started.connect(self._parent.task_started.emit)
        self.task_completed.connect(self._parent.task_completed.emit)

    def start(self):
        """Start processing of the task queue."""
        do_start = False
        with self._lock:
            # the task queue can only be started once
            if self.running or self.done:
                if self.running:
                    # attempt to start a task that is already running
                    log.error("Can't start task queue %s - already running.")
                else:
                    # attempt to start a task that an already finished task
                    log.error("Can't start task queue %s - already done.")
            else:
                do_start = True
                self._running = True
                if self.task_count:
                    # only set the initial task number if we have some tasks
                    self._current_task_number = 0
                    self._current_queue_number = 0
                else:
                    log.warning(
                        "Attempting to start an empty task queue (%s).",
                        self.name)

        if do_start:
            # go over all task groups and their tasks in order
            self.started.emit(self)
            if len(self) == 0:
                log.warning("The task group %s is empty.", self.name)
            for item in self:
                # start the item (TaskQueue/Task)
                item.start()

            # we are done, set the task queue state accordingly
            with self._lock:
                self._running = False
                self._done = True
                # also set the current task variables accordingly as we no longer process a task
                self._current_task_number = None
                self._current_queue_number = None

            # trigger the "completed" signals
            self.completed.emit(self)

    # implement the Python list "interface" and make sure parent is always
    # set to a correct value
    @synchronized
    def append(self, item):
        item.parent = self
        self._list.append(item)

    @synchronized
    def insert(self, index, item):
        item.parent = self
        self._list.insert(index, item)

    @synchronized
    def __setitem__(self, index, item):
        item.parent = self
        return self._list.__setitem__(index, item)

    @synchronized
    def __len__(self):
        return self._list.__len__()

    @synchronized
    def count(self):
        return self._list.count()

    @synchronized
    def __getitem__(self, ii):
        return self._list[ii]

    @synchronized
    def __delitem__(self, index):
        self._list[index].parent = None
        del self._list[index]

    @synchronized
    def pop(self):
        item = self._list.pop()
        item.parent = None
        return item

    @synchronized
    def clear(self):
        for item in self._list:
            item.parent = None
        self._list.clear()
Esempio n. 17
0
class Controller(object):
    """A singleton that track initialization of Anaconda modules."""
    def __init__(self):
        self._lock = RLock()
        self._modules = set()
        self._all_modules_added = False
        self.init_done = Signal()
        self._init_done_triggered = False
        self._added_module_count = 0

    @synchronized
    def module_init_start(self, module):
        """Tell the controller that a module has started initialization.

        :param module: a module which has started initialization
        """
        if self._all_modules_added:
            log.warning("Late module_init_start() from: %s", self)
        elif module in self._modules:
            log.warning("Module already marked as initializing: %s", module)
        else:
            self._added_module_count += 1
            self._modules.add(module)

    def all_modules_added(self):
        """Tell the controller that all expected modules have started initialization.

        Tell the controller that all expected modules have been registered
        for initialization tracking (or have already been initialized)
        and no more are expected to be added.

        This is needed so that we don't prematurely trigger the init_done signal
        when all known modules finish initialization while other modules have not
        yet been added.
        """
        init_done = False
        with self._lock:
            log.info("Initialization of all modules (%d) has been started.",
                     self._added_module_count)
            self._all_modules_added = True

            # if all modules finished initialization before this was added then
            # trigger the init_done signal at once
            if not self._modules and not self._init_done_triggered:
                self._init_done_triggered = True
                init_done = True

        # we should emit the signal out of the main lock as it doesn't make sense
        # to hold the controller-state lock once we decide to the trigger init_done signal
        # (and any callbacks registered on it)
        if init_done:
            self._trigger_init_done()

    def module_init_done(self, module):
        """Tell the controller that a module has finished initialization.

        And if no more modules are being initialized trigger the init_done signal.

        :param module: a module that has finished initialization
        """
        init_done = False
        with self._lock:
            # prevent the init_done signal from
            # being triggered more than once
            if self._init_done_triggered:
                log.warning("Late module_init_done from module %s.", module)
            else:
                if module in self._modules:
                    log.info("Module initialized: %s", module)
                    self._modules.discard(module)
                else:
                    log.warning("Unknown module reported as initialized: %s",
                                module)
                # don't trigger the signal if all modules have not yet been added
                if self._all_modules_added and not self._modules:
                    init_done = True
                    self._init_done_triggered = True

        # we should emit the signal out of the main lock as it doesn't make sense
        # to hold the controller-state lock once we decide to the trigger init_done signal
        # (and any callbacks registered on it)
        if init_done:
            self._trigger_init_done()

    def _trigger_init_done(self):
        log.info("All modules have been initialized.")
        self.init_done.emit()
Esempio n. 18
0
class Hub(object, metaclass=ABCMeta):
    """A Hub is an overview UI screen.  A Hub consists of one or more grids of
       configuration options that the user may choose from.  Each grid is
       provided by a SpokeCategory, and each option is provided by a Spoke.
       When the user dives down into a Spoke and is finished interacting with
       it, they are returned to the Hub.

       Some Spokes are required.  The user must interact with all required
       Spokes before they are allowed to proceed to the next stage of
       installation.

       From a layout perspective, a Hub is the entirety of the screen, though
       the screen itself can be roughly divided into thirds.  The top third is
       some basic navigation information (where you are, what you're
       installing).  The middle third is the grid of Spokes.  The bottom third
       is an action area providing additional buttons (quit, continue) or
       progress information (during package installation).

       Installation may consist of multiple chained Hubs, or Hubs with
       additional standalone screens either before or after them.
    """

    def __init__(self, storage, payload, instclass):
        """Create a new Hub instance.

           The arguments this base class accepts defines the API that Hubs
           have to work with.  A Hub does not get free reign over everything
           in the anaconda class, as that would be a big mess.  Instead, a
           Hub may count on the following:

           data         -- An instance of a pykickstart Handler object.  The
                           Hub uses this to populate its UI with defaults
                           and to pass results back after it has run. The data
                           property must be implemented by classes inheriting
                           from Hub.
           storage      -- An instance of storage.Storage.  This is useful for
                           determining what storage devices are present and how
                           they are configured.
           payload      -- An instance of a packaging.Payload subclass.  This
                           is useful for displaying and selecting packages to
                           install, and in carrying out the actual installation.
           instclass    -- An instance of a BaseInstallClass subclass.  This
                           is useful for determining distribution-specific
                           installation information like default package
                           selections and default partitioning.
        """
        self._storage = storage
        self.payload = payload
        self.instclass = instclass

        self.paths = {}
        self._spokes = {}

        # entry and exit signals
        # - get the hub instance as a single argument
        self.entered = Signal()
        self.exited = Signal()

        # connect the default callbacks
        self.entered.connect(self.entry_logger)
        self.exited.connect(self.exit_logger)

    @abstractproperty
    def data(self):
        pass

    @property
    def storage(self):
        return self._storage

    def set_path(self, path_id, paths):
        """Update the paths attribute with list of tuples in the form (module
           name format string, directory name)"""
        self.paths[path_id] = paths

    def entry_logger(self, hub_instance):
        """Log immediately before this hub is about to be displayed on the
           screen.  Subclasses may override this method if they want to log
           more specific information, but an overridden method should finish
           by calling this method so the entry will be logged.

           Note that due to how the GUI flows, hubs are only entered once -
           when they are initially displayed.  Going to a spoke from a hub
           and then coming back to the hub does not count as exiting and
           entering.
        """
        log.debug("Entered hub: %s", hub_instance)

    def _collectCategoriesAndSpokes(self):
        """This method is provided so that is can be overridden in a subclass
           by a custom collect method.
           One example of such usage is the Initial Setup application.
        """
        return collectCategoriesAndSpokes(self.paths, self.__class__, self.data.displaymode.displayMode)

    def exit_logger(self, hub_instance):
        """Log when a user leaves the hub.  Subclasses may override this
           method if they want to log more specific information, but an
           overridden method should finish by calling this method so the
           exit will be logged.

           Note that due to how the GUI flows, hubs are not exited when the
           user selects a spoke from the hub.  They are only exited when the
           continue or quit button is clicked on the hub.
        """
        log.debug("Left hub: %s", hub_instance)

    def __repr__(self):
        """Return the class name as representation.

        Returning the class name should be enough the uniquely identify a hub.
        """
        return self.__class__.__name__
Esempio n. 19
0
class PasswordChecker(object):
    """Run multiple password and input checks in a given order and report the results.

    All added checks (in insertion order) will be run and results returned as error message
    and success value (True/False). If any check fails success will be False and the
    error message of the first check to fail will be returned.

    It's also possible to mark individual checks to be skipped by setting their skip property to True.
    Such check will be skipped during the checking run.
    """
    def __init__(self, initial_password_content,
                 initial_password_confirmation_content, policy):
        self._password = InputField(initial_password_content)
        self._password_confirmation = InputField(
            initial_password_confirmation_content)
        self._checks = []
        self._success = False
        self._error_message = ""
        self._failed_checks = []
        self._successful_checks = []
        self._policy = policy
        self._username = None
        self._fullname = ""
        # connect to the password field signals
        self.password.changed.connect(self.run_checks)
        self.password_confirmation.changed.connect(self.run_checks)

        # password naming (for use in status/error messages)
        self._name_of_password = _(constants.NAME_OF_PASSWORD)
        self._name_of_password_plural = _(constants.NAME_OF_PASSWORD_PLURAL)

        # signals
        self.checks_done = Signal()

    @property
    def password(self):
        """Main password field."""
        return self._password

    @property
    def password_confirmation(self):
        """Password confirmation field."""
        return self._password_confirmation

    @property
    def checks(self):
        return self._checks

    @property
    def success(self):
        return self._success

    @property
    def error_message(self):
        return self._error_message

    @property
    def successful_checks(self):
        """List of successful checks during the last checking run.

        If no checks have succeeded the list will be empty.

        :returns: list of successful checks (if any)
        :rtype: list
        """
        return self._successful_checks

    @property
    def failed_checks(self):
        """List of checks failed during the last checking run.

        If no checks have failed the list will be empty.

        :returns: list of failed checks (if any)
        :rtype: list
        """
        return self._failed_checks

    @property
    def policy(self):
        return self._policy

    @property
    def username(self):
        return self._username

    @username.setter
    def username(self, new_username):
        self._username = new_username

    @property
    def fullname(self):
        """The full name of the user for which the password is being set.

        If no full name is provided, "root" will be used.

        :returns: full user name corresponding to the password
        :rtype: str or None
        """
        return self._fullname

    @fullname.setter
    def fullname(self, new_fullname):
        self._fullname = new_fullname

    # password naming
    @property
    def name_of_password(self):
        """Name of the password to be used called in warnings and error messages.

        For example:
        "%s contains non-ASCII characters"
        can be customized to:
        "Password contains non-ASCII characters"
        or
        "Passphrase contains non-ASCII characters"

        :returns: name of the password being checked
        :rtype: str
        """
        return self._name_of_password

    @name_of_password.setter
    def name_of_password(self, name):
        self._name_of_password = name

    @property
    def name_of_password_plural(self):
        """Plural name of the password to be used called in warnings and error messages.

        :returns: plural name of the password being checked
        :rtype: str
        """
        return self._name_of_password_plural

    @name_of_password_plural.setter
    def name_of_password_plural(self, name_plural):
        self._name_of_password_plural = name_plural

    def add_check(self, check_instance):
        """Add check instance to list of checks."""
        self._checks.append(check_instance)

    def run_checks(self):
        # first we need to prepare a check request instance
        check_request = PasswordCheckRequest()
        check_request.password = self.password.content
        check_request.password_confirmation = self.password_confirmation.content
        check_request.policy = self.policy
        check_request.username = self.username
        check_request.fullname = self.fullname
        check_request.name_of_password = self.name_of_password
        check_request.name_of_password_plural = self.name_of_password_plural

        # reset the list of failed checks
        self._failed_checks = []

        error_message = ""
        for check in self.checks:
            if not check.skip:
                check.run(check_request)
                if check.result.success:
                    self._successful_checks.append(check)
                else:
                    self._failed_checks.append(check)

                if not check.result.success and not self.failed_checks:
                    # a check failed:
                    # - remember that & it's error message
                    # - run other checks as well and ignore their error messages (if any)
                    # - fail the overall check run (success = False)
                    error_message = check.result.error_message
        if self.failed_checks:
            self._error_message = error_message
            self._success = False
        else:
            self._success = True
            self._error_message = ""
        # trigger the success changed signal
        self.checks_done.emit(self._error_message)
Esempio n. 20
0
 def method_test(self):
     """Test if a method can be correctly connected to a signal."""
     signal = Signal()
     foo = FooClass()
     self.assertIsNone(foo.var)
     # connect the signal
     signal.connect(foo.set_var)
     # trigger the signal
     signal.emit("bar")
     # check if the callback triggered correctly
     self.assertEqual(foo.var, "bar")
     # try to trigger the signal again
     signal.emit("baz")
     self.assertEqual(foo.var, "baz")
     # now try to disconnect the signal
     signal.disconnect(foo.set_var)
     # check that calling the signal again
     # no longer triggers the callback
     signal.emit("anaconda")
     self.assertEqual(foo.var, "baz")
Esempio n. 21
0
    def __init__(self):
        """ Create installation manager."""
        self._tasks = set()
        self._actual_task = None
        self._step_sum = 0
        self._tasks_done_step = 0
        self._installation_terminated = False
        self._modules = []

        self._install_started_signal = Signal()
        self._install_stopped_signal = Signal()
        self._task_changed_signal = Signal()
        self._progress_changed_signal = Signal()
        self._progress_changed_float_signal = Signal()
        self._error_raised_signal = Signal()

        self._subscriptions = []
Esempio n. 22
0
class InstallManager(object):
    """Manager to control module installation.

    Installation tasks will be collected from modules and run one by one.

    Provides summarized API (InstallationInterface class) for UI.
    """

    def __init__(self):
        """ Create installation manager."""
        self._tasks = set()
        self._actual_task = None
        self._step_sum = 0
        self._tasks_done_step = 0
        self._installation_terminated = False
        self._modules = []

        self._install_started_signal = Signal()
        self._install_stopped_signal = Signal()
        self._task_changed_signal = Signal()
        self._progress_changed_signal = Signal()
        self._progress_changed_float_signal = Signal()
        self._error_raised_signal = Signal()

        self._subscriptions = []

    @property
    def installation_started(self):
        return self._install_started_signal

    @property
    def installation_stopped(self):
        return self._install_stopped_signal

    @property
    def task_changed_signal(self):
        """Signal when installation task changed."""
        return self._task_changed_signal

    @property
    def progress_changed_signal(self):
        """Signal when progress changed."""
        return self._progress_changed_signal

    @property
    def progress_changed_float_signal(self):
        """Signal when progress in float changed."""
        return self._progress_changed_float_signal

    @property
    def error_raised_signal(self):
        """Signal which will be emitted when error raised during installation."""
        return self._error_raised_signal

    @property
    def available_modules(self):
        """Get available modules which will be used for installation."""
        return self._modules

    @available_modules.setter
    def available_modules(self, modules):
        """Set available modules which will be used for installation.

        :param modules: Modules list.
        :type modules: list
        """
        self._modules = modules

    def start_installation(self):
        """Start the installation."""
        self._collect_tasks()

        self._sum_steps_count()
        self._disconnect_task()
        self._tasks_done_step = 0
        self._installation_terminated = False

        if self._tasks:
            self._actual_task = self._tasks.pop()
            self._install_started_signal.emit()
            self._run_task()

    def _collect_tasks(self):
        self._tasks.clear()

        if not self._modules:
            log.error("Starting installation without available modules.")

        for module_service in self._modules:
            # FIXME: This is just a temporary solution.
            module_object = DBus.get_proxy(module_service, auto_object_path(module_service))

            tasks = module_object.AvailableTasks()
            for task in tasks:
                log.debug("Getting task %s from module %s", task[TASK_NAME], module_service)
                task_proxy = DBus.get_proxy(module_service, task[TASK_PATH])
                self._tasks.add(task_proxy)

    def _sum_steps_count(self):
        self._step_sum = 0
        for task in self._tasks:
            self._step_sum += task.ProgressStepsCount

    def _run_task(self):
        if self._installation_terminated:
            log.debug("Don't run another task. The installation was terminated.")
            return

        task_name = self._actual_task.Name

        log.debug("Running installation task %s", task_name)
        self._disconnect_task()
        self._connect_task()
        self._task_changed_signal.emit(task_name)
        self._actual_task.Start()

    def _connect_task(self):
        s = self._actual_task.ProgressChanged.connect(self._progress_changed)
        self._subscriptions.append(s)

        s = self._actual_task.Started.connect(self._task_started())
        self._subscriptions.append(s)

        s = self._actual_task.Stopped.connect(self._task_stopped)
        self._subscriptions.append(s)

        s = self._actual_task.ErrorRaised.connect(self._task_error_raised)
        self._subscriptions.append(s)

    def _disconnect_task(self):
        for subscription in self._subscriptions:
            subscription.disconnect()

    def _test_if_running(self, log_msg=None):
        if self._actual_task is not None:
            return True
        else:
            log.warning(log_msg)
            return False

    def _task_stopped(self):
        self._tasks_done_step += self._actual_task.ProgressStepsCount
        if self._tasks:
            self._actual_task = self._tasks.pop()
            self._run_task()
        else:
            log.info("Installation finished.")
            self._actual_task = None
            self._install_stopped_signal.emit()

    def _task_started(self):
        log.info("Installation task %s has started.", self._actual_task)

    def _task_error_raised(self, error_description):
        self._error_raised_signal.emit(error_description)

    @property
    def installation_running(self):
        """Installation is running right now.

        :returns: True if installation is running. False otherwise.
        """
        return self._actual_task is not None

    @property
    def task_name(self):
        """Get name of the running task."""
        if self._test_if_running("Can't get task name when installation is not running."):
            return self._actual_task.Name
        else:
            return ""

    @property
    def task_description(self):
        """Get description of the running task."""
        if self._test_if_running("Can't get task description when installation is not running."):
            return self._actual_task.Description
        else:
            return ""

    @property
    def progress_steps_count(self):
        """Sum of steps in all tasks used for installation."""
        if self._test_if_running("Can't get sum of all tasks when installation is not running."):
            return self._step_sum
        else:
            return 0

    def _progress_changed(self, step, msg):
        actual_progress = step + self._tasks_done_step
        self._progress_changed_signal.emit(actual_progress, msg)
        self._progress_changed_float_signal.emit(actual_progress / self._step_sum, msg)

    @property
    def progress(self):
        """Get progress of the installation.

        :returns: (step: int, msg: str) tuple.
                  step - step in the installation process.
                  msg - short description of the step
        """
        if self._test_if_running("Can't get task progress when installation is not running."):
            (step, msg) = self._actual_task.Progress
            actual_progress = step + self._tasks_done_step

            return actual_progress, msg
        else:
            return 0, ""

    @property
    def progress_float(self):
        """Get progress of the installation as float number from 0.0 to 1.0.

        :returns: (step: float, msg: str) tuple.
                  step - step in the installation process.
                  msg - short description of the step
        """
        if self._test_if_running("Can't get task progress in float "
                                 "when installation is not running."):
            (step, msg) = self._actual_task.Progress
            actual_progress = step + self._tasks_done_step
            actual_progress = actual_progress / self._step_sum

            return actual_progress, msg
        else:
            return 0, ""

    def cancel(self):
        """Cancel installation.

        Installation will be canceled as soon as possible. When exactly depends on the actual task
        running.
        """
        if self._test_if_running():

            self._installation_terminated = True

            if self._actual_task.IsCancelable:
                self._actual_task.Cancel()
        else:
            raise InstallationNotRunning("Can't cancel task when installation is not running.")
Esempio n. 23
0
class TaskQueue(BaseTask):
    """TaskQueue represents a queue of TaskQueues or Tasks.

    TaskQueues and Tasks can be mixed in a single TaskQueue.
    """

    def __init__(self, name, status_message=None):
        super(TaskQueue, self).__init__(name=name)
        self._status_message = status_message
        self._current_task_number = None
        self._current_queue_number = None
        # the list backing this TaskQueue instance
        self._list = []
        # triggered if a TaskQueue contained in this one was started/completed
        self.queue_started = Signal()
        self.queue_completed = Signal()
        # triggered when a task is started
        self.task_started = Signal()
        self.task_completed = Signal()

        # connect to the task & queue started signals for
        # progress reporting purposes
        self.queue_started.connect(self._queue_started_cb)
        self.task_started.connect(self._task_started_cb)

    @synchronized
    def _queue_started_cb(self, *args):
        self._current_queue_number += 1

    @synchronized
    def _task_started_cb(self, *args):
        self._current_task_number += 1

    @property
    def status_message(self):
        """A status message describing the Queue is trying to achieve.

        Eq. "Converting all foo into bar."

        The current main usecase is to set the ProgressHub status message when
        a TaskQueue is started.

        :returns: a status message
        :rtype: str
        """
        return self._status_message

    @property
    @synchronized
    def queue_count(self):
        """Returns number of TaskQueues contained in this and all nested TaskQueues.

        :returns: number of queues
        :rtype: int
        """
        queue_count = 0
        for item in self:
            # count only queues
            if isinstance(item, TaskQueue):
                # count the queue itself
                queue_count += 1
                # and its contents
                queue_count += item.queue_count
        return queue_count

    @property
    @synchronized
    def task_count(self):
        """Returns number of tasks contained in this and all nested TaskQueues.

        :returns: number of tasks
        :rtype: int
        """
        task_count = 0
        for item in self:
            if isinstance(item, Task):
                # count tasks
                task_count += 1
            elif isinstance(item, TaskQueue):
                # count tasks in nested queues
                task_count += item.task_count
        return task_count

    @property
    @synchronized
    def current_task_number(self):
        """Number of the currently running task (if any).

        :returns: number of the currently running task (if any)
        :rtype: int or None if no task is currently running
        """
        return self._current_task_number

    @property
    @synchronized
    def current_queue_number(self):
        """Number of the currently running task queue (if any).

        :returns: number of the currently running task queue (if any)
        :rtype: int or None if no task queue is currently running
        """
        return self._current_queue_number

    @property
    @synchronized
    def progress(self):
        """Task queue processing progress.

        The progress is reported as a floating point number from 0.0 to 1.0.
        :returns: task queue processing progress
        :rtype: float
        """
        if self.current_task_number:
            return self.task_count / self.current_task_number
        else:
            return 0.0

    @property
    @synchronized
    def summary(self):
        """Return a multi-line summary of the contents of the task queue.

        :returns: summary of task queue contents
        :rtype: str
        """
        if self.parent is None:
            message = "Top-level task queue: %s\n" % self.name
            # this is the top-level queue, so add some "global" stats
            message += "Number of task queues: %d\n" % self.queue_count
            message += "Number of tasks: %d\n" % self.task_count
            message += "Task & task group listing:\n"
        else:
            message = "Task queue: %s\n" % self.name
        for item in self:
            for line in item.summary.splitlines():
                message += " %s\n" % line
        # remove trailing newlines from the top level message
        if self.parent is None and message[-1] == "\n":
            message = message.rstrip("\n")
        return message

    @property
    @synchronized
    def parent(self):
        """The parent task queue of this task queue (if any).

        :returns: parent of this task queue (if any)
        :rtype: TaskQueue instance or None
        """
        return self._parent

    @parent.setter
    @synchronized
    def parent(self, parent_item):
        # check if a parent is already set
        if self._parent is not None:
            # disconnect from the previous parent first
            self.started.disconnect(self._parent.queue_started.emit)
            self.completed.disconnect(self._parent.queue_completed.emit)
            self.queue_started.disconnect(self._parent.queue_started.emit)
            self.queue_completed.disconnect(self._parent.queue_completed.emit)
            self.task_started.disconnect(self._parent.task_started.emit)
            self.task_completed.disconnect(self._parent.task_completed.emit)
        # set the parent
        self._parent = parent_item
        # Connect own signals "up" to the parent,
        # so that it is possible to monitor how all nested TaskQueues and Tasks
        # are running from the top-level element.

        # connect own start/completion signal to parents queue start/completion signal
        self.started.connect(self._parent.queue_started.emit)
        self.completed.connect(self._parent.queue_completed.emit)

        # propagate start/completion signals from nested queues/tasks
        self.queue_started.connect(self._parent.queue_started.emit)
        self.queue_completed.connect(self._parent.queue_completed.emit)
        self.task_started.connect(self._parent.task_started.emit)
        self.task_completed.connect(self._parent.task_completed.emit)

    def start(self):
        """Start processing of the task queue."""
        do_start = False
        with self._lock:
            # the task queue can only be started once
            if self.running or self.done:
                if self.running:
                    # attempt to start a task that is already running
                    log.error("Can't start task queue %s - already running.")
                else:
                    # attempt to start a task that an already finished task
                    log.error("Can't start task queue %s - already done.")
            else:
                do_start = True
                self._running = True
                if self.task_count:
                    # only set the initial task number if we have some tasks
                    self._current_task_number = 0
                    self._current_queue_number = 0
                else:
                    log.warning("Attempting to start an empty task queue (%s).", self.name)

        if do_start:
            # go over all task groups and their tasks in order
            self.started.emit(self)
            if len(self) == 0:
                log.warning("The task group %s is empty.", self.name)
            for item in self:
                # start the item (TaskQueue/Task)
                item.start()

            # we are done, set the task queue state accordingly
            with self._lock:
                self._running = False
                self._done = True
                # also set the current task variables accordingly as we no longer process a task
                self._current_task_number = None
                self._current_queue_number = None

            # trigger the "completed" signals
            self.completed.emit(self)

    # implement the Python list "interface" and make sure parent is always
    # set to a correct value
    @synchronized
    def append(self, item):
        item.parent = self
        self._list.append(item)

    @synchronized
    def insert(self, index, item):
        item.parent = self
        self._list.insert(index, item)

    @synchronized
    def __setitem__(self, index, item):
        item.parent = self
        return self._list.__setitem__(index, item)

    @synchronized
    def __len__(self):
        return self._list.__len__()

    @synchronized
    def count(self):
        return self._list.count()

    @synchronized
    def __getitem__(self, ii):
        return self._list[ii]

    @synchronized
    def __delitem__(self, index):
        self._list[index].parent = None
        del self._list[index]

    @synchronized
    def pop(self):
        item = self._list.pop()
        item.parent = None
        return item

    @synchronized
    def clear(self):
        for item in self._list:
            item.parent = None
        self._list.clear()
Esempio n. 24
0
 def signal_chain_test(self):
     """Check if signals can be chained together."""
     foo = FooClass()
     self.assertIsNone(foo.var)
     signal1 = Signal()
     signal1.connect(foo.set_var)
     signal2 = Signal()
     signal2.connect(signal1.emit)
     signal3 = Signal()
     signal3.connect(signal2.emit)
     # trigger the chain
     signal3.emit("bar")
     # check if the initial callback was triggered
     self.assertEqual(foo.var, "bar")
Esempio n. 25
0
class Spoke(object, metaclass=ABCMeta):
    """A Spoke is a single configuration screen.  There are several different
       places where a Spoke can be displayed, each of which will have its own
       unique class.  A Spoke is typically used when an element in the Hub is
       selected but can also be displayed before a Hub or between multiple
       Hubs.

       What amount of the UI layout a Spoke provides depends upon where it is
       to be shown.  Regardless, the UI of a Spoke should be given by an
       interface description file like glade as often as possible, though this
       is not a strict requirement.

       Class attributes:

       category   -- Under which SpokeCategory shall this Spoke be displayed
                     in the Hub?  This is a reference to a Hub subclass (not an
                     object, but the class itself).  If no category is given,
                     this Spoke will not be displayed.  Note that category is
                     not required for any Spokes appearing before or after a
                     Hub.
       icon       -- The name of the icon to be displayed in the SpokeSelector
                     widget corresponding to this Spoke instance.  If no icon
                     is given, the default from SpokeSelector will be used.
       title      -- The title to be displayed in the SpokeSelector widget
                     corresponding to this Spoke instance.  If no title is
                     given, the default from SpokeSelector will be used.
    """

    category = None
    icon = None
    title = None

    def __init__(self, storage, payload, instclass):
        """Create a new Spoke instance.

           The arguments this base class accepts defines the API that spokes
           have to work with.  A Spoke does not get free reign over everything
           in the anaconda class, as that would be a big mess.  Instead, a
           Spoke may count on the following:

           data         -- An instance of a pykickstart Handler object.  The
                           Spoke uses this to populate its UI with defaults
                           and to pass results back after it has run. The data
                           property must be implemented by classes inherting
                           from Spoke.
           storage      -- An instance of storage.Storage.  This is useful for
                           determining what storage devices are present and how
                           they are configured.
           payload      -- An instance of a packaging.Payload subclass.  This
                           is useful for displaying and selecting packages to
                           install, and in carrying out the actual installation.
           instclass    -- An instance of a BaseInstallClass subclass.  This
                           is useful for determining distribution-specific
                           installation information like default package
                           selections and default partitioning.
        """
        self._storage = storage
        self.payload = payload
        self.instclass = instclass
        self.applyOnSkip = False

        self.visitedSinceApplied = True

        # entry and exit signals
        # - get the hub instance as a single argument
        self.entered = Signal()
        self.exited = Signal()

        # connect default callbacks for the signals
        self.entered.connect(self.entry_logger)
        self.entered.connect(self._mark_screen_visited)
        self.exited.connect(self.exit_logger)

    @abstractproperty
    def data(self):
        pass

    @property
    def storage(self):
        return self._storage

    @classmethod
    def should_run(cls, environment, data):
        """This method is responsible for beginning Spoke initialization.

           It should return True if the spoke is to be shown while in
           <environment> and False if it should be skipped.

           It might be called multiple times, with or without (None)
           the data argument.
        """
        return environment == ANACONDA_ENVIRON

    def apply(self):
        """Apply the selections made on this Spoke to the object's preset
           data object.  This method must be provided by every subclass.
        """
        raise NotImplementedError

    @property
    def changed(self):
        """Have the values on the spoke changed since the last time it was
           run?  If not, the apply and execute methods will be skipped.  This
           is to avoid the spoke doing potentially long-lived and destructive
           actions that are completely unnecessary.
        """
        return True

    @property
    def configured(self):
        """This method returns a list of textual ids that should
           be written into the after-install customization status
           file for the firstboot and GIE to know that the spoke was
           configured and what value groups were provided."""
        return ["%s.%s" % (self.__class__.__module__, self.__class__.__name__)]

    @property
    def completed(self):
        """Has this spoke been visited and completed?  If not and the spoke is
           mandatory, a special warning icon will be shown on the Hub beside the
           spoke, and a highlighted message will be shown at the bottom of the
           Hub.  Installation will not be allowed to proceed until all mandatory
           spokes are complete.

           WARNING: This can be called before the spoke is finished initializing
           if the spoke starts a thread. It should make sure it doesn't access
           things until they are completely setup.
        """
        return False

    @property
    def sensitive(self):
        """May the user click on this spoke's selector and be taken to the spoke?
           This is different from the showable property.  A spoke that is not
           sensitive will still be shown on the hub, but the user may not enter it.
           This is also different from the ready property.  A spoke that is not
           ready may not be entered, but the spoke may become ready in the future.
           A spoke that is not sensitive will likely not become so.

           Most spokes will not want to override this method.
        """
        return True

    @property
    def mandatory(self):
        """Mark this spoke as mandatory. Installation will not be allowed
           to proceed until all mandatory spokes are complete.

           Spokes are mandatory unless marked as not being so.
        """
        return True

    def execute(self):
        """Cause the data object to take effect on the target system.  This will
           usually be as simple as calling one or more of the execute methods on
           the data object.  This method does not need to be provided by all
           subclasses.

           This method will be called in two different places:  (1) Immediately
           after initialize on kickstart installs.  (2) Immediately after apply
           in all cases.
        """
        pass

    @property
    def status(self):
        """Given the current status of whatever this Spoke configures, return
           a very brief string.  The purpose of this is to display something
           on the Hub under the Spoke's title so the user can tell at a glance
           how things are configured.

           A spoke's status line on the Hub can also be overloaded to provide
           information about why a Spoke is not yet ready, or if an error has
           occurred when setting it up.  This can be done by calling
           send_message from pyanaconda.ui.communication with the target
           Spoke's class name and the message to be displayed.

           If the Spoke was not yet ready when send_message was called, the
           message will be overwritten with the value of this status property
           when the Spoke becomes ready.
        """
        raise NotImplementedError


    def _mark_screen_visited(self, spoke_instance):
        """Report the spoke screen as visited to the Spoke Access Manager."""
        screen_access.sam.mark_screen_visited(spoke_instance.__class__.__name__)

    def entry_logger(self, spoke_instance):
        """Log immediately before this spoke is about to be displayed on the
           screen.  Subclasses may override this method if they want to log
           more specific information, but an overridden method should finish
           by calling this method so the entry will be logged.
        """
        log.debug("Entered spoke: %s", spoke_instance)

    def exit_logger(self, spoke_instance):
        """Log when a user leaves the spoke.  Subclasses may override this
           method if they want to log more specific information, but an
           overridden method should finish by calling this method so the
           exit will be logged.
        """
        log.debug("Left spoke: %s", spoke_instance)

    def finished(self):
        """Called when exiting the Summary Hub

        This can be used to cleanup the spoke before continuing the
        installation. This method is optional.
        """
        pass

    # Initialization controller related code
    #
    # - initialization_controller
    # -> The controller for this spokes and all others on the given hub.
    # -> The controller has the init_done signal that can be used to trigger
    #    actions that should happen once all spokes on the given Hub have
    #    finished initialization.
    # -> If there is no Hub (standalone spoke) the property is None
    #
    # - initialize_start()
    # -> Should be called when Spoke initialization is started.
    # -> Needs to be called explicitly, if we called it for every spoke by default
    #    then any spoke that does not call initialize_done() would prevent the
    #    controller form ever triggering the init_done signal.
    #
    # - initialize_done()
    # -> Must be called by every spoke that calls initialize_start() or else the init_done
    #    signal will never be emitted.

    @property
    def initialization_controller(self):
        # standalone spokes don't have a category
        if self.category:
            return lifecycle.get_controller_by_category(category_name=self.category.__name__)
        else:
            return None

    def initialize_start(self):
        # get the correct controller for this spoke
        spoke_controller = self.initialization_controller
        # check if there actually is a controller for this spoke, there might not be one
        # if this is a standalone spoke
        if spoke_controller:
            spoke_controller.module_init_start(self)

    def initialize_done(self):
        # get the correct controller for this spoke
        spoke_controller = self.initialization_controller
        # check if there actually is a controller for this spoke, there might not be one
        # if this is a standalone spoke
        if spoke_controller:
            spoke_controller.module_init_done(self)

    def __repr__(self):
        """Return the class name as representation.

        Returning the class name should be enough the uniquely identify a spoke.
        """
        return self.__class__.__name__
Esempio n. 26
0
class DasdFormatting(object):
    """Class for formatting DASDs."""
    def __init__(self):
        self._dasds = []

        self._can_format_unformatted = True
        self._can_format_ldl = True

        self._report = Signal()
        self._report.connect(log.debug)

    @staticmethod
    def is_supported():
        """Is DASD formatting supported on this machine?"""
        return arch.is_s390()

    @property
    def report(self):
        """Signal for the progress reporting.

        Emits messages during the formatting.
        """
        return self._report

    @property
    def dasds(self):
        """List of found DASDs to format."""
        return self._dasds

    @property
    def dasds_summary(self):
        """Returns a string summary of DASDs to format."""
        return "\n".join(map(self.get_dasd_info, self.dasds))

    def get_dasd_info(self, disk):
        """Returns a string with description of a DASD."""
        return "/dev/" + disk.name + " (" + disk.busid + ")"

    def _is_dasd(self, disk):
        """Is it a DASD disk?"""
        return disk.type == "dasd"

    def _is_unformatted_dasd(self, disk):
        """Is it an unformatted DASD?"""
        return self._is_dasd(disk) and blockdev.s390.dasd_needs_format(
            disk.busid)

    def _is_ldl_dasd(self, disk):
        """Is it an LDL DASD?"""
        return self._is_dasd(disk) and blockdev.s390.dasd_is_ldl(disk.name)

    def _get_unformatted_dasds(self, disks):
        """Returns a list of unformatted DASDs."""
        result = []

        if not self._can_format_unformatted:
            log.debug("We are not allowed to format unformatted DASDs.")
            return result

        for disk in disks:
            if self._is_unformatted_dasd(disk):
                log.debug("Found unformatted DASD: %s",
                          self.get_dasd_info(disk))
                result.append(disk)

        return result

    def _get_ldl_dasds(self, disks):
        """Returns a list of LDL DASDs."""
        result = []

        if not self._can_format_ldl:
            log.debug("We are not allowed to format LDL DASDs.")
            return result

        for disk in disks:
            if self._is_ldl_dasd(disk):
                log.debug("Found LDL DASD: %s", self.get_dasd_info(disk))
                result.append(disk)

        return result

    def update_restrictions(self, data):
        """Read kickstart data to update the restrictions."""
        self._can_format_unformatted = data.zerombr.zerombr
        self._can_format_ldl = data.clearpart.cdl

    def search_disks(self, disks):
        """Search for a list of disks for DASDs to format."""
        self._dasds = list(
            set(
                self._get_unformatted_dasds(disks) +
                self._get_ldl_dasds(disks)))

    def should_run(self):
        """Should we run the formatting?"""
        return bool(self._dasds)

    def do_format(self, disk):
        """Format a disk."""
        try:
            self.report.emit(_("Formatting %s") % self.get_dasd_info(disk))
            blockdev.s390.dasd_format(disk.name)
        except blockdev.S390Error as err:
            self.report.emit(
                _("Failed formatting %s") % self.get_dasd_info(disk))
            log.error(err)

    def run(self, storage, data):
        """Format all found DASDs and update the storage.

        This method could be run in a separate thread.
        """
        # Check if we have something to format.
        if not self._dasds:
            self.report.emit(_("Nothing to format"))
            return

        # Format all found DASDs.
        self.report.emit(_("Formatting DASDs"))
        for disk in self._dasds:
            self.do_format(disk)

        # Update the storage.
        self.report.emit(_("Probing storage"))
        storage_initialize(storage, data,
                           storage.devicetree.protected_dev_names)

        # Update also the storage snapshot to reflect the changes.
        if on_disk_storage.created:
            on_disk_storage.dispose_snapshot()
        on_disk_storage.create_snapshot(storage)

    @staticmethod
    def run_automatically(storage, data, callback=None):
        """Run the DASD formatting automatically.

        This method could be run in a separate thread.
        """
        if not flags.automatedInstall:
            return

        if not DasdFormatting.is_supported():
            return

        disks = getDisks(storage.devicetree)

        formatting = DasdFormatting()
        formatting.update_restrictions(data)
        formatting.search_disks(disks)

        if not formatting.should_run():
            return

        if callback:
            formatting.report.connect(callback)

        formatting.run(storage, data)

        if callback:
            formatting.report.disconnect(callback)
Esempio n. 27
0
class Hub(object, metaclass=ABCMeta):
    """A Hub is an overview UI screen.  A Hub consists of one or more grids of
       configuration options that the user may choose from.  Each grid is
       provided by a SpokeCategory, and each option is provided by a Spoke.
       When the user dives down into a Spoke and is finished interacting with
       it, they are returned to the Hub.

       Some Spokes are required.  The user must interact with all required
       Spokes before they are allowed to proceed to the next stage of
       installation.

       From a layout perspective, a Hub is the entirety of the screen, though
       the screen itself can be roughly divided into thirds.  The top third is
       some basic navigation information (where you are, what you're
       installing).  The middle third is the grid of Spokes.  The bottom third
       is an action area providing additional buttons (quit, continue) or
       progress information (during package installation).

       Installation may consist of multiple chained Hubs, or Hubs with
       additional standalone screens either before or after them.
    """

    def __init__(self, storage, payload, instclass):
        """Create a new Hub instance.

           The arguments this base class accepts defines the API that Hubs
           have to work with.  A Hub does not get free reign over everything
           in the anaconda class, as that would be a big mess.  Instead, a
           Hub may count on the following:

           data         -- An instance of a pykickstart Handler object.  The
                           Hub uses this to populate its UI with defaults
                           and to pass results back after it has run. The data
                           property must be implemented by classes inheriting
                           from Hub.
           storage      -- An instance of storage.Storage.  This is useful for
                           determining what storage devices are present and how
                           they are configured.
           payload      -- An instance of a payload.Payload subclass.  This
                           is useful for displaying and selecting packages to
                           install, and in carrying out the actual installation.
           instclass    -- An instance of a BaseInstallClass subclass.  This
                           is useful for determining distribution-specific
                           installation information like default package
                           selections and default partitioning.
        """
        self._storage = storage
        self.payload = payload
        self.instclass = instclass

        self.paths = {}
        self._spokes = {}

        # entry and exit signals
        # - get the hub instance as a single argument
        self.entered = Signal()
        self.exited = Signal()

        # connect the default callbacks
        self.entered.connect(self.entry_logger)
        self.exited.connect(self.exit_logger)

    @abstractproperty
    def data(self):
        pass

    @property
    def storage(self):
        return self._storage

    def set_path(self, path_id, paths):
        """Update the paths attribute with list of tuples in the form (module
           name format string, directory name)"""
        self.paths[path_id] = paths

    def entry_logger(self, hub_instance):
        """Log immediately before this hub is about to be displayed on the
           screen.  Subclasses may override this method if they want to log
           more specific information, but an overridden method should finish
           by calling this method so the entry will be logged.

           Note that due to how the GUI flows, hubs are only entered once -
           when they are initially displayed.  Going to a spoke from a hub
           and then coming back to the hub does not count as exiting and
           entering.
        """
        log.debug("Entered hub: %s", hub_instance)

    def _collectCategoriesAndSpokes(self):
        """This method is provided so that is can be overridden in a subclass
           by a custom collect method.
           One example of such usage is the Initial Setup application.
        """
        return collectCategoriesAndSpokes(self.paths, self.__class__, self.data.displaymode.displayMode)

    def exit_logger(self, hub_instance):
        """Log when a user leaves the hub.  Subclasses may override this
           method if they want to log more specific information, but an
           overridden method should finish by calling this method so the
           exit will be logged.

           Note that due to how the GUI flows, hubs are not exited when the
           user selects a spoke from the hub.  They are only exited when the
           continue or quit button is clicked on the hub.
        """
        log.debug("Left hub: %s", hub_instance)

    def __repr__(self):
        """Return the class name as representation.

        Returning the class name should be enough the uniquely identify a hub.
        """
        return self.__class__.__name__
Esempio n. 28
0
class PasswordValidityCheckResult(CheckResult):
    """A wrapper for results for a password check."""
    def __init__(self):
        super().__init__()
        self._check_request = None
        self._password_score = 0
        self.password_score_changed = Signal()
        self._status_text = ""
        self.status_text_changed = Signal()
        self._password_quality = 0
        self.password_quality_changed = Signal()
        self._length_ok = False
        self.length_ok_changed = Signal()

    @property
    def check_request(self):
        """The check request used to generate this check result object.

        Can be used to get the password text and checking parameters
        for this password check result.

        :returns: the password check request that triggered this password check result
        :rtype: a PasswordCheckRequest instance
        """
        return self._check_request

    @check_request.setter
    def check_request(self, new_request):
        self._check_request = new_request

    @property
    def password_score(self):
        """A high-level integer score indicating password quality.

        Goes from 0 (invalid password) to 4 (valid & very strong password).
        Mainly used to drive the password quality indicator in the GUI.
        """
        return self._password_score

    @password_score.setter
    def password_score(self, new_score):
        self._password_score = new_score
        self.password_score_changed.emit(new_score)

    @property
    def status_text(self):
        """A short overall status message describing the password.

        Generally something like "Good.", "Too short.", "Empty.", etc.

        :rtype: short status message
        :rtype: str
        """
        return self._status_text

    @status_text.setter
    def status_text(self, new_status_text):
        self._status_text = new_status_text
        self.status_text_changed.emit(new_status_text)

    @property
    def password_quality(self):
        """More fine grained integer indicator describing password strength.

        This basically exports the quality score assigned by libpwquality to the password,
        which goes from 0 (unacceptable password) to 100 (strong password).

        Note of caution though about using the password quality value - it is intended
        mainly for on-line password strength hints, not for long-term stability,
        even just because password dictionary updates and other peculiarities of password
        strength judging.

        :returns: password quality value as reported by libpwquality
        :rtype: int
        """
        return self._password_quality

    @password_quality.setter
    def password_quality(self, value):
        self._password_quality = value
        self.password_quality_changed.emit(value)

    @property
    def length_ok(self):
        """Reports if the password is long enough.

        :returns: if the password is long enough
        :rtype: bool
        """
        return self._length_ok

    @length_ok.setter
    def length_ok(self, value):
        self._length_ok = value
        self.length_ok_changed.emit(value)
Esempio n. 29
0
    def function_test(self):
        """Test if a local function can be correctly connected to a signal."""

        # create a local function
        def set_var(value):
            self.var = value

        signal = Signal()
        self.assertIsNone(self.var)
        # connect the signal
        signal.connect(set_var)
        # trigger the signal
        signal.emit("bar")
        # check if the callback triggered correctly
        self.assertEqual(self.var, "bar")
        # try to trigger the signal again
        signal.emit("baz")
        self.assertEqual(self.var, "baz")
        # now try to disconnect the signal
        signal.disconnect(set_var)
        # check that calling the signal again
        # no longer triggers the callback
        signal.emit("anaconda")
        self.assertEqual(self.var, "baz")
Esempio n. 30
0
class Controller(object):
    """A singleton that track initialization of Anaconda modules."""
    def __init__(self):
        self._lock = RLock()
        self._modules = set()
        self._all_modules_added = False
        self.init_done = Signal()
        self._init_done_triggered = False
        self._added_module_count = 0

    @synchronized
    def module_init_start(self, module):
        """Tell the controller that a module has started initialization.

        :param module: a module which has started initialization
        """
        if self._all_modules_added:
            log.warning("Late module_init_start() from: %s", self)
        elif module in self._modules:
            log.warning("Module already marked as initializing: %s", module)
        else:
            self._added_module_count += 1
            self._modules.add(module)

    def all_modules_added(self):
        """Tell the controller that all expected modules have started initialization.

        Tell the controller that all expected modules have been registered
        for initialization tracking (or have already been initialized)
        and no more are expected to be added.

        This is needed so that we don't prematurely trigger the init_done signal
        when all known modules finish initialization while other modules have not
        yet been added.
        """
        init_done = False
        with self._lock:
            log.info("Initialization of all modules (%d) has been started.", self._added_module_count)
            self._all_modules_added = True

            # if all modules finished initialization before this was added then
            # trigger the init_done signal at once
            if not self._modules and not self._init_done_triggered:
                self._init_done_triggered = True
                init_done = True

        # we should emit the signal out of the main lock as it doesn't make sense
        # to hold the controller-state lock once we decide to the trigger init_done signal
        # (and any callbacks registered on it)
        if init_done:
            self._trigger_init_done()

    def module_init_done(self, module):
        """Tell the controller that a module has finished initialization.

        And if no more modules are being initialized trigger the init_done signal.

        :param module: a module that has finished initialization
        """
        init_done = False
        with self._lock:
            # prevent the init_done signal from
            # being triggered more than once
            if self._init_done_triggered:
                log.warning("Late module_init_done from module %s.", module)
            else:
                if module in self._modules:
                    log.info("Module initialized: %s", module)
                    self._modules.discard(module)
                else:
                    log.warning("Unknown module reported as initialized: %s", module)
                # don't trigger the signal if all modules have not yet been added
                if self._all_modules_added and not self._modules:
                    init_done = True
                    self._init_done_triggered = True

        # we should emit the signal out of the main lock as it doesn't make sense
        # to hold the controller-state lock once we decide to the trigger init_done signal
        # (and any callbacks registered on it)
        if init_done:
            self._trigger_init_done()

    def _trigger_init_done(self):
        log.info("All modules have been initialized.")
        self.init_done.emit()
Esempio n. 31
0
 def __init__(self, initial_content):
     self._initial_content = initial_content
     self._content = initial_content
     self.changed = Signal()
     self._initial_change_signal_fired = False
     self.changed_from_initial_state = Signal()
Esempio n. 32
0
class Task(ABC):
    """Base class implementing DBus Task interface."""
    def __init__(self, dbus_modul_path):
        super().__init__()
        self._progress = (0, "")

        self.__cancel_lock = Lock()
        self.__cancel = False

        self._started_signal = Signal()
        self._stopped_signal = Signal()
        self._progress_changed_signal = Signal()
        self._error_raised_signal = Signal()

        self._thread_name = "{}-{}".format(THREAD_DBUS_TASK, self.name)

    @property
    def started_signal(self):
        """Signal emitted when this tasks starts."""
        return self._started_signal

    @property
    def stopped_signal(self):
        """Signal emitted when this task stops."""
        return self._stopped_signal

    @property
    def progress_changed_signal(self):
        """Signal emits when the progress of this task will change."""
        return self._progress_changed_signal

    @property
    def error_raised_signal(self):
        """Signal emits if error is raised during installation."""
        return self._error_raised_signal

    @property
    @abstractmethod
    def name(self):
        """Name of this task."""
        pass

    @property
    @abstractmethod
    def description(self):
        """Short description of this task."""
        pass

    @property
    @abstractmethod
    def progress_steps_count(self):
        """Number of the steps in this task."""
        pass

    @property
    def progress(self):
        """Actual progress of this task.

        :returns: tuple (step, description).
        """
        return self._progress

    @property
    def is_running(self):
        """Is this task running."""
        return threadMgr.exists(self._thread_name)

    @property
    def is_cancelable(self):
        """Can this task be cancelled?

        :returns: bool.
        """
        return False

    @async_action_nowait
    def progress_changed(self, step, message):
        """Update actual progress.

        Thread safe method. Can be used from the self.run_task() method.

        Signal change of the progress and update Progress DBus property.

        :param step: Number of the actual step.
        :type step: int

        :param message: Short description of the actual step.
        :type message: str
        """
        self._progress = (step, message)
        self._progress_changed_signal.emit(step, message)

    @async_action_nowait
    def error_raised(self, error_message):
        self._error_raised_signal.emit(error_message)

    @async_action_nowait
    def running_changed(self):
        """Notify about change when this task stops/starts."""
        if self.is_running:
            self._started_signal.emit()
        else:
            self._stopped_signal.emit()

    def cancel(self):
        """Cancel this task.

        This will do something only if the IsCancelable property will return `True`.
        """
        with self.__cancel_lock:
            self.__cancel = True

    def check_cancel(self, clear=True):
        """Check if Task should be canceled and clear the cancel flag.

        :param clear: Clear the flag.
        :returns: bool
        """
        with self.__cancel_lock:
            if self.__cancel:
                if clear:
                    self.__cancel = False
                return True

        return False

    def run(self):
        """Run Task job.

        Overriding of the self.run_task() method instead is recommended.

        This method will create thread which will run the self.run_task() method.
        """
        thread = AnacondaThread(name=self._thread_name, target=self.runnable)

        if not threadMgr.exists(self._thread_name):
            threadMgr.add(thread)
            self.running_changed()
            threadMgr.call_when_thread_terminates(self._thread_name,
                                                  self.running_changed)
        else:
            raise TaskAlreadyRunningException(
                "Task {} is already running".format(self.name))

    @abstractmethod
    def runnable(self):
        """Tasks job implementation.

        This will run in separate thread by calling the self.run() method.

        To report progress change use the self.progress_changed() method.
        To report fatal error use the self.error_raised() method.
        If this Task can be cancelled check the self.check_cancel() method.
        """
        pass
Esempio n. 33
0
    def clear_test(self):
        """Test if the clear() method correctly clears any connected callbacks."""
        def set_var(value):
            self.var = value

        signal = Signal()
        foo = FooClass()
        lambda_foo = FooClass()
        self.assertIsNone(foo.var)
        self.assertIsNone(lambda_foo.var)
        self.assertIsNone(self.var)
        # connect the callbacks
        signal.connect(set_var)
        signal.connect(foo.set_var)
        # pylint: disable=unnecessary-lambda
        signal.connect(lambda x: lambda_foo.set_var(x))
        # trigger the signal
        signal.emit("bar")
        # check that the callbacks were triggered
        self.assertEqual(self.var, "bar")
        self.assertEqual(foo.var, "bar")
        self.assertEqual(lambda_foo.var, "bar")
        # clear the callbacks
        signal.clear()
        # trigger the signal again
        signal.emit("anaconda")
        # check that the callbacks were not triggered
        self.assertEqual(self.var, "bar")
        self.assertEqual(foo.var, "bar")
        self.assertEqual(lambda_foo.var, "bar")
Esempio n. 34
0
class Spoke(object, metaclass=ABCMeta):
    """A Spoke is a single configuration screen.  There are several different
       places where a Spoke can be displayed, each of which will have its own
       unique class.  A Spoke is typically used when an element in the Hub is
       selected but can also be displayed before a Hub or between multiple
       Hubs.

       What amount of the UI layout a Spoke provides depends upon where it is
       to be shown.  Regardless, the UI of a Spoke should be given by an
       interface description file like glade as often as possible, though this
       is not a strict requirement.

       Class attributes:

       category   -- Under which SpokeCategory shall this Spoke be displayed
                     in the Hub?  This is a reference to a Hub subclass (not an
                     object, but the class itself).  If no category is given,
                     this Spoke will not be displayed.  Note that category is
                     not required for any Spokes appearing before or after a
                     Hub.
       icon       -- The name of the icon to be displayed in the SpokeSelector
                     widget corresponding to this Spoke instance.  If no icon
                     is given, the default from SpokeSelector will be used.
       title      -- The title to be displayed in the SpokeSelector widget
                     corresponding to this Spoke instance.  If no title is
                     given, the default from SpokeSelector will be used.
    """

    category = None
    icon = None
    title = None

    def __init__(self, storage, payload, instclass):
        """Create a new Spoke instance.

           The arguments this base class accepts defines the API that spokes
           have to work with.  A Spoke does not get free reign over everything
           in the anaconda class, as that would be a big mess.  Instead, a
           Spoke may count on the following:

           data         -- An instance of a pykickstart Handler object.  The
                           Spoke uses this to populate its UI with defaults
                           and to pass results back after it has run. The data
                           property must be implemented by classes inherting
                           from Spoke.
           storage      -- An instance of storage.Storage.  This is useful for
                           determining what storage devices are present and how
                           they are configured.
           payload      -- An instance of a payload.Payload subclass.  This
                           is useful for displaying and selecting packages to
                           install, and in carrying out the actual installation.
           instclass    -- An instance of a BaseInstallClass subclass.  This
                           is useful for determining distribution-specific
                           installation information like default package
                           selections and default partitioning.
        """
        self._storage = storage
        self.payload = payload
        self.instclass = instclass
        self.applyOnSkip = False

        self.visitedSinceApplied = True

        # entry and exit signals
        # - get the hub instance as a single argument
        self.entered = Signal()
        self.exited = Signal()

        # connect default callbacks for the signals
        self.entered.connect(self.entry_logger)
        self.entered.connect(self._mark_screen_visited)
        self.exited.connect(self.exit_logger)

    @abstractproperty
    def data(self):
        pass

    @property
    def storage(self):
        return self._storage

    @classmethod
    def should_run(cls, environment, data):
        """This method is responsible for beginning Spoke initialization.

           It should return True if the spoke is to be shown while in
           <environment> and False if it should be skipped.

           It might be called multiple times, with or without (None)
           the data argument.
        """
        return environment == ANACONDA_ENVIRON

    def apply(self):
        """Apply the selections made on this Spoke to the object's preset
           data object.  This method must be provided by every subclass.
        """
        raise NotImplementedError

    @property
    def changed(self):
        """Have the values on the spoke changed since the last time it was
           run?  If not, the apply and execute methods will be skipped.  This
           is to avoid the spoke doing potentially long-lived and destructive
           actions that are completely unnecessary.
        """
        return True

    @property
    def configured(self):
        """This method returns a list of textual ids that should
           be written into the after-install customization status
           file for the firstboot and GIE to know that the spoke was
           configured and what value groups were provided."""
        return ["%s.%s" % (self.__class__.__module__, self.__class__.__name__)]

    @property
    def completed(self):
        """Has this spoke been visited and completed?  If not and the spoke is
           mandatory, a special warning icon will be shown on the Hub beside the
           spoke, and a highlighted message will be shown at the bottom of the
           Hub.  Installation will not be allowed to proceed until all mandatory
           spokes are complete.

           WARNING: This can be called before the spoke is finished initializing
           if the spoke starts a thread. It should make sure it doesn't access
           things until they are completely setup.
        """
        return False

    @property
    def sensitive(self):
        """May the user click on this spoke's selector and be taken to the spoke?
           This is different from the showable property.  A spoke that is not
           sensitive will still be shown on the hub, but the user may not enter it.
           This is also different from the ready property.  A spoke that is not
           ready may not be entered, but the spoke may become ready in the future.
           A spoke that is not sensitive will likely not become so.

           Most spokes will not want to override this method.
        """
        return True

    @property
    def mandatory(self):
        """Mark this spoke as mandatory. Installation will not be allowed
           to proceed until all mandatory spokes are complete.

           Spokes are mandatory unless marked as not being so.
        """
        return True

    def execute(self):
        """Cause the data object to take effect on the target system.  This will
           usually be as simple as calling one or more of the execute methods on
           the data object.  This method does not need to be provided by all
           subclasses.

           This method will be called in two different places:  (1) Immediately
           after initialize on kickstart installs.  (2) Immediately after apply
           in all cases.
        """
        pass

    @property
    def status(self):
        """Given the current status of whatever this Spoke configures, return
           a very brief string.  The purpose of this is to display something
           on the Hub under the Spoke's title so the user can tell at a glance
           how things are configured.

           A spoke's status line on the Hub can also be overloaded to provide
           information about why a Spoke is not yet ready, or if an error has
           occurred when setting it up.  This can be done by calling
           send_message from pyanaconda.ui.communication with the target
           Spoke's class name and the message to be displayed.

           If the Spoke was not yet ready when send_message was called, the
           message will be overwritten with the value of this status property
           when the Spoke becomes ready.
        """
        raise NotImplementedError


    def _mark_screen_visited(self, spoke_instance):
        """Report the spoke screen as visited to the Spoke Access Manager."""
        screen_access.sam.mark_screen_visited(spoke_instance.__class__.__name__)

    def entry_logger(self, spoke_instance):
        """Log immediately before this spoke is about to be displayed on the
           screen.  Subclasses may override this method if they want to log
           more specific information, but an overridden method should finish
           by calling this method so the entry will be logged.
        """
        log.debug("Entered spoke: %s", spoke_instance)

    def exit_logger(self, spoke_instance):
        """Log when a user leaves the spoke.  Subclasses may override this
           method if they want to log more specific information, but an
           overridden method should finish by calling this method so the
           exit will be logged.
        """
        log.debug("Left spoke: %s", spoke_instance)

    def finished(self):
        """Called when exiting the Summary Hub

        This can be used to cleanup the spoke before continuing the
        installation. This method is optional.
        """
        pass

    # Initialization controller related code
    #
    # - initialization_controller
    # -> The controller for this spokes and all others on the given hub.
    # -> The controller has the init_done signal that can be used to trigger
    #    actions that should happen once all spokes on the given Hub have
    #    finished initialization.
    # -> If there is no Hub (standalone spoke) the property is None
    #
    # - initialize_start()
    # -> Should be called when Spoke initialization is started.
    # -> Needs to be called explicitly, if we called it for every spoke by default
    #    then any spoke that does not call initialize_done() would prevent the
    #    controller form ever triggering the init_done signal.
    #
    # - initialize_done()
    # -> Must be called by every spoke that calls initialize_start() or else the init_done
    #    signal will never be emitted.

    @property
    def initialization_controller(self):
        # standalone spokes don't have a category
        if self.category:
            return lifecycle.get_controller_by_category(category_name=self.category.__name__)
        else:
            return None

    def initialize_start(self):
        # get the correct controller for this spoke
        spoke_controller = self.initialization_controller
        # check if there actually is a controller for this spoke, there might not be one
        # if this is a standalone spoke
        if spoke_controller:
            spoke_controller.module_init_start(self)

    def initialize_done(self):
        # get the correct controller for this spoke
        spoke_controller = self.initialization_controller
        # check if there actually is a controller for this spoke, there might not be one
        # if this is a standalone spoke
        if spoke_controller:
            spoke_controller.module_init_done(self)

    def __repr__(self):
        """Return the class name as representation.

        Returning the class name should be enough the uniquely identify a spoke.
        """
        return self.__class__.__name__