[master 5/22] Add tasks infrastructure to tasks directory.

mulkieran installerbot-noreply at redhat.com
Tue May 5 16:35:29 UTC 2015


From: mulhern <amulhern at redhat.com>

Related: #12

Signed-off-by: mulhern <amulhern at redhat.com>
---
 blivet/tasks/__init__.py     |   0
 blivet/tasks/availability.py | 276 +++++++++++++++++++++++++++++++++++++++++++
 blivet/tasks/task.py         |  95 +++++++++++++++
 python-blivet.spec           |   2 +
 setup.py                     |   2 +-
 tests/pylint/runpylint.py    |   2 +
 6 files changed, 376 insertions(+), 1 deletion(-)
 create mode 100644 blivet/tasks/__init__.py
 create mode 100644 blivet/tasks/availability.py
 create mode 100644 blivet/tasks/task.py

diff --git a/blivet/tasks/__init__.py b/blivet/tasks/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/blivet/tasks/availability.py b/blivet/tasks/availability.py
new file mode 100644
index 0000000..4a35ef0
--- /dev/null
+++ b/blivet/tasks/availability.py
@@ -0,0 +1,276 @@
+# availability.py
+# Class for tracking availability of an application.
+#
+# Copyright (C) 2014-2015  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Anne Mulhern <amulhern at redhat.com>
+
+import abc
+from distutils.version import LooseVersion
+import rpm
+
+from six import add_metaclass
+
+from gi.repository import BlockDev as blockdev
+
+from .. import util
+from ..errors import AvailabilityError
+
+import logging
+log = logging.getLogger("blivet")
+
+CACHE_AVAILABILITY = True
+
+class ExternalResource(object):
+    """ An external resource. """
+
+    def __init__(self, method, name):
+        """ Initializes an instance of an application.
+
+            :param method: A method object
+            :type method: :class:`Method`
+            :param str name: the name of the external resource
+        """
+        self._method = method
+        self.name = name
+        self._availabilityErrors = None
+
+    def __str__(self):
+        return self.name
+
+    @property
+    def availabilityErrors(self):
+        """ Whether the resource has any availability errors.
+
+            :returns: [] if the resource is available
+            :rtype: list of str
+        """
+        if self._availabilityErrors is None or not CACHE_AVAILABILITY:
+            self._availabilityErrors = self._method.availabilityErrors(self)
+        return self._availabilityErrors[:]
+
+    @property
+    def available(self):
+        """ Whether the resource is available.
+
+            :returns: True if the resource is available
+            :rtype: bool
+        """
+        return self.availabilityErrors == []
+
+ at add_metaclass(abc.ABCMeta)
+class Method(object):
+    """ Method for determining if external resource is available."""
+
+    @abc.abstractmethod
+    def availabilityErrors(self, resource):
+        """ Returns [] if the resource is available.
+
+            :param resource: any external resource
+            :type resource: :class:`ExternalResource`
+
+            :returns: [] if the external resource is available
+            :rtype: list of str
+        """
+        raise NotImplementedError()
+
+class Path(Method):
+    """ Methods for when application is found in  PATH. """
+
+    def availabilityErrors(self, resource):
+        """ Returns [] if the name of the application is in the path.
+
+            :param resource: any application
+            :type resource: :class:`ExternalResource`
+
+            :returns: [] if the name of the application is in the path
+            :rtype: list of str
+        """
+        if not util.find_program_in_path(resource.name):
+            return ["application %s is not in $PATH" % resource.name]
+        else:
+            return []
+
+Path = Path()
+
+class PackageInfo(object):
+
+    def __init__(self, package_name, required_version=None):
+        """ Initializer.
+
+            :param str package_name: the name of the package
+            :param required_version: the required version for this package
+            :type required_version: :class:`distutils.LooseVersion` or NoneType
+        """
+        self.package_name = package_name
+        self.required_version = required_version
+
+    def __str__(self):
+        return "%s-%s" % (self.package_name, self.required_version)
+
+class PackageMethod(Method):
+    """ Methods for checking the package version of the external resource. """
+
+    def __init__(self, package=None):
+        """ Initializer.
+
+            :param :class:`PackageInfo` package:
+        """
+        self.package = package
+        self._availabilityErrors = None
+
+    @property
+    def packageVersion(self):
+        """ Returns the version of the package.
+
+            :returns: the package version
+            :rtype: LooseVersion
+        """
+        ts = rpm.TransactionSet()
+        info = ts.dbMatch("provides", self.package.package_name)
+        if info.count() == 0:
+            raise AvailabilityError("Could not determine package version for %s" % self.package.package_name)
+        return LooseVersion(info.next()['version'])
+
+    def availabilityErrors(self, resource):
+        if self._availabilityErrors is not None and CACHE_AVAILABILITY:
+            return self._availabilityErrors[:]
+
+        self._availabilityErrors = Path.availabilityErrors(resource)
+
+        if self.package.required_version is None:
+            return self._availabilityErrors[:]
+
+        try:
+            if self.packageVersion < self.package.required_version:
+                self._availabilityErrors.append("installed version %s for package %s is less than required version %s" % (self.packageVersion, self.package.package_name, self.package.required_version))
+        except AvailabilityError as e:
+            # In contexts like the installer, a package may not be available,
+            # but the version of the tools is likely to be correct.
+            log.warning(str(e))
+
+        return self._availabilityErrors[:]
+
+class BlockDevMethod(Method):
+    """ Methods for when application is actually a libblockdev plugin. """
+
+    def availabilityErrors(self, resource):
+        """ Returns [] if the plugin is loaded.
+
+            :param resource: a libblockdev plugin
+            :type resource: :class:`ExternalResource`
+
+            :returns: [] if the name of the plugin is loaded
+            :rtype: list of str
+        """
+        if resource.name in blockdev.get_available_plugin_names():
+            return []
+        else:
+            return ["libblockdev plugin %s not loaded" % resource.name]
+
+BlockDevMethod = BlockDevMethod()
+
+class UnavailableMethod(Method):
+    """ Method that indicates a resource is unavailable. """
+
+    def availabilityErrors(self, resource):
+        return ["always unavailable"]
+
+UnavailableMethod = UnavailableMethod()
+
+class AvailableMethod(Method):
+    """ Method that indicates a resource is available. """
+
+    def availabilityErrors(self, resource):
+        return []
+
+AvailableMethod = AvailableMethod()
+
+def application(name):
+    """ Construct an external resource that is an application.
+
+        This application will be available if its name can be found in $PATH.
+    """
+    return ExternalResource(Path, name)
+
+def application_by_package(name, package_method):
+    """ Construct an external resource that is an application.
+
+        This application will be available if its name can be found in $PATH
+        AND its package version is at least the required version.
+
+        :param :class:`PackageMethod` package_method: the package method
+    """
+    return ExternalResource(package_method, name)
+
+def blockdev_plugin(name):
+    """ Construct an external resource that is a libblockdev plugin. """
+    return ExternalResource(BlockDevMethod, name)
+
+def unavailable_resource(name):
+    """ Construct an external resource that is always unavailable. """
+    return ExternalResource(UnavailableMethod, name)
+
+def available_resource(name):
+    """ Construct an external resource that is always available. """
+    return ExternalResource(AvailableMethod, name)
+
+# blockdev plugins
+BLOCKDEV_BTRFS_PLUGIN = blockdev_plugin("btrfs")
+BLOCKDEV_CRYPTO_PLUGIN = blockdev_plugin("crypto")
+BLOCKDEV_DM_PLUGIN = blockdev_plugin("dm")
+BLOCKDEV_LOOP_PLUGIN = blockdev_plugin("loop")
+BLOCKDEV_LVM_PLUGIN = blockdev_plugin("lvm")
+BLOCKDEV_MDRAID_PLUGIN = blockdev_plugin("mdraid")
+BLOCKDEV_MPATH_PLUGIN = blockdev_plugin("mpath")
+BLOCKDEV_SWAP_PLUGIN = blockdev_plugin("swap")
+
+# packages
+E2FSPROGS_PACKAGE = PackageMethod(PackageInfo("e2fsprogs", LooseVersion("1.41.0")))
+
+# applications
+DEBUGREISERFS_APP = application("debugreiserfs")
+DF_APP = application("df")
+DOSFSCK_APP = application("dosfsck")
+DOSFSLABEL_APP = application("dosfslabel")
+DUMPE2FS_APP = application_by_package("dumpe2fs", E2FSPROGS_PACKAGE)
+E2FSCK_APP = application_by_package("e2fsck", E2FSPROGS_PACKAGE)
+E2LABEL_APP = application_by_package("e2label", E2FSPROGS_PACKAGE)
+FSCK_HFSPLUS_APP = application("fsck.hfsplus")
+HFORMAT_APP = application("hformat")
+JFSTUNE_APP = application("jfs_tune")
+KPARTX_APP = application("kpartx")
+MKDOSFS_APP = application("mkdosfs")
+MKE2FS_APP = application_by_package("mke2fs", E2FSPROGS_PACKAGE)
+MKFS_BTRFS_APP = application("mkfs.btrfs")
+MKFS_GFS2_APP = application("mkfs.gfs2")
+MKFS_HFSPLUS_APP = application("mkfs.hfsplus")
+MKFS_JFS_APP = application("mkfs.jfs")
+MKFS_XFS_APP = application("mkfs.xfs")
+MKNTFS_APP = application("mkntfs")
+MKREISERFS_APP = application("mkreiserfs")
+MULTIPATH_APP = application("multipath")
+NTFSINFO_APP = application("ntfsinfo")
+NTFSLABEL_APP = application("ntfslabel")
+NTFSRESIZE_APP = application("ntfsresize")
+REISERFSTUNE_APP = application("reiserfstune")
+RESIZE2FS_APP = application_by_package("resize2fs", E2FSPROGS_PACKAGE)
+XFSADMIN_APP = application("xfs_admin")
+XFSDB_APP = application("xfs_db")
+XFSFREEZE_APP = application("xfs_freeze")
+
+MOUNT_APP = application("mount")
diff --git a/blivet/tasks/task.py b/blivet/tasks/task.py
new file mode 100644
index 0000000..31e8dd4
--- /dev/null
+++ b/blivet/tasks/task.py
@@ -0,0 +1,95 @@
+# task.py
+# Abstract class for tasks.
+#
+# Copyright (C) 2015  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Anne Mulhern <amulhern at redhat.com>
+
+import abc
+
+from six import add_metaclass
+
+ at add_metaclass(abc.ABCMeta)
+class Task(object):
+    """ An abstract class that represents some task. """
+
+    # Whether or not the functionality is implemented in the task class.
+    # It is True by default. Only NotImplementedClass and its descendants
+    # should have a False value.
+    implemented = True
+
+    description = abc.abstractproperty(doc="Brief description for this task.")
+
+    @property
+    def availabilityErrors(self):
+        """ Reasons if this task or the tasks it depends on are unavailable. """
+        return self._availabilityErrors + \
+           [e for t in self.dependsOn for e in t.availabilityErrors]
+
+    @property
+    def available(self):
+        """ True if the task is available, otherwise False.
+
+            :returns: True if the task is available
+            :rtype: bool
+        """
+        return self.availabilityErrors == []
+
+    _availabilityErrors = abc.abstractproperty(
+       doc="Reasons if the necessary external tools are unavailable.")
+
+    dependsOn = abc.abstractproperty(doc="tasks that this task depends on")
+
+    @abc.abstractmethod
+    def doTask(self, *args, **kwargs):
+        """ Do the task for this class. """
+        raise NotImplementedError()
+
+class UnimplementedTask(Task):
+    """ A null Task, which returns a negative or empty for all properties."""
+
+    description = "an unimplemented task"
+    implemented = False
+
+    @property
+    def _availabilityErrors(self):
+        return ["Not implemented task can not succeed."]
+
+    dependsOn = []
+
+    def doTask(self, *args, **kwargs):
+        raise NotImplementedError()
+
+ at add_metaclass(abc.ABCMeta)
+class BasicApplication(Task):
+    """ A task representing an application. """
+
+    ext = abc.abstractproperty(doc="The object representing the external resource.")
+
+    # TASK methods
+
+    @property
+    def _availabilityErrors(self):
+        errors = self.ext.availabilityErrors
+        if errors:
+            return ["application %s is not available: %s" % (self.ext, " and ".join(errors))]
+        else:
+            return []
+
+    @property
+    def dependsOn(self):
+        return []
diff --git a/python-blivet.spec b/python-blivet.spec
index ef4bacb..9d499be 100644
--- a/python-blivet.spec
+++ b/python-blivet.spec
@@ -43,6 +43,7 @@ Requires: libselinux-python
 Requires: libblockdev >= %{libblockdevver}
 Requires: libblockdev-plugins-all >= %{libblockdevver}
 Requires: libselinux-python
+Requires: rpm-python
 
 %description
 The python-blivet package is a python module for examining and modifying
@@ -64,6 +65,7 @@ Requires: util-linux >= %{utillinuxver}
 Requires: dosfstools
 Requires: e2fsprogs >= %{e2fsver}
 Requires: lsof
+Requires: rpm-python3
 
 %description -n python3-%{realname}
 The python3-%{realname} is a python3 package for examining and modifying storage
diff --git a/setup.py b/setup.py
index 9bb5831..f5f575d 100644
--- a/setup.py
+++ b/setup.py
@@ -36,4 +36,4 @@ def add_member_order_option(files):
       author='David Lehman', author_email='dlehman at redhat.com',
       url='http://fedoraproject.org/wiki/blivet',
       data_files=data_files,
-      packages=['blivet', 'blivet.devices', 'blivet.devicelibs', 'blivet.formats'])
+      packages=['blivet', 'blivet.devices', 'blivet.devicelibs', 'blivet.formats', 'blivet.tasks'])
diff --git a/tests/pylint/runpylint.py b/tests/pylint/runpylint.py
index 3fb7540..4abc9a2 100755
--- a/tests/pylint/runpylint.py
+++ b/tests/pylint/runpylint.py
@@ -14,6 +14,8 @@ def __init__(self):
                                 FalsePositive(r"(PartitioningTestCase|PartitionDeviceTestCase).*: Instance of 'DeviceFormat' has no .* member"),
                                 FalsePositive(r"Instance of 'int' has no .* member"),
                                 FalsePositive(r"Instance of 'LUKSDevice' has no .* member"),
+                                FalsePositive(r"Method 'doTask' is abstract in class 'Task' but is not overridden"),
+                                FalsePositive(r"Method 'doTask' is abstract in class 'UnimplementedTask' but is not overridden"),
                                 FalsePositive(r"No value for argument 'member_count' in unbound method call$"),
                                 FalsePositive(r"No value for argument 'smallest_member_size' in unbound method call$"),
 


-- 
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/caaa1c4fe050111e7dba3064b58b7520dd8fe0fe


More information about the anaconda-patches mailing list