[PATCH 1/3] Don't use parted.Device to obtain size info.
Vratislav Podzimek
vpodzime at redhat.com
Mon Jun 29 13:39:00 UTC 2015
On Fri, 2015-06-26 at 16:54 -0500, David Lehman wrote:
> Deleting a parted.Device causes parted to close its rw fd for the device,
> which triggers a change uevent on that device, which could in turn trigger
> any number of actions via udev rules. One example is when we reset a Blivet
> instance: All devices are deleted, including their parted.Device instances,
> which triggers a change uevent for every device. In response to these
> events, mdadm's udev rules activate all arrays on those devices. Activating
> devices -- even indirectly -- without cause, is not acceptable behavior for
> a storage library.
>
> This also adds a trailing comma to 1-tuples in variable_copy arguments.
> In my testing, ('foo') is the string 'foo' -- not a tuple with lone element
> 'foo'.
>
> (cherry picked from commit 73d9c995c8d46f1aa248d1ade3bbb1838653b6b0)
>
> Resolves: rhbz#1069597
> ---
> blivet/deviceaction.py | 9 ----
> blivet/devices/btrfs.py | 3 ++
> blivet/devices/container.py | 3 ++
> blivet/devices/device.py | 2 +-
> blivet/devices/disk.py | 14 ++----
> blivet/devices/dm.py | 3 +-
> blivet/devices/file.py | 9 ++++
> blivet/devices/lib.py | 3 ++
> blivet/devices/luks.py | 6 +--
> blivet/devices/md.py | 19 ++++----
> blivet/devices/nfs.py | 3 ++
> blivet/devices/nodev.py | 2 +
> blivet/devices/partition.py | 41 +++++++++--------
> blivet/devices/storage.py | 107 +++++++++++++++++++++-----------------------
> blivet/devicetree.py | 15 +++----
> blivet/platform.py | 10 +++--
> tests/storagetestcase.py | 5 +--
> 17 files changed, 124 insertions(+), 130 deletions(-)
>
> diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py
> index a8cd489..bc7c1a0 100644
> --- a/blivet/deviceaction.py
> +++ b/blivet/deviceaction.py
> @@ -28,7 +28,6 @@ from .util import get_current_entropy
> from .devices import StorageDevice
> from .devices import PartitionDevice, LVMLogicalVolumeDevice
> from .formats import getFormat, luks
> -from .storage_log import log_exception_info
> from parted import partitionFlag, PARTITION_LBA
> from .i18n import _, N_
> from .callbacks import CreateFormatPreData, CreateFormatPostData
> @@ -344,14 +343,6 @@ class ActionDestroyDevice(DeviceAction):
> super(ActionDestroyDevice, self).execute(callbacks=None)
> self.device.destroy()
>
> - # Make sure libparted does not keep cached info for this device
> - # and returns it when we create a new device with the same name
> - if self.device.partedDevice:
> - try:
> - self.device.partedDevice.removeFromCache()
> - except Exception: # pylint: disable=broad-except
> - log_exception_info(fmt_str="failed to remove info for device %s from libparted cache", fmt_args=[self.device])
> -
> def requires(self, action):
> """ Return True if self requires action.
>
> diff --git a/blivet/devices/btrfs.py b/blivet/devices/btrfs.py
> index 8e47708..eeb203f 100644
> --- a/blivet/devices/btrfs.py
> +++ b/blivet/devices/btrfs.py
> @@ -61,6 +61,9 @@ class BTRFSDevice(StorageDevice):
> self.sysfsPath = self.parents[0].sysfsPath
> log.debug("%s sysfsPath set to %s", self.name, self.sysfsPath)
>
> + def updateSize(self):
> + pass
> +
> def _postCreate(self):
> super(BTRFSDevice, self)._postCreate()
> self.format.exists = True
> diff --git a/blivet/devices/container.py b/blivet/devices/container.py
> index d8a1379..6e5683e 100644
> --- a/blivet/devices/container.py
> +++ b/blivet/devices/container.py
> @@ -200,3 +200,6 @@ class ContainerDevice(StorageDevice):
>
> if member in self.parents:
> self.parents.remove(member)
> +
> + def updateSize(self):
> + pass
> diff --git a/blivet/devices/device.py b/blivet/devices/device.py
> index 498fef4..1824f8c 100644
> --- a/blivet/devices/device.py
> +++ b/blivet/devices/device.py
> @@ -99,7 +99,7 @@ class Device(util.ObjectID):
> """
> return util.variable_copy(self, memo,
> omit=('_raidSet', 'node'),
> - shallow=('_partedDevice', '_partedPartition'))
> + shallow=('_partedPartition',))
>
> def __repr__(self):
> s = ("%(type)s instance (%(id)s) --\n"
> diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py
> index 6a9a91a..d17e975 100644
> --- a/blivet/devices/disk.py
> +++ b/blivet/devices/disk.py
> @@ -27,6 +27,7 @@ import block
> from .. import errors
> from .. import util
> from ..flags import flags
> +from ..size import Size
> from ..storage_log import log_method_call
> from .. import udev
>
> @@ -89,8 +90,7 @@ class DiskDevice(StorageDevice):
>
> def __repr__(self):
> s = StorageDevice.__repr__(self)
> - s += (" removable = %(removable)s partedDevice = %(partedDevice)r" %
> - {"removable": self.removable, "partedDevice": self.partedDevice})
> + s += (" removable = %(removable)s" % {"removable": self.removable})
> return s
>
> @property
> @@ -98,23 +98,15 @@ class DiskDevice(StorageDevice):
> if flags.testing:
> return True
>
> - if not self.partedDevice:
> - return False
> -
> # Some drivers (cpqarray <blegh>) make block device nodes for
> # controllers with no disks attached and then report a 0 size,
> # treat this as no media present
> - return self.partedDevice.getLength(unit="B") != 0
> + return self.exists and self.currentSize > Size(0)
>
> @property
> def description(self):
> return self.model
>
> - @property
> - def size(self):
> - """ The disk's size """
> - return super(DiskDevice, self).size
> -
> def _preDestroy(self):
> """ Destroy the device. """
> log_method_call(self, self.name, status=self.status)
> diff --git a/blivet/devices/dm.py b/blivet/devices/dm.py
> index 57314bd..9326786 100644
> --- a/blivet/devices/dm.py
> +++ b/blivet/devices/dm.py
> @@ -34,6 +34,7 @@ import logging
> log = logging.getLogger("blivet")
>
> from .storage import StorageDevice
> +from .lib import LINUX_SECTOR_SIZE
>
> class DMDevice(StorageDevice):
> """ A device-mapper device """
> @@ -181,7 +182,7 @@ class DMLinearDevice(DMDevice):
> """ Open, or set up, a device. """
> log_method_call(self, self.name, orig=orig, status=self.status,
> controllable=self.controllable)
> - slave_length = self.slave.partedDevice.length
> + slave_length = self.slave.currentSize / LINUX_SECTOR_SIZE
> dm.dm_create_linear(self.name, self.slave.path, slave_length,
> self.dmUuid)
>
> diff --git a/blivet/devices/file.py b/blivet/devices/file.py
> index da231c5..7aaf011 100644
> --- a/blivet/devices/file.py
> +++ b/blivet/devices/file.py
> @@ -21,6 +21,7 @@
> #
>
> import os
> +import stat
>
> from .. import util
> from ..storage_log import log_method_call
> @@ -80,6 +81,14 @@ class FileDevice(StorageDevice):
>
> return os.path.normpath("%s%s" % (root, self.name))
>
> + def _getSize(self):
> + size = self._size
> + if self.exists and os.path.exists(self.path):
> + st = os.stat(self.path)
> + size = Size(st[stat.ST_SIZE])
> +
> + return size
> +
> def _preSetup(self, orig=False):
> if self.format and self.format.exists and not self.format.status:
> self.format.device = self.path
> diff --git a/blivet/devices/lib.py b/blivet/devices/lib.py
> index c4996db..979dd6e 100644
> --- a/blivet/devices/lib.py
> +++ b/blivet/devices/lib.py
> @@ -20,6 +20,9 @@
> #
> from .. import errors
> from .. import udev
> +from ..size import Size
> +
> +LINUX_SECTOR_SIZE = Size(512)
>
> def get_device_majors():
> majors = {}
> diff --git a/blivet/devices/luks.py b/blivet/devices/luks.py
> index f02cd28..197926f 100644
> --- a/blivet/devices/luks.py
> +++ b/blivet/devices/luks.py
> @@ -22,8 +22,6 @@
> # device backend modules
> from ..devicelibs import crypto
>
> -from ..size import Size
> -
> import logging
> log = logging.getLogger("blivet")
>
> @@ -63,10 +61,10 @@ class LUKSDevice(DMCryptDevice):
>
> @property
> def size(self):
> - if not self.exists or not self.partedDevice:
> + if not self.exists:
> size = self.slave.size - crypto.LUKS_METADATA_SIZE
> else:
> - size = Size(self.partedDevice.getLength(unit="B"))
> + size = self.currentSize
> return size
>
> def _postCreate(self):
> diff --git a/blivet/devices/md.py b/blivet/devices/md.py
> index 8ce7854..33d91b5 100644
> --- a/blivet/devices/md.py
> +++ b/blivet/devices/md.py
> @@ -28,7 +28,6 @@ from .. import util
> from ..flags import flags
> from ..storage_log import log_method_call
> from .. import udev
> -from ..size import Size
> from ..i18n import P_
>
> import logging
> @@ -81,15 +80,15 @@ class MDRaidArrayDevice(ContainerDevice):
> self._memberDevices = 0 # the number of active (non-spare) members
> self._totalDevices = 0 # the total number of members
>
> + if level == "container":
> + self._type = "mdcontainer"
> + self.level = level
> +
> super(MDRaidArrayDevice, self).__init__(name, fmt=fmt, uuid=uuid,
> exists=exists, size=size,
> parents=parents,
> sysfsPath=sysfsPath)
>
> - if level == "container":
> - self._type = "mdcontainer"
> - self.level = level
This chunk comes from a different commit and is unrelated to the rest of
this commit, I think.
> -
> # For new arrays check if we have enough members
> if (not exists and parents and len(parents) < self.level.min_members):
> for dev in self.parents:
> @@ -196,7 +195,7 @@ class MDRaidArrayDevice(ContainerDevice):
> if self.type == "mdbiosraidarray":
> return self._size
>
> - if not self.exists or not self.partedDevice:
> + if not self.exists or not self.mediaPresent:
> try:
> size = self.level.get_size([d.size for d in self.devices],
> self.memberDevices,
> @@ -207,11 +206,15 @@ class MDRaidArrayDevice(ContainerDevice):
> size = 0
> log.debug("non-existent RAID %s size == %s", self.level, size)
> else:
> - size = Size(self.partedDevice.getLength(unit="B"))
> + size = self.currentSize
> log.debug("existing RAID %s size == %s", self.level, size)
>
> return size
>
> + def updateSize(self):
> + # pylint: disable=bad-super-call
> + super(ContainerDevice, self).updateSize()
> +
> @property
> def description(self):
> if self.type == "mdcontainer":
> @@ -561,7 +564,7 @@ class MDRaidArrayDevice(ContainerDevice):
> elif flags.testing:
> return True
> else:
> - return self.partedDevice is not None
> + return super(MDRaidArrayDevice, self).mediaPresent
>
> @property
> def model(self):
> diff --git a/blivet/devices/nfs.py b/blivet/devices/nfs.py
> index 513a11c..da00114 100644
> --- a/blivet/devices/nfs.py
> +++ b/blivet/devices/nfs.py
> @@ -69,6 +69,9 @@ class NFSDevice(StorageDevice, NetworkStorageDevice):
> """ Destroy the device. """
> log_method_call(self, self.name, status=self.status)
>
> + def updateSize(self):
> + pass
> +
> @classmethod
> def isNameValid(cls, name):
> # Override StorageDevice.isNameValid to allow /
> diff --git a/blivet/devices/nodev.py b/blivet/devices/nodev.py
> index a66636c..1aee5d3 100644
> --- a/blivet/devices/nodev.py
> +++ b/blivet/devices/nodev.py
> @@ -69,6 +69,8 @@ class NoDevice(StorageDevice):
> log_method_call(self, self.name, status=self.status)
> self._preDestroy()
>
> + def udpateSize(self):
> + pass
>
> class TmpFSDevice(NoDevice):
> """ A nodev device for a tmpfs filesystem. """
> diff --git a/blivet/devices/partition.py b/blivet/devices/partition.py
> index 2701572..18add66 100644
> --- a/blivet/devices/partition.py
> +++ b/blivet/devices/partition.py
> @@ -125,6 +125,12 @@ class PartitionDevice(StorageDevice):
>
> self._bootable = False
>
> + # FIXME: Validate partType, but only if this is a new partition
> + # Otherwise, overwrite it with the partition's type.
> + self._partType = None
> + self._partedPartition = None
> + self._origPath = None
> +
> StorageDevice.__init__(self, name, fmt=fmt, size=size,
> major=major, minor=minor, exists=exists,
> sysfsPath=sysfsPath, parents=parents)
> @@ -134,13 +140,6 @@ class PartitionDevice(StorageDevice):
> self.req_disks = list(self.parents)
> self.parents = []
>
> - # FIXME: Validate partType, but only if this is a new partition
> - # Otherwise, overwrite it with the partition's type.
> - self._partType = None
> - self._partedPartition = None
> - self._origPath = None
> - self._currentSize = 0
> -
> # FIXME: Validate size, but only if this is a new partition.
> # For existing partitions we will get the size from
> # parted.
> @@ -529,7 +528,6 @@ class PartitionDevice(StorageDevice):
> return
>
> self._size = Size(self.partedPartition.getLength(unit="B"))
> - self._currentSize = self._size
> self.targetSize = self._size
>
> self._partType = self.partedPartition.type
> @@ -592,7 +590,6 @@ class PartitionDevice(StorageDevice):
> DeviceFormat(device=self.path, exists=True).destroy()
>
> StorageDevice._postCreate(self)
> - self._currentSize = Size(self.partedPartition.getLength(unit="B"))
>
> def create(self):
> """ Create the device. """
> @@ -661,7 +658,7 @@ class PartitionDevice(StorageDevice):
> end=geometry.end)
>
> self.disk.format.commit()
> - self._currentSize = Size(partition.getLength(unit="B"))
> + self.updateSize()
>
> def _preDestroy(self):
> StorageDevice._preDestroy(self)
> @@ -728,12 +725,16 @@ class PartitionDevice(StorageDevice):
> return size
>
> def _setSize(self, newsize):
> - """ Set the device's size (for resize, not creation).
> + """ Set the device's size.
> +
> + Most devices have two scenarios for setting a size:
>
> - Arguments:
> + 1) set actual/current size
> + 2) set target for resize
>
> - newsize -- the new size
> + Partitions have a third scenario:
>
> + 3) update size of an allocated-but-non-existent partition
> """
> log_method_call(self, self.name,
> status=self.status, size=self._size, newsize=newsize)
> @@ -746,6 +747,12 @@ class PartitionDevice(StorageDevice):
> self.req_size = newsize
> self.req_base_size = newsize
>
> + if self.exists:
> + super(PartitionDevice, self)._setSize(newsize)
> + return
> +
> + # the rest is for changing the size of an allocated-but-not-existing
> + # partition, which I'm not sure is advisable
> if newsize > self.disk.size:
> raise ValueError("partition size would exceed disk size")
>
> @@ -829,14 +836,6 @@ class PartitionDevice(StorageDevice):
> unalignedMax = min(maxFormatSize, maxPartSize) if maxFormatSize else maxPartSize
> return self.alignTargetSize(unalignedMax)
>
> - @property
> - def currentSize(self):
> - if self.exists:
> - return self._currentSize
> - else:
> - return 0
> -
> - @property
> def resizable(self):
> return super(PartitionDevice, self).resizable and \
> self.disk.type != 'dasd'
> diff --git a/blivet/devices/storage.py b/blivet/devices/storage.py
> index 73ba645..6128886 100644
> --- a/blivet/devices/storage.py
> +++ b/blivet/devices/storage.py
> @@ -22,8 +22,6 @@
>
> import os
> import copy
> -import parted
> -import _ped
> import pyudev
>
> from .. import errors
> @@ -38,6 +36,7 @@ import logging
> log = logging.getLogger("blivet")
>
> from .device import Device
> +from .lib import LINUX_SECTOR_SIZE
>
> class StorageDevice(Device):
> """ A generic storage device.
> @@ -105,7 +104,11 @@ class StorageDevice(Device):
> Device.__init__(self, name, parents=parents)
>
> self._format = None
> +
> + # The size will be overridden by a call to updateSize at the end of this
> + # method for existing and active devices.
> self._size = Size(util.numeric_type(size))
> + self._currentSize = self._size if self.exists else Size(0)
> self.major = util.numeric_type(major)
> self.minor = util.numeric_type(minor)
> self._serial = serial
> @@ -123,16 +126,8 @@ class StorageDevice(Device):
>
> self.deviceLinks = []
>
> - if self.exists and flags.testing and not self._size:
> - def read_int_from_sys(path):
> - return int(open(path).readline().strip())
> -
> - device_root = "/sys/class/block/%s" % self.name
> - if os.path.exists("%s/queue" % device_root):
> - sector_size = read_int_from_sys("%s/queue/logical_block_size"
> - % device_root)
> - size = read_int_from_sys("%s/size" % device_root)
> - self._size = Size(size * sector_size)
> + if self.exists and self.status:
> + self.updateSize()
>
> self._orig_size = self._size
>
> @@ -199,26 +194,6 @@ class StorageDevice(Device):
> """ True if this device, or any it requires, is encrypted. """
> return self._encrypted or any(p.encrypted for p in self.parents)
>
> - def _getPartedDevicePath(self):
> - return self.path
> -
> - @property
> - def partedDevice(self):
> - devicePath = self._getPartedDevicePath()
> - if self.exists and self.status and not self._partedDevice:
> - log.debug("looking up parted Device: %s", devicePath)
> -
> - # We aren't guaranteed to be able to get a device. In
> - # particular, built-in USB flash readers show up as devices but
> - # do not always have any media present, so parted won't be able
> - # to find a device.
> - try:
> - self._partedDevice = parted.Device(path=devicePath)
> - except (_ped.IOException, _ped.DeviceException):
> - pass
> -
> - return self._partedDevice
> -
> @property
> def raw_device(self):
> """ The device itself, or when encrypted, the backing device. """
> @@ -281,12 +256,12 @@ class StorageDevice(Device):
> " format = %(format)s\n"
> " major = %(major)s minor = %(minor)s exists = %(exists)s"
> " protected = %(protected)s\n"
> - " sysfs path = %(sysfs)s partedDevice = %(partedDevice)s\n"
> + " sysfs path = %(sysfs)s\n"
> " target size = %(targetSize)s path = %(path)s\n"
> " format args = %(formatArgs)s originalFormat = %(origFmt)s" %
> {"uuid": self.uuid, "format": self.format, "size": self.size,
> "major": self.major, "minor": self.minor, "exists": self.exists,
> - "sysfs": self.sysfsPath, "partedDevice": self.partedDevice,
> + "sysfs": self.sysfsPath,
> "targetSize": self.targetSize, "path": self.path,
> "protected": self.protected,
> "formatArgs": self.formatArgs, "origFmt": self.originalFormat.type})
> @@ -410,9 +385,10 @@ class StorageDevice(Device):
> def _postSetup(self):
> """ Perform post-setup operations. """
> udev.settle()
> - # we always probe since the device may not be set up when we want
> - # information about it
> - self._size = self.currentSize
> + self.updateSysfsPath()
> + # the device may not be set up when we want information about it
> + if self._size == Size(0):
> + self.updateSize()
>
> #
> # teardown
> @@ -487,9 +463,7 @@ class StorageDevice(Device):
> udev.settle()
>
> # make sure that targetSize is updated to reflect the actual size
> - if self.resizable:
> - self._partedDevice = None
> - self._targetSize = self.currentSize
> + self.updateSize()
>
> #
> # destroy
> @@ -535,12 +509,6 @@ class StorageDevice(Device):
>
> def _getSize(self):
> """ Get the device's size, accounting for pending changes. """
> - if self.exists and not self.mediaPresent:
> - return 0
> -
> - if self.exists and self.partedDevice:
> - self._size = self.currentSize
> -
> size = self._size
> if self.exists and self.resizable:
> size = self.targetSize
> @@ -548,19 +516,41 @@ class StorageDevice(Device):
> return size
>
> def _setSize(self, newsize):
> - """ Set the device's size to a new value. """
> + """ Set the device's size to a new value.
> +
> + This is not adequate to set up a resize as it does not set a new
> + target size for the device.
> + """
> if not isinstance(newsize, Size):
> raise ValueError("new size must of type Size")
>
> - if self.maxSize and newsize > self.maxSize:
> + # only calculate these once
> + max_size = self.maxSize
> + min_size = self.minSize
> + if max_size and newsize > max_size:
> raise errors.DeviceError("device cannot be larger than %s" %
> - (self.maxSize,), self.name)
> + max_size, self.name)
> + elif min_size and newsize < min_size:
> + raise errors.DeviceError("device cannot be smaller than %s" %
> + min_size, self.name)
> +
> self._size = newsize
>
> size = property(lambda x: x._getSize(),
> lambda x, y: x._setSize(y),
> doc="The device's size, accounting for pending changes")
>
> + def readCurrentSize(self):
> + log_method_call(self, exists=self.exists, path=self.path,
> + sysfsPath=self.sysfsPath)
> + size = Size(0)
> + if self.exists and os.path.exists(self.path) and \
> + os.path.isdir(self.sysfsPath):
> + blocks = int(util.get_sysfs_attr(self.sysfsPath, "size"))
> + size = Size(blocks * LINUX_SECTOR_SIZE)
> +
> + return size
> +
> @property
> def currentSize(self):
> """ The device's actual size, generally the size discovered by using
> @@ -569,12 +559,17 @@ class StorageDevice(Device):
>
> If the device does not exist, then the actual size is 0.
> """
> - size = 0
> - if self.exists and self.partedDevice:
> - size = Size(self.partedDevice.getLength(unit="B"))
> - elif self.exists:
> - size = self._size
> - return size
> + if self._currentSize == Size(0):
> + self._currentSize = self.readCurrentSize()
> + return self._currentSize
> +
> + def updateSize(self):
> + """ Update size, currentSize, and targetSize to actual size. """
> + self._currentSize = Size(0)
> + new_size = self.currentSize
> + self._size = new_size
> + self._targetSize = new_size # bypass setter checks
> + log.debug("updated %s size to %s (%s)", self.name, self.size, new_size)
>
> @property
> def minSize(self):
> @@ -658,8 +653,6 @@ class StorageDevice(Device):
>
> @property
> def model(self):
> - if not self._model:
> - self._model = getattr(self.partedDevice, "model", "")
> return self._model
>
> @property
> diff --git a/blivet/devicetree.py b/blivet/devicetree.py
> index 70b95f1..a70d7b9 100644
> --- a/blivet/devicetree.py
> +++ b/blivet/devicetree.py
> @@ -1085,6 +1085,7 @@ class DeviceTree(object):
> device = diskType(name,
> major=udev.device_get_major(info),
> minor=udev.device_get_minor(info),
> + model=udev.device_get_model(info),
> sysfsPath=sysfs_path, **kwargs)
>
> if diskType == DASDDevice:
> @@ -1203,9 +1204,7 @@ class DeviceTree(object):
> # The first step is to either look up or create the device
> #
> if device:
> - # we successfully looked up the device. skip to format handling.
> - # first, grab the parted.Device while it's active
> - _unused = device.partedDevice
> + pass
> elif udev.device_is_loop(info):
> log.info("%s is a loop device", name)
> device = self.addUdevLoopDevice(info)
> @@ -1529,6 +1528,7 @@ class DeviceTree(object):
>
> if lv_device.status:
> lv_device.updateSysfsPath()
> + lv_device.updateSize()
> lv_info = udev.get_device(lv_device.sysfsPath)
> if not lv_info:
> log.error("failed to get udev data for lv %s", lv_device.name)
> @@ -1871,15 +1871,10 @@ class DeviceTree(object):
> kwargs["name"] = "luks-%s" % uuid
> elif format_type in formats.mdraid.MDRaidMember._udevTypes:
> # mdraid
> - try:
> - kwargs["mdUuid"] = udev.device_get_md_uuid(info)
> - except KeyError:
> - log.warning("mdraid member %s has no md uuid", name)
> -
> # reset the uuid to the member-specific value
> # this will be None for members of v0 metadata arrays
> - kwargs["uuid"] = udev.device_get_md_device_uuid(info)
> -
> + kwargs["uuid"] = info.get("ID_FS_UUID_SUB")
> + kwargs["mdUuid"] = uuid
This chunk also looks unrelated to the rest of the commit.
--
Vratislav Podzimek
Anaconda Rider | Red Hat, Inc. | Brno - Czech Republic
More information about the anaconda-patches
mailing list