[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