[blivet] Make RAIDLevel an abstract class using abc.
by Anne Mulhern
RAIDLevel was, already, an abstract class, but using abc makes this more
obvious and harder to break.
Signed-off-by: mulhern <amulhern(a)redhat.com>
---
blivet/devicelibs/raid.py | 79 ++++++++++++++++++++++++++---------------------
1 file changed, 43 insertions(+), 36 deletions(-)
diff --git a/blivet/devicelibs/raid.py b/blivet/devicelibs/raid.py
index fddf56d..37f9755 100644
--- a/blivet/devicelibs/raid.py
+++ b/blivet/devicelibs/raid.py
@@ -21,6 +21,8 @@
# Anne Mulhern <amulhern(a)redhat.com>
#
+import abc
+
from ..errors import RaidError
def div_up(a,b):
@@ -29,46 +31,45 @@ def div_up(a,b):
class RAIDLevel(object):
+ __metaclass__ = abc.ABCMeta
+
"""An abstract class which is the parent of classes which represent a
RAID level. A better word would be classification, since 'level'
implies an ordering, but level is the canonical word.
- The fields of the class are:
- _level --- A string containing the number that designates this level
- _min_members --- The minimum number of members required for this
+ The abstract properties of the class are:
+ level --- A string containing the number that designates this level
+ min_members --- The minimum number of members required for this
level to be sensibly used.
- _nick --- A single nickname for this level, may be None
+ nick --- A single nickname for this level, may be None
All methods in this class fall into these categories:
1) May not be overrridden in any subclass.
- 2) Are private methods that must be overridden in every subclass.
+ 2) Are private abstract methods.
3) Are special Python methods, e.g., __str__
Note that each subclass in this file is instantiated immediately after
- it is defined, effectively yielding a singleton object of the class.
+ it is defined and using the same name, effectively yielding a
+ singleton object of the class.
"""
- # FIELDS
- _level = None
- _min_members = None
- _nick = None
-
- # PROPERTIES
- level = property(lambda s: s._level,
+ # ABSTRACT PROPERTIES
+ level = abc.abstractproperty(lambda s: None,
doc="A code representing the level")
- min_members = property(lambda s: s._min_members,
+ min_members = abc.abstractproperty(lambda s: None,
doc="The minimum number of members required for this level")
- number = property(lambda s : int(s._level),
+ nick = abc.abstractproperty(lambda s : None,
+ doc="A nickname for this level")
+
+ # PROPERTIES
+ number = property(lambda s : int(s.level),
doc="A numeric code for this level")
name = property(lambda s : "raid" + s.level,
doc="The canonical name for this level")
- nick = property(lambda s : s._nick,
- doc="A nickname for this level")
-
alt_synth_names = property(lambda s : ["RAID" + s.level, s.level, s.number],
doc="names that can be synthesized from level but are not name")
@@ -85,6 +86,7 @@ class RAIDLevel(object):
raise RaidError("%s requires at least %d disks" % (self.name, self.min_members))
return self._get_max_spares(member_count)
+ @abc.abstractmethod
def _get_max_spares(self, member_count):
"""Helper function; not to be called directly."""
raise NotImplementedError()
@@ -104,6 +106,7 @@ class RAIDLevel(object):
raise RaidError("size is a negative number")
return self._get_base_member_size(size, member_count)
+ @abc.abstractmethod
def _get_base_member_size(self, size, member_count):
"""Helper function; not to be called directly."""
raise NotImplementedError()
@@ -122,6 +125,7 @@ class RAIDLevel(object):
raise RaidError("size is a negative number")
return self._get_raw_array_size(member_count, smallest_member_size)
+ @abc.abstractmethod
def _get_raw_array_size(self, member_count, smallest_member_size):
"""Helper function; not to be called directly."""
raise NotImplementedError()
@@ -130,6 +134,7 @@ class RAIDLevel(object):
size = self.get_raw_array_size(member_count, smallest_member_size)
return self._get_size(size, chunk_size)
+ @abc.abstractmethod
def _get_size(self, size, chunk_size):
"""Helper function; not to be called directly."""
raise NotImplementedError()
@@ -142,6 +147,7 @@ class RAIDLevel(object):
raise RaidError("%s requires at least %d disks" % (self.name, self.min_members))
return self._get_recommended_stride(member_count)
+ @abc.abstractmethod
def _get_recommended_stride(member_count):
"""Helper function; not to be called directly."""
raise NotImplementedError()
@@ -242,9 +248,10 @@ class RAIDLevels(object):
self._raid_levels.append(level)
class RAID0(RAIDLevel):
- _level = "0"
- _min_members = 2
- _nick = "stripe"
+
+ level = property(lambda s: "0")
+ min_members = property(lambda s: 2)
+ nick = property(lambda s: "stripe")
def _get_max_spares(self, member_count):
return 0
@@ -265,9 +272,9 @@ RAID0 = RAID0()
RAIDLevels.addRAIDLevelToStandardLevels(RAID0)
class RAID1(RAIDLevel):
- _level = "1"
- _min_members = 2
- _nick = "mirror"
+ level = property(lambda s: "1")
+ min_members = property(lambda s: 2)
+ nick = property(lambda s: "mirror")
def _get_max_spares(self, member_count):
return member_count - self.min_members
@@ -288,9 +295,9 @@ RAID1 = RAID1()
RAIDLevels.addRAIDLevelToStandardLevels(RAID1)
class RAID4(RAIDLevel):
- _level = "4"
- _min_members = 3
- _nick = None
+ level = property(lambda s: "4")
+ min_members = property(lambda s: 3)
+ nick = property(lambda s: None)
def _get_max_spares(self, member_count):
return member_count - self.min_members
@@ -311,9 +318,9 @@ RAID4 = RAID4()
RAIDLevels.addRAIDLevelToStandardLevels(RAID4)
class RAID5(RAIDLevel):
- _level = "5"
- _min_members = 3
- _nick = None
+ level = property(lambda s: "5")
+ min_members = property(lambda s: 3)
+ nick = property(lambda s: None)
def _get_max_spares(self, member_count):
return member_count - self.min_members
@@ -334,9 +341,9 @@ RAID5 = RAID5()
RAIDLevels.addRAIDLevelToStandardLevels(RAID5)
class RAID6(RAIDLevel):
- _level = "6"
- _min_members = 4
- _nick = None
+ level = property(lambda s: "6")
+ min_members = property(lambda s: 4)
+ nick = property(lambda s: None)
def _get_max_spares(self, member_count):
return member_count - self.min_members
@@ -357,9 +364,9 @@ RAID6 = RAID6()
RAIDLevels.addRAIDLevelToStandardLevels(RAID6)
class RAID10(RAIDLevel):
- _level = "10"
- _min_members = 4
- _nick = None
+ level = property(lambda s: "10")
+ min_members = property(lambda s: 4)
+ nick = property(lambda s: None)
def _get_max_spares(self, member_count):
return member_count - self.min_members
--
1.8.3.1
10 years, 5 months
[master][PATCH] Use libxklavier's new methods instead of our nasty hack
by Vratislav Podzimek
Signed-off-by: Vratislav Podzimek <vpodzime(a)redhat.com>
---
anaconda.spec.in | 5 +++--
pyanaconda/keyboard.py | 39 ++++++++++++---------------------------
2 files changed, 15 insertions(+), 29 deletions(-)
diff --git a/anaconda.spec.in b/anaconda.spec.in
index 4c68b31..9bd6c30 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -41,6 +41,7 @@ Source0: %{name}-%{version}.tar.bz2
%define rpmver 4.10.0
%define libarchivever 3.0.4
%define langtablever 0.0.18-1
+%define libxklavierver 5.4
BuildRequires: audit-libs-devel
BuildRequires: gettext >= %{gettextver}
@@ -55,7 +56,7 @@ BuildRequires: libXt-devel
BuildRequires: libXxf86misc-devel
BuildRequires: libgnomekbd-devel
BuildRequires: libnl-devel >= %{libnlver}
-BuildRequires: libxklavier-devel
+BuildRequires: libxklavier-devel >= %{libxklavierver}
BuildRequires: pango-devel
BuildRequires: pykickstart >= %{pykickstartver}
%if ! 0%{?rhel}
@@ -105,7 +106,7 @@ Requires: python-IPy
Requires: python-nss
Requires: tigervnc-server-minimal
Requires: pytz
-Requires: libxklavier
+Requires: libxklavier >= %{libxklavierver}
Requires: libgnomekbd
Requires: realmd
Requires: teamd
diff --git a/pyanaconda/keyboard.py b/pyanaconda/keyboard.py
index 21505a2..0d607e6 100644
--- a/pyanaconda/keyboard.py
+++ b/pyanaconda/keyboard.py
@@ -351,21 +351,6 @@ def background_XklWrapper_initialize():
threadMgr.add(AnacondaThread(name=THREAD_XKL_WRAPPER_INIT, target=XklWrapper.get_instance))
-def item_str(s):
- """Convert a zero-terminated byte array to a proper str"""
-
- # depending of version of libxklavier and the tools generating introspection
- # data the value of 's' can be either byte string or list of integers
- if type(s) == types.StringType:
- i = s.find(b'\x00')
- s = s[:i]
- elif type(s) == types.ListType:
- # XXX: this is the wrong case that should be fixed (rhbz#920595)
- i = s.index(0)
- s = "".join(chr(ctypes.c_uint8(char).value) for char in s[:i])
-
- return s.decode("utf-8") #there are some non-ascii layout descriptions
-
class XklWrapperError(KeyboardConfigError):
"""Exception class for reporting libxklavier-related problems"""
@@ -442,11 +427,11 @@ class XklWrapper(object):
def _get_lang_variant(self, c_reg, item, subitem, lang):
if subitem:
- name = item_str(item.name) + " (" + item_str(subitem.name) + ")"
- description = item_str(subitem.description)
+ name = item.get_name() + " (" + subitem.get_name() + ")"
+ description = subitem.get_description()
else:
- name = item_str(item.name)
- description = item_str(item.description)
+ name = item.get_name()
+ description = item.get_description()
#if this layout has already been added for some other language,
#do not add it again (would result in duplicates in our lists)
@@ -456,11 +441,11 @@ class XklWrapper(object):
def _get_country_variant(self, c_reg, item, subitem, country):
if subitem:
- name = item_str(item.name) + " (" + item_str(subitem.name) + ")"
- description = item_str(subitem.description)
+ name = item.get_name() + " (" + subitem.get_name() + ")"
+ description = subitem.get_description()
else:
- name = item_str(item.name)
- description = item_str(item.description)
+ name = item.get_name()
+ description = item.get_description()
# if the layout was not added with any language, add it with a country
if name not in self._layout_infos:
@@ -468,20 +453,20 @@ class XklWrapper(object):
description.encode("utf-8"))
def _get_language_variants(self, c_reg, item, user_data=None):
- lang_name, lang_desc = item_str(item.name), item_str(item.description)
+ lang_name, lang_desc = item.get_name(), item.get_description()
c_reg.foreach_language_variant(lang_name, self._get_lang_variant, lang_desc)
def _get_country_variants(self, c_reg, item, user_data=None):
- country_name, country_desc = item_str(item.name), item_str(item.description)
+ country_name, country_desc = item.get_name(), item.get_description()
c_reg.foreach_country_variant(country_name, self._get_country_variant,
country_desc)
def _get_switch_option(self, c_reg, item, user_data=None):
"""Helper function storing layout switching options in foreach cycle"""
- desc = item_str(item.description)
- name = item_str(item.name)
+ desc = item.get_description()
+ name = item.get_name()
self._switch_opt_infos[name] = desc.encode("utf-8")
--
1.8.4.2
10 years, 5 months
[PATCH] Fixed mdraid imports.
by Jan Safranek
blivet on today's rawhide throws a traceback on any MD RAID operation.
This patch restores "import util", which was erroneously removed in commit
b03b584f716f91956bbc564bff193ca575559700. mdraid.py still uses
util.run_program() and util.capture_output().
---
blivet/devicelibs/mdraid.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/blivet/devicelibs/mdraid.py b/blivet/devicelibs/mdraid.py
index 024ca35..276ac80 100644
--- a/blivet/devicelibs/mdraid.py
+++ b/blivet/devicelibs/mdraid.py
@@ -20,6 +20,7 @@
# Author(s): Dave Lehman <dlehman(a)redhat.com>
#
+from .. import util
from ..errors import *
from . import raid
10 years, 5 months
[master/f20] Show Invalid Disk Label for damaged GPT (#1020974)
by Brian C. Lane
From: "Brian C. Lane" <bcl(a)redhat.com>
A GPT labeled disk could have a damaged partition table, but still be
useful. If that is the case we don't want to treat it like an empty
disk. With these changes it will show up as 'Invalid Disk Label' which
will be translated.
This patch also checks for partitions in the lvm filter that depend on
devices being destroyed. If their dependent device is going away they
are removed from the filter, preventing the lvm pv create from failing
when devices with the same name are created.
---
blivet/devicetree.py | 15 +++++++++++++--
blivet/formats/disklabel.py | 3 +--
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
index 1324499..b5f8f5d 100644
--- a/blivet/devicetree.py
+++ b/blivet/devicetree.py
@@ -47,6 +47,7 @@ from flags import flags
from storage_log import log_method_call, log_method_return
import parted
import _ped
+from i18n import _
import logging
log = logging.getLogger("blivet")
@@ -227,6 +228,13 @@ class DeviceTree(object):
for action in self._actions:
log.debug("action: %s" % action)
+ # Remove any partitions that depend on destroyed ones from the lvm filters.
+ if isinstance(action, ActionDestroyDevice) or \
+ isinstance(action, ActionDestroyFormat):
+ for device in self._devices:
+ if device.dependsOn(action.device):
+ lvm.lvm_cc_removeFilterRejectRegexp(device.name)
+
for action in self._actions[:]:
log.info("executing action: %s" % action)
if not dryRun:
@@ -238,6 +246,7 @@ class DeviceTree(object):
self.teardownAll()
action.execute()
+
udev_settle()
for device in self._devices:
# make sure we catch any renumbering parted does
@@ -1119,9 +1128,11 @@ class DeviceTree(object):
device=device.path,
labelType=labelType,
exists=True)
- except InvalidDiskLabelError:
+ except InvalidDiskLabelError as e:
log.info("no usable disklabel on %s" % device.name)
- return
+ if disklabel_type == "gpt":
+ log.debug(e)
+ device.format = getFormat(_("Invalid Disk Label"))
else:
device.format = format
diff --git a/blivet/formats/disklabel.py b/blivet/formats/disklabel.py
index a79f0a0..5015548 100644
--- a/blivet/formats/disklabel.py
+++ b/blivet/formats/disklabel.py
@@ -150,7 +150,7 @@ class DiskLabel(DeviceFormat):
self._partedDisk = parted.Disk(device=self.partedDevice)
except (_ped.DiskLabelException, _ped.IOException,
NotImplementedError) as e:
- raise InvalidDiskLabelError()
+ raise InvalidDiskLabelError(e)
if self._partedDisk.type == "loop":
# When the device has no partition table but it has a FS,
@@ -444,4 +444,3 @@ class DiskLabel(DeviceFormat):
return 0
register_device_format(DiskLabel)
-
--
1.8.3.1
10 years, 5 months
[master] Fix problems found by pylint
by David Shea
David Shea (3):
Remove unused variables
Disable pylint errors about gobject-introspection methods
Defer translation of device_type_name
pyanaconda/nm.py | 2 +-
pyanaconda/ui/gui/spokes/network.py | 28 ++++++++++++++++------------
2 files changed, 17 insertions(+), 13 deletions(-)
--
1.8.4.2
10 years, 5 months
[PATCH] Add one more directory for ignoring test log files
by David Shea
The newly arranged glade tests put log files one layer deeper, so pile
it on.
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index 913da4d..079e463 100644
--- a/.gitignore
+++ b/.gitignore
@@ -71,6 +71,8 @@ py-compile
stamp-h1
tags
test-driver
+tests/*/*/*.log
+tests/*/*/*.trs
tests/*/*.log
tests/*/*.trs
tests/*.log
--
1.8.4.2
10 years, 5 months
[master] List addons in bug reports
by Vratislav Podzimek
These two patches enrich bug reports from the installer with the information
about addons that were in the installation environment (if any).
Vratislav Podzimek (1):
List addons in exception report data
pyanaconda/exception.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
--
1.8.4.2
10 years, 5 months
[master][PATCH] Allow catching exceptions from threads
by Vratislav Podzimek
By running exception handler every time an exception appears in a thread we
block us from using our features provided by the wait method. Some exceptions
may be non-critical and the waiting thread may recover from the erroneous state.
However, we need to give it a chance to do so.
The wait_all method called before we start the actual installation should make
sure there are no abandoned problematic threads that raised exception nobody
cared about.
Signed-off-by: Vratislav Podzimek <vpodzime(a)redhat.com>
---
pyanaconda/exception.py | 2 ++
pyanaconda/threads.py | 6 +++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/pyanaconda/exception.py b/pyanaconda/exception.py
index e61cce0..850a616 100644
--- a/pyanaconda/exception.py
+++ b/pyanaconda/exception.py
@@ -297,3 +297,5 @@ f%s(msg, non_ascii)
threadMgr.add(AnacondaThread(name=THREAD_EXCEPTION_HANDLING_TEST,
target=raise_exception,
args=(msg, non_ascii)))
+
+ threadMgr.wait(THREAD_EXCEPTION_HANDLING_TEST)
diff --git a/pyanaconda/threads.py b/pyanaconda/threads.py
index 29290df..dadc75d 100644
--- a/pyanaconda/threads.py
+++ b/pyanaconda/threads.py
@@ -121,6 +121,11 @@ class ThreadManager(object):
log.debug("Waiting for thread %s to exit", name)
self.wait(name)
+ if self.any_errors:
+ msg = "Unhandled errors from the following threads detected: %s" %\
+ ", ".join(self._errors.iterkeys())
+ raise RuntimeError(msg)
+
def set_error(self, name, *exc_info):
"""Set the error data for a thread
@@ -212,7 +217,6 @@ class AnacondaThread(threading.Thread):
# pylint: disable-msg=W0702
except:
threadMgr.set_error(self.name, *sys.exc_info())
- sys.excepthook(*sys.exc_info())
finally:
threadMgr.remove(self.name)
log.info("Thread Done: %s (%s)", self.name, self.ident)
--
1.8.4.2
10 years, 5 months