[master 12/27] Only handle loop devices with a backing file.
by dashea
From: David Lehman <dlehman(a)redhat.com>
---
blivet/populator/helpers/loop.py | 3 ++-
tests/populator_test.py | 28 ++++++++++++++++++++++++----
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/blivet/populator/helpers/loop.py b/blivet/populator/helpers/loop.py
index a0580bd..c723b8f 100644
--- a/blivet/populator/helpers/loop.py
+++ b/blivet/populator/helpers/loop.py
@@ -33,7 +33,8 @@
class LoopDevicePopulator(DevicePopulator):
@classmethod
def match(cls, data):
- return udev.device_is_loop(data)
+ return (udev.device_is_loop(data) and
+ bool(blockdev.loop.get_backing_file(udev.device_get_name(data))))
def run(self):
name = udev.device_get_name(self.data)
diff --git a/tests/populator_test.py b/tests/populator_test.py
index 6c5f30a..987b45e 100644
--- a/tests/populator_test.py
+++ b/tests/populator_test.py
@@ -179,14 +179,25 @@ def test_run(self, *args):
class LoopDevicePopulatorTestCase(PopulatorHelperTestCase):
helper_class = LoopDevicePopulator
+ @patch("blivet.populator.helpers.loop.blockdev.loop.get_backing_file")
+ @patch("blivet.udev.device_get_name")
@patch("blivet.udev.device_is_loop", return_value=True)
def test_match(self, *args):
"""Test matching of loop device populator."""
device_is_loop = args[0]
+ get_backing_file = args[2]
+ get_backing_file.return_value = True
self.assertTrue(self.helper_class.match(None))
+
+ get_backing_file.return_value = False
+ self.assertFalse(self.helper_class.match(None))
+ get_backing_file.return_value = True
+
device_is_loop.return_value = False
self.assertFalse(self.helper_class.match(None))
+ @patch("blivet.populator.helpers.loop.blockdev.loop.get_backing_file")
+ @patch("blivet.udev.device_get_name")
@patch("blivet.udev.device_is_dm", return_value=False)
@patch("blivet.udev.device_is_dm_luks", return_value=False)
@patch("blivet.udev.device_is_dm_lvm", return_value=False)
@@ -196,9 +207,15 @@ def test_match(self, *args):
def test_get_helper(self, *args):
"""Test get_device_helper for loop devices."""
device_is_loop = args[0]
+ get_backing_file = args[7]
data = Mock()
+ get_backing_file.return_value = True
self.assertEqual(get_device_helper(data), self.helper_class)
+ get_backing_file.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ get_backing_file.return_value = True
+
# verify that setting one of the required True return values to False prevents success
device_is_loop.return_value = False
self.assertNotEqual(get_device_helper(data), self.helper_class)
@@ -399,6 +416,7 @@ def test_match(self, *args):
device_is_dm_partition.return_value = True
self.assertTrue(self.helper_class.match(None))
+ @patch("blivet.udev.device_get_name")
@patch("blivet.udev.device_is_dm", return_value=False)
@patch("blivet.udev.device_is_dm_luks", return_value=False)
@patch("blivet.udev.device_is_dm_lvm", return_value=False)
@@ -422,7 +440,9 @@ def test_get_helper(self, *args):
# verify that setting one of the required False return values to True prevents success
# as of now, loop is always checked before partition
device_is_loop.return_value = True
- self.assertNotEqual(get_device_helper(data), self.helper_class)
+ with patch("blivet.populator.helpers.loop.blockdev.loop.get_backing_file", return_value=True):
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+
device_is_loop.return_value = False
@patch.object(DiskDevice, "partitioned")
@@ -503,7 +523,7 @@ def test_match(self, *args):
def test_get_helper(self, *args):
"""Test get_device_helper for disks."""
device_is_disk = args[0]
- device_is_loop = args[3]
+ device_is_cdrom = args[5]
data = {"DM_NAME": None}
self.assertEqual(get_device_helper(data), self.helper_class)
@@ -514,9 +534,9 @@ def test_get_helper(self, *args):
# verify that setting one of the required False return values to True prevents success
# as of now, loop is always checked before partition
- device_is_loop.return_value = True
+ device_is_cdrom.return_value = True
self.assertNotEqual(get_device_helper(data), self.helper_class)
- device_is_loop.return_value = False
+ device_is_cdrom.return_value = False
@patch("blivet.udev.device_get_major", return_value=99)
@patch("blivet.udev.device_get_minor", return_value=222)
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/35a6540951371e90ae457fe8f3d5...
8 years, 5 months
[master 11/27] Add unit tests for populator helpers.
by dashea
From: David Lehman <dlehman(a)redhat.com>
This is by no means complete.
---
blivet/populator/helpers/loop.py | 4 +
tests/populator_test.py | 1091 +++++++++++++++++++++++++++++++++++++-
2 files changed, 1093 insertions(+), 2 deletions(-)
diff --git a/blivet/populator/helpers/loop.py b/blivet/populator/helpers/loop.py
index 5f1cf2b..a0580bd 100644
--- a/blivet/populator/helpers/loop.py
+++ b/blivet/populator/helpers/loop.py
@@ -20,6 +20,10 @@
# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
#
+import gi
+gi.require_version("BlockDev", "1.0")
+from gi.repository import BlockDev as blockdev
+
from ... import udev
from ...devices import FileDevice, LoopDevice
from ...storage_log import log_method_call
diff --git a/tests/populator_test.py b/tests/populator_test.py
index 8549d0d..6c5f30a 100644
--- a/tests/populator_test.py
+++ b/tests/populator_test.py
@@ -1,10 +1,31 @@
+import gi
import os
import unittest
+from unittest.mock import call, patch, sentinel, Mock
+
+gi.require_version("BlockDev", "1.0")
+from gi.repository import BlockDev as blockdev
+
from blivet import Blivet
-from blivet.flags import flags
from blivet import util
-from blivet.size import Size
+from blivet.devices import DiskDevice, DMDevice, FileDevice, LoopDevice
+from blivet.devices import MDRaidArrayDevice, MultipathDevice, OpticalDevice
+from blivet.devices import PartitionDevice, StorageDevice
+from blivet.devicetree import DeviceTree
+from blivet.flags import flags
+from blivet.formats import get_device_format_class, get_format, DeviceFormat
+from blivet.formats.disklabel import DiskLabel
from blivet.osinstall import storage_initialize
+from blivet.populator import Populator
+from blivet.populator.helpers import DiskDevicePopulator, DMDevicePopulator, LoopDevicePopulator
+from blivet.populator.helpers import LVMDevicePopulator, MDDevicePopulator, MultipathDevicePopulator
+from blivet.populator.helpers import OpticalDevicePopulator, PartitionDevicePopulator
+from blivet.populator.helpers import LVMFormatPopulator, MDFormatPopulator
+from blivet.populator.helpers import get_format_helper, get_device_helper
+from blivet.populator.helpers.boot import AppleBootFormatPopulator, EFIFormatPopulator, MacEFIFormatPopulator
+from blivet.populator.helpers.formatpopulator import FormatPopulator
+from blivet.populator.helpers.disklabel import DiskLabelFormatPopulator
+from blivet.size import Size
try:
from pyanaconda import kickstart
pyanaconda_present = True
@@ -57,5 +78,1071 @@ def runTest(self):
for d in self.blivet.devicetree.devices:
self.assertTrue(d.size > 0)
+
+class PopulatorHelperTestCase(unittest.TestCase):
+ helper_class = None
+
+
+class DMDevicePopulatorTestCase(PopulatorHelperTestCase):
+ helper_class = DMDevicePopulator
+
+ @patch("blivet.udev.device_is_dm_luks", return_value=False)
+ @patch("blivet.udev.device_is_dm_lvm", return_value=False)
+ @patch("blivet.udev.device_is_dm_mpath", return_value=False)
+ @patch("blivet.udev.device_is_dm_partition", return_value=False)
+ @patch("blivet.udev.device_is_dm_raid", return_value=False)
+ @patch("blivet.udev.device_is_dm", return_value=True)
+ def test_match(self, *args):
+ """Test matching of dm device populator."""
+ device_is_dm = args[0]
+ device_is_dm_luks = args[5]
+ self.assertTrue(self.helper_class.match(None))
+ device_is_dm.return_value = False
+ self.assertFalse(self.helper_class.match(None))
+
+ # verify that setting one of the required False return values to True prevents success
+ device_is_dm_luks.return_value = True
+ self.assertFalse(self.helper_class.match(None))
+ device_is_dm_luks.return_value = False
+
+ @patch("blivet.udev.device_is_dm_luks", return_value=False)
+ @patch("blivet.udev.device_is_dm_lvm", return_value=False)
+ @patch("blivet.udev.device_is_dm_mpath", return_value=False)
+ @patch("blivet.udev.device_is_dm_partition", return_value=False)
+ @patch("blivet.udev.device_is_dm_raid", return_value=False)
+ @patch("blivet.udev.device_is_md", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=False)
+ @patch("blivet.udev.device_is_dm", return_value=True)
+ def test_get_helper(self, *args):
+ """Test get_device_helper for dm devices."""
+ device_is_dm = args[0]
+ device_is_dm_lvm = args[6]
+ data = Mock()
+ self.assertEqual(get_device_helper(data), self.helper_class)
+
+ # verify that setting one of the required True return values to False prevents success
+ device_is_dm.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_dm.return_value = True
+
+ # verify that setting one of the required False return values to True prevents success
+ device_is_dm_lvm.return_value = True
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_dm_lvm.return_value = False
+
+ @patch.object(DeviceTree, "get_device_by_name")
+ @patch.object(DMDevice, "status", return_value=True)
+ @patch.object(Populator, "_add_slave_devices")
+ @patch("blivet.udev.device_is_dm_livecd", return_value=False)
+ @patch("blivet.udev.device_get_name")
+ @patch("blivet.udev.device_get_sysfs_path", return_value=sentinel.sysfs_path)
+ def test_run(self, *args):
+ """Test dm device populator."""
+ device_is_dm_livecd = args[2]
+ device_get_name = args[1]
+
+ populator = Populator(devicetree=DeviceTree())
+
+ # The general case for dm devices is that adding the slave/parent devices
+ # will result in the dm device itself being in the tree.
+ device = Mock()
+ populator.devicetree.get_device_by_name.return_value = device
+ data = {"DM_UUID": sentinel.dm_uuid}
+ helper = self.helper_class(populator, data)
+
+ self.assertEqual(helper.run(), device)
+ self.assertEqual(populator._add_slave_devices.call_count, 1)
+ self.assertEqual(populator.devicetree.get_device_by_name.call_count, 1)
+ # since we faked the lookup as if the device was already in the tree
+ # the helper should not have added it, meaning it shouldn't be there
+ self.assertFalse(device in populator.devicetree.devices)
+
+ # The other case is adding a live media image
+ parent = Mock()
+ parent.parents = []
+ populator._add_slave_devices.return_value = [parent]
+ populator.devicetree._add_device(parent)
+ populator.devicetree.get_device_by_name.return_value = None
+ device_name = "livedevice"
+ device_get_name.return_value = device_name
+ device_is_dm_livecd.return_value = True
+
+ device = helper.run()
+ self.assertIsInstance(device, DMDevice)
+ self.assertTrue(device in populator.devicetree.devices)
+ self.assertEqual(device.dm_uuid, sentinel.dm_uuid)
+ self.assertEqual(device.name, device_name)
+ self.assertEqual(device.sysfs_path, sentinel.sysfs_path)
+ self.assertEqual(list(device.parents), [parent])
+
+
+class LoopDevicePopulatorTestCase(PopulatorHelperTestCase):
+ helper_class = LoopDevicePopulator
+
+ @patch("blivet.udev.device_is_loop", return_value=True)
+ def test_match(self, *args):
+ """Test matching of loop device populator."""
+ device_is_loop = args[0]
+ self.assertTrue(self.helper_class.match(None))
+ device_is_loop.return_value = False
+ self.assertFalse(self.helper_class.match(None))
+
+ @patch("blivet.udev.device_is_dm", return_value=False)
+ @patch("blivet.udev.device_is_dm_luks", return_value=False)
+ @patch("blivet.udev.device_is_dm_lvm", return_value=False)
+ @patch("blivet.udev.device_is_dm_mpath", return_value=False)
+ @patch("blivet.udev.device_is_md", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=True)
+ def test_get_helper(self, *args):
+ """Test get_device_helper for loop devices."""
+ device_is_loop = args[0]
+ data = Mock()
+ self.assertEqual(get_device_helper(data), self.helper_class)
+
+ # verify that setting one of the required True return values to False prevents success
+ device_is_loop.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_loop.return_value = True
+
+ # You can't be assured that setting any of the False return values to True will trigger
+ # a failure because the ordering is not complete, meaning any of several device helpers
+ # could be the first helper class checked.
+
+ @patch.object(DeviceTree, "get_device_by_name")
+ @patch.object(FileDevice, "status", return_value=True)
+ @patch.object(LoopDevice, "status", return_value=True)
+ @patch("blivet.populator.helpers.loop.blockdev.loop.get_backing_file")
+ @patch("blivet.udev.device_get_name")
+ @patch("blivet.udev.device_get_sysfs_path", return_value=sentinel.sysfs_path)
+ def test_run(self, *args):
+ """Test loop device populator."""
+ device_get_name = args[1]
+ get_backing_file = args[2]
+
+ populator = Populator(devicetree=DeviceTree())
+ data = Mock()
+
+ # Add backing file and loop device.
+ populator.devicetree.get_device_by_name.return_value = None
+ device_name = "loop3"
+ device_get_name.return_value = device_name
+ backing_file = "/some/file"
+ get_backing_file.return_value = backing_file
+ helper = self.helper_class(populator, data)
+
+ device = helper.run()
+
+ self.assertIsInstance(device, LoopDevice)
+ self.assertTrue(device in populator.devicetree.devices)
+ self.assertTrue(device.exists)
+ self.assertEqual(device.name, device_name)
+ self.assertIsInstance(device.parents[0], FileDevice)
+ self.assertTrue(device.parents[0].exists)
+
+ self.assertEqual(populator.devicetree.get_device_by_name.call_count, 1)
+ populator.devicetree.get_device_by_name.assert_called_with(backing_file)
+
+
+class LVMDevicePopulatorTestCase(PopulatorHelperTestCase):
+ helper_class = LVMDevicePopulator
+
+ @patch("blivet.udev.device_is_dm_lvm", return_value=True)
+ def test_match(self, *args):
+ """Test matching of lvm device populator."""
+ device_is_dm_lvm = args[0]
+ self.assertTrue(self.helper_class.match(None))
+ device_is_dm_lvm.return_value = False
+ self.assertFalse(self.helper_class.match(None))
+
+ @patch("blivet.udev.device_is_dm", return_value=False)
+ @patch("blivet.udev.device_is_dm_mpath", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=False)
+ @patch("blivet.udev.device_is_md", return_value=False)
+ @patch("blivet.udev.device_is_dm_lvm", return_value=True)
+ def test_get_helper(self, *args):
+ """Test get_device_helper for lvm devices."""
+ device_is_dm_lvm = args[0]
+ data = Mock()
+ self.assertEqual(get_device_helper(data), self.helper_class)
+
+ # verify that setting one of the required True return values to False prevents success
+ device_is_dm_lvm.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_dm_lvm.return_value = True
+
+ # You can't be assured that setting any of the False return values to True will trigger
+ # a failure because the ordering is not complete, meaning any of several device helpers
+ # could be the first helper class checked.
+
+ @patch.object(DeviceTree, "get_device_by_name")
+ @patch.object(Populator, "_add_slave_devices")
+ @patch("blivet.udev.device_get_name")
+ @patch("blivet.udev.device_get_lv_vg_name")
+ def test_run(self, *args):
+ """Test lvm device populator."""
+ device_get_lv_vg_name = args[0]
+ device_get_name = args[1]
+ get_device_by_name = args[3]
+
+ populator = Populator(devicetree=DeviceTree())
+ data = Mock()
+
+ # Add slave/parent devices and then look up the device.
+ device_get_name.return_value = sentinel.lv_name
+ populator.devicetree.get_device_by_name.return_value = None
+
+ # pylint: disable=unused-argument
+ def _get_device_by_name(name, **kwargs):
+ if name == sentinel.lv_name:
+ return sentinel.lv_device
+
+ get_device_by_name.side_effect = _get_device_by_name
+ device_get_lv_vg_name.return_value = sentinel.vg_name
+ helper = self.helper_class(populator, data)
+
+ self.assertEqual(helper.run(), sentinel.lv_device)
+ self.assertEqual(populator.devicetree.get_device_by_name.call_count, 3)
+ get_device_by_name.assert_has_calls(
+ [call(sentinel.vg_name, hidden=True),
+ call(sentinel.vg_name),
+ call(sentinel.lv_name)])
+
+ # Add slave/parent devices, but the device is still not in the tree
+ get_device_by_name.side_effect = None
+ get_device_by_name.return_value = None
+ self.assertEqual(helper.run(), None)
+ get_device_by_name.assert_called_with(sentinel.lv_name)
+
+ # A non-vg device with the same name as the vg is already in the tree.
+ # pylint: disable=unused-argument
+ def _get_device_by_name2(name, **kwargs):
+ if name == sentinel.lv_name:
+ return sentinel.lv_device
+ elif name == sentinel.vg_name:
+ return sentinel.non_vg_device
+
+ get_device_by_name.side_effect = _get_device_by_name2
+ with self.assertLogs('blivet', level='WARNING') as log_cm:
+ self.assertEqual(helper.run(), sentinel.lv_device)
+ log_entry = "WARNING:blivet:found non-vg device with name %s" % sentinel.vg_name
+ self.assertTrue(log_entry in log_cm.output)
+
+
+class OpticalDevicePopulatorTestCase(PopulatorHelperTestCase):
+ helper_class = OpticalDevicePopulator
+
+ @patch("blivet.udev.device_is_cdrom", return_value=True)
+ def test_match(self, *args):
+ """Test matching of optical device populator."""
+ device_is_cdrom = args[0]
+ self.assertTrue(self.helper_class.match(None))
+ device_is_cdrom.return_value = False
+ self.assertFalse(self.helper_class.match(None))
+
+ @patch("blivet.udev.device_is_dm", return_value=False)
+ @patch("blivet.udev.device_is_dm_lvm", return_value=False)
+ @patch("blivet.udev.device_is_dm_mpath", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=False)
+ @patch("blivet.udev.device_is_md", return_value=False)
+ @patch("blivet.udev.device_is_cdrom", return_value=True)
+ def test_get_helper(self, *args):
+ """Test get_device_helper for optical devices."""
+ device_is_cdrom = args[0]
+ data = Mock()
+ self.assertEqual(get_device_helper(data), self.helper_class)
+
+ # verify that setting one of the required True return values to False prevents success
+ device_is_cdrom.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_cdrom.return_value = True
+
+ # You can't be assured that setting any of the False return values to True will trigger
+ # a failure because the ordering is not complete, meaning any of several device helpers
+ # could be the first helper class checked.
+
+ @patch("blivet.udev.device_get_major", return_value=99)
+ @patch("blivet.udev.device_get_minor", return_value=17)
+ @patch("blivet.udev.device_get_sysfs_path", return_value='')
+ @patch("blivet.udev.device_get_name")
+ def test_run(self, *args):
+ """Test optical device populator."""
+ device_get_name = args[0]
+
+ populator = Populator(devicetree=DeviceTree())
+ data = Mock()
+
+ helper = self.helper_class(populator, data)
+ device_name = "sr0"
+ device_get_name.return_value = device_name
+
+ device = helper.run()
+ self.assertIsInstance(device, OpticalDevice)
+ self.assertTrue(device.exists)
+ self.assertEqual(device.name, device_name)
+ self.assertTrue(device in populator.devicetree.devices)
+
+
+class PartitionDevicePopulatorTestCase(PopulatorHelperTestCase):
+ """Test partition device populator match method"""
+ helper_class = PartitionDevicePopulator
+
+ @patch("blivet.udev.device_is_dm_partition", return_value=False)
+ @patch("blivet.udev.device_is_partition", return_value=True)
+ def test_match(self, *args):
+ """Test matching for partition device populator."""
+ device_is_partition = args[0]
+ device_is_dm_partition = args[1]
+ self.assertTrue(self.helper_class.match(None))
+ device_is_partition.return_value = False
+ self.assertFalse(self.helper_class.match(None))
+
+ device_is_dm_partition.return_value = True
+ self.assertTrue(self.helper_class.match(None))
+
+ @patch("blivet.udev.device_is_dm", return_value=False)
+ @patch("blivet.udev.device_is_dm_luks", return_value=False)
+ @patch("blivet.udev.device_is_dm_lvm", return_value=False)
+ @patch("blivet.udev.device_is_dm_mpath", return_value=False)
+ @patch("blivet.udev.device_is_dm_partition", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=False)
+ @patch("blivet.udev.device_is_md", return_value=False)
+ @patch("blivet.udev.device_is_partition", return_value=True)
+ def test_get_helper(self, *args):
+ """Test get_device_helper for partitions."""
+ device_is_partition = args[0]
+ device_is_loop = args[2]
+ data = Mock()
+ self.assertEqual(get_device_helper(data), self.helper_class)
+
+ # verify that setting one of the required True return values to False prevents success
+ device_is_partition.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_partition.return_value = True
+
+ # verify that setting one of the required False return values to True prevents success
+ # as of now, loop is always checked before partition
+ device_is_loop.return_value = True
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_loop.return_value = False
+
+ @patch.object(DiskDevice, "partitioned")
+ @patch.object(DiskLabel, "parted_disk")
+ @patch.object(DiskLabel, "parted_device")
+ @patch.object(PartitionDevice, "probe")
+ # TODO: fix the naming of the lvm filter functions
+ @patch("blivet.devicelibs.lvm.lvm_cc_addFilterRejectRegexp")
+ @patch("blivet.udev.device_get_major", return_value=88)
+ @patch("blivet.udev.device_get_minor", return_value=19)
+ @patch.object(DeviceTree, "get_device_by_name")
+ @patch("blivet.udev.device_get_name")
+ @patch("blivet.udev.device_get_partition_disk")
+ def test_run(self, *args):
+ """Test partition device populator."""
+ device_get_partition_disk = args[0]
+ device_get_name = args[1]
+ get_device_by_name = args[2]
+
+ populator = Populator(devicetree=DeviceTree())
+ data = Mock()
+
+ # for every case:
+ # 1. device(s) in tree
+ # 2. lvm filter updated
+ # 3. exceptions raised
+
+ # base case: disk is already in the tree, normal disk
+ fmt = get_format("disklabel", exists=True, device="/dev/xyz")
+ disk = DiskDevice("xyz", fmt=fmt, exists=True)
+ populator.devicetree._add_device(disk)
+
+ device_name = "xyz1"
+ device_get_name.return_value = device_name
+ device_get_partition_disk.return_value = "xyz"
+ get_device_by_name.return_value = disk
+ helper = self.helper_class(populator, data)
+
+ device = helper.run()
+ self.assertIsInstance(device, PartitionDevice)
+ self.assertTrue(device.exists)
+ self.assertEqual(device.name, device_name)
+ self.assertTrue(device in populator.devicetree.devices)
+
+ # TODO: disk not already in the tree
+
+ # TODO: disk not already in tree and attempt to add it failed
+
+ # TODO: md partition
+
+ # TODO: corrupt disklabel
+
+
+class DiskDevicePopulatorTestCase(PopulatorHelperTestCase):
+ helper_class = DiskDevicePopulator
+
+ @patch("os.path.join")
+ @patch("blivet.udev.device_is_cdrom", return_value=False)
+ @patch("blivet.udev.device_is_dm", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=False)
+ @patch("blivet.udev.device_is_md", return_value=False)
+ @patch("blivet.udev.device_is_partition", return_value=False)
+ @patch("blivet.udev.device_is_disk", return_value=True)
+ def test_match(self, *args):
+ """Test matching of disk device populator."""
+ device_is_disk = args[0]
+ self.assertTrue(self.helper_class.match(None))
+ device_is_disk.return_value = False
+ self.assertFalse(self.helper_class.match(None))
+
+ @patch("os.path.join")
+ @patch("blivet.udev.device_is_cdrom", return_value=False)
+ @patch("blivet.udev.device_is_dm", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=False)
+ @patch("blivet.udev.device_is_md", return_value=False)
+ @patch("blivet.udev.device_is_partition", return_value=False)
+ @patch("blivet.udev.device_is_disk", return_value=True)
+ def test_get_helper(self, *args):
+ """Test get_device_helper for disks."""
+ device_is_disk = args[0]
+ device_is_loop = args[3]
+ data = {"DM_NAME": None}
+ self.assertEqual(get_device_helper(data), self.helper_class)
+
+ # verify that setting one of the required True return values to False prevents success
+ device_is_disk.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_disk.return_value = True
+
+ # verify that setting one of the required False return values to True prevents success
+ # as of now, loop is always checked before partition
+ device_is_loop.return_value = True
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_loop.return_value = False
+
+ @patch("blivet.udev.device_get_major", return_value=99)
+ @patch("blivet.udev.device_get_minor", return_value=222)
+ @patch("blivet.udev.device_get_name")
+ def test_run(self, *args):
+ """Test disk device populator."""
+ device_get_name = args[0]
+
+ populator = Populator(devicetree=DeviceTree())
+ data = Mock()
+
+ device_name = "nop"
+ device_get_name.return_value = device_name
+ helper = self.helper_class(populator, data)
+
+ device = helper.run()
+ self.assertIsInstance(device, DiskDevice)
+ self.assertTrue(device.exists)
+ self.assertTrue(device.is_disk)
+ self.assertEqual(device.name, device_name)
+ self.assertTrue(device in populator.devicetree.devices)
+
+
+class MDDevicePopulatorTestCase(PopulatorHelperTestCase):
+ helper_class = MDDevicePopulator
+
+ @patch("blivet.udev.device_get_md_container", return_value=None)
+ @patch("blivet.udev.device_is_md", return_value=True)
+ def test_match(self, *args):
+ """Test matching of md device populator."""
+ device_is_md = args[0]
+ device_get_md_container = args[1]
+
+ self.assertEqual(self.helper_class.match(None), True)
+
+ device_is_md.return_value = False
+ device_get_md_container.return_value = None
+ self.assertEqual(self.helper_class.match(None), False)
+
+ device_is_md.return_value = True
+ device_get_md_container.return_value = True
+ self.assertEqual(self.helper_class.match(None), False)
+
+ device_is_md.return_value = False
+ device_get_md_container.return_value = True
+ self.assertEqual(self.helper_class.match(None), False)
+
+ @patch("blivet.udev.device_is_cdrom", return_value=False)
+ @patch("blivet.udev.device_is_disk", return_value=False)
+ @patch("blivet.udev.device_is_dm", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=False)
+ @patch("blivet.udev.device_is_partition", return_value=False)
+ @patch("blivet.udev.device_get_md_container", return_value=None)
+ @patch("blivet.udev.device_is_md", return_value=True)
+ def test_get_helper(self, *args):
+ """Test get_device_helper for md arrays."""
+ device_is_md = args[0]
+
+ data = dict()
+ self.assertEqual(get_device_helper(data), self.helper_class)
+
+ # verify that setting one of the required True return values to False prevents success
+ device_is_md.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_md.return_value = True
+
+ # You can't be assured that setting any of the False return values to True will trigger
+ # a failure because the ordering is not complete, meaning any of several device helpers
+ # could be the first helper class checked.
+
+ @patch.object(DeviceTree, "get_device_by_name")
+ @patch.object(Populator, "_add_slave_devices")
+ @patch("blivet.udev.device_get_name")
+ @patch("blivet.udev.device_get_md_uuid")
+ @patch("blivet.udev.device_get_md_name")
+ def test_run(self, *args):
+ """Test md device populator."""
+ device_get_md_name = args[0]
+ get_device_by_name = args[4]
+
+ populator = Populator(devicetree=DeviceTree())
+
+ # base case: _add_slave_devices gets the array into the tree
+ data = Mock()
+ device = Mock()
+ device.parents = []
+
+ device_name = "mdtest"
+ device_get_md_name.return_value = device_name
+ get_device_by_name.return_value = device
+ helper = self.helper_class(populator, data)
+
+ self.assertEqual(helper.run(), device)
+
+
+class MultipathDevicePopulatorTestCase(PopulatorHelperTestCase):
+ helper_class = MultipathDevicePopulator
+ match_auto_patches = ["blivet.udev.device_is_dm_mpath", "blivet.udev.device_is_dm_partition"]
+
+ @patch("blivet.udev.device_is_dm_partition", return_value=False)
+ @patch("blivet.udev.device_is_dm_mpath", return_value=True)
+ def test_match(self, *args):
+ """Test matching of multipath device populator."""
+ device_is_dm_mpath = args[0]
+ device_is_dm_partition = args[1]
+
+ device_is_dm_partition.return_value = False
+ self.assertEqual(MultipathDevicePopulator.match(None), True)
+
+ device_is_dm_mpath.return_value = True
+ device_is_dm_partition.return_value = True
+ self.assertEqual(MultipathDevicePopulator.match(None), False)
+
+ device_is_dm_mpath.return_value = False
+ device_is_dm_partition.return_value = True
+ self.assertEqual(MultipathDevicePopulator.match(None), False)
+
+ device_is_dm_mpath.return_value = False
+ device_is_dm_partition.return_value = False
+ self.assertEqual(MultipathDevicePopulator.match(None), False)
+
+ @patch("blivet.udev.device_is_cdrom", return_value=False)
+ @patch("blivet.udev.device_is_loop", return_value=False)
+ @patch("blivet.udev.device_is_partition", return_value=False)
+ @patch("blivet.udev.device_is_md", return_value=False)
+ @patch("blivet.udev.device_is_dm_partition", return_value=False)
+ @patch("blivet.udev.device_is_dm", return_value=True)
+ @patch("blivet.udev.device_is_dm_mpath", return_value=True)
+ def test_get_helper(self, *args):
+ """Test get_device_helper for multipaths."""
+ device_is_dm_mpath = args[0]
+
+ data = dict()
+ self.assertEqual(get_device_helper(data), self.helper_class)
+
+ # verify that setting one of the required True return values to False prevents success
+ device_is_dm_mpath.return_value = False
+ self.assertNotEqual(get_device_helper(data), self.helper_class)
+ device_is_dm_mpath.return_value = True
+
+ # You can't be assured that setting any of the False return values to True will trigger
+ # a failure because the ordering is not complete, meaning any of several device helpers
+ # could be the first helper class checked.
+
+ @patch("blivet.udev.device_get_sysfs_path")
+ @patch.object(Populator, "_add_slave_devices")
+ @patch("blivet.udev.device_get_name")
+ def test_run(self, *args):
+ """Test multipath device populator."""
+ device_get_name = args[0]
+ add_slave_devices = args[1]
+
+ populator = Populator(devicetree=DeviceTree())
+ data = {"DM_UUID": "1-2-3-4"}
+
+ device_name = "mpathtest"
+ device_get_name.return_value = device_name
+ slave_1 = Mock()
+ slave_1.parents = []
+ slave_2 = Mock()
+ slave_2.parents = []
+ populator.devicetree._add_device(slave_1)
+ populator.devicetree._add_device(slave_2)
+ add_slave_devices.return_value = [slave_1, slave_2]
+
+ helper = self.helper_class(populator, data)
+
+ device = helper.run()
+ self.assertIsInstance(device, MultipathDevice)
+ self.assertTrue(device.exists)
+ self.assertEqual(device.name, device_name)
+ self.assertTrue(device in populator.devicetree.devices)
+
+
+class FormatPopulatorTestCase(PopulatorHelperTestCase):
+ """Format types that don't require special handling use FormatPopulator."""
+ helper_class = FormatPopulator
+ udev_type = None
+ blivet_type = None
+
+ @property
+ def helper_name(self):
+ return self.helper_class.__name__
+
+ def test_match(self):
+ if self.udev_type is None:
+ return
+
+ data = dict()
+ device = Mock()
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ self.assertTrue(self.helper_class.match(data, device),
+ msg="Failed to match %s against %s" % (self.udev_type, self.helper_name))
+
+ @patch("blivet.populator.helpers.disklabel.blockdev.mpath_is_mpath_member", return_value=False)
+ @patch("blivet.udev.device_is_partition", return_value=False)
+ @patch("blivet.udev.device_is_dm_partition", return_value=False)
+ # pylint: disable=unused-argument
+ def test_get_helper(self, *args):
+ if self.udev_type is None:
+ return
+
+ data = dict()
+ device = Mock()
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ self.assertEqual(get_format_helper(data, device),
+ self.helper_class,
+ msg="get_format_helper failed for %s" % self.udev_type)
+
+ def test_run(self):
+ if self.udev_type is None:
+ return
+
+ populator = Populator(devicetree=DeviceTree())
+ data = dict()
+ device = Mock()
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ helper = self.helper_class(populator, data, device)
+ helper.run()
+ self.assertEqual(device.format.type,
+ self.blivet_type,
+ msg="FormatPopulator.run failed for %s" % self.udev_type)
+
+
+class Ext4PopulatorTestCase(FormatPopulatorTestCase):
+ """Test ext4 format populator."""
+ udev_type = blivet_type = "ext4"
+
+
+class XFSPopulatorTestCase(FormatPopulatorTestCase):
+ udev_type = blivet_type = "xfs"
+
+
+class SwapPopulatorTestCase(FormatPopulatorTestCase):
+ udev_type = blivet_type = "swap"
+
+
+class VFATPopulatorTestCase(FormatPopulatorTestCase):
+ udev_type = blivet_type = "vfat"
+
+
+class HFSPopulatorTestCase(FormatPopulatorTestCase):
+ udev_type = blivet_type = "hfs"
+
+
+class DiskLabelPopulatorTestCase(PopulatorHelperTestCase):
+ helper_class = DiskLabelFormatPopulator
+
+ @patch("blivet.populator.helpers.disklabel.blockdev.mpath_is_mpath_member", return_value=False)
+ @patch("blivet.udev.device_is_biosraid_member", return_value=False)
+ @patch("blivet.udev.device_get_format", return_value=None)
+ @patch("blivet.udev.device_get_disklabel_type", return_value="dos")
+ def test_match(self, *args):
+ """Test matching for disklabel format populator."""
+ device_get_disklabel_type = args[0]
+ device_get_format = args[1]
+ device_is_biosraid_member = args[2]
+ is_mpath_member = args[3]
+
+ device = Mock()
+ device.is_disk = True
+
+ data = Mock()
+
+ self.assertTrue(self.helper_class.match(data, device))
+
+ # ID_PART_TABLE_TYPE is required in udev data
+ device_get_disklabel_type.return_value = None
+ self.assertFalse(self.helper_class.match(data, device))
+ device_get_disklabel_type.return_value = "dos"
+
+ # no match for whole-disk iso9660 filesystems (isohybrid media)
+ device_get_format.return_value = "iso9660"
+ self.assertFalse(self.helper_class.match(data, device))
+ device_get_format.return_value = None
+
+ # no match for biosraid members
+ device_is_biosraid_member.return_value = True
+ self.assertFalse(self.helper_class.match(data, device))
+ device_is_biosraid_member.return_value = False
+
+ # no match for multipath members
+ is_mpath_member.return_value = True
+ self.assertFalse(self.helper_class.match(data, device))
+ is_mpath_member.return_value = False
+
+ @patch("blivet.populator.helpers.disklabel.blockdev.mpath_is_mpath_member", return_value=False)
+ @patch("blivet.udev.device_is_biosraid_member", return_value=False)
+ @patch("blivet.udev.device_get_format", return_value=None)
+ @patch("blivet.udev.device_get_disklabel_type", return_value="dos")
+ def test_get_helper(self, *args):
+ """Test get_format_helper for disklabels."""
+ device_get_disklabel_type = args[0]
+
+ device = Mock()
+ device.is_disk = True
+
+ data = Mock()
+
+ self.assertEqual(get_format_helper(data, device), self.helper_class)
+
+ # no disklabel type reported by udev/blkid -> get_format_helper does not return
+ # disklabel helper
+ device_get_disklabel_type.return_value = None
+ self.assertNotEqual(get_format_helper(data, device), self.helper_class)
+ device_get_disklabel_type.return_value = "dos"
+
+
+class LVMFormatPopulatorTestCase(FormatPopulatorTestCase):
+ helper_class = LVMFormatPopulator
+ udev_type = "LVM2_member"
+ blivet_type = "lvmpv"
+
+ def _clean_up(self):
+ blockdev.lvm.pvs = self._pvs
+
+ @patch("blivet.udev.device_get_name")
+ @patch.object(DeviceFormat, "_device_check", return_value=None)
+ @patch.object(DeviceTree, "get_device_by_uuid")
+ def test_run(self, *args):
+ """Test lvm format populator."""
+ get_device_by_uuid = args[0]
+
+ populator = Populator(devicetree=DeviceTree())
+ data = dict()
+ device = Mock()
+ device.parents = []
+ device.size = Size("10g")
+ populator.devicetree._add_device(device)
+
+ # pylint: disable=attribute-defined-outside-init
+ self._pvs = blockdev.lvm.pvs
+ blockdev.lvm.pvs = Mock(return_value={})
+ self.addCleanup(self._clean_up)
+
+ # base case: pv format with no vg
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ helper = self.helper_class(populator, data, device)
+ helper.run()
+ self.assertEqual(device.format.type,
+ self.blivet_type,
+ msg="Wrong format type after FormatPopulator.run on %s" % self.udev_type)
+
+ # pv belongs to a valid vg which is already in the tree (no lvs)
+ pv_info = Mock()
+
+ pv_info.vg_name = "testvgname"
+ pv_info.vg_uuid = sentinel.vg_uuid
+ pv_info.pe_start = 0
+
+ populator.devicetree._pvs_cache = dict()
+ populator.devicetree._pvs_cache[sentinel.pv_path] = pv_info
+ device.path = sentinel.pv_path
+
+ vg_device = Mock()
+ get_device_by_uuid.return_value = vg_device
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ helper = self.helper_class(populator, data, device)
+ helper.run()
+ self.assertEqual(device.format.type,
+ self.blivet_type,
+ msg="Wrong format type after FormatPopulator.run on %s" % self.udev_type)
+
+ self.assertEqual(get_device_by_uuid.call_count, 1)
+ get_device_by_uuid.assert_called_with(pv_info.vg_uuid, incomplete=True)
+ vg_device.parents.append.assert_called_once_with(device)
+
+ get_device_by_uuid.reset_mock()
+ get_device_by_uuid.return_value = None
+
+ # pv belongs to a valid vg which is not in the tree (no lvs, either)
+ pv_info.vg_size = "10g"
+ pv_info.vg_free = 0
+ pv_info.vg_extent_size = "4m"
+ pv_info.vg_extent_count = 2500
+ pv_info.vg_free_count = 0
+ pv_info.vg_pv_count = 1
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ helper = self.helper_class(populator, data, device)
+ helper.run()
+ self.assertEqual(device.format.type,
+ self.blivet_type,
+ msg="Wrong format type after FormatPopulator.run on %s" % self.udev_type)
+
+ self.assertEqual(get_device_by_uuid.call_count, 1)
+ get_device_by_uuid.assert_called_with(pv_info.vg_uuid, incomplete=True)
+ vg_device = populator.devicetree.get_device_by_name(pv_info.vg_name)
+ self.assertTrue(vg_device is not None)
+ populator.devicetree._remove_device(vg_device)
+
+ get_device_by_uuid.reset_mock()
+
+ # pv belongs to a valid vg not in the tree with two lvs
+ lv1 = Mock()
+ lv1.vg_name = pv_info.vg_name
+ lv1.lv_name = "testlv1"
+ lv1.uuid = sentinel.lv1_uuid
+ lv1.attr = "-wi-ao----"
+ lv1.size = "2g"
+ lv1.segtype = "linear"
+ lv1_name = "%s-%s" % (pv_info.vg_name, lv1.lv_name)
+
+ lv2 = Mock()
+ lv2.vg_name = pv_info.vg_name
+ lv2.lv_name = "testlv2"
+ lv2.uuid = sentinel.lv2_uuid
+ lv2.attr = "-wi-ao----"
+ lv2.size = "7g"
+ lv2.segtype = "linear"
+ lv2_name = "%s-%s" % (pv_info.vg_name, lv2.lv_name)
+
+ lv_info = {lv1_name: lv1,
+ lv2_name: lv2}
+ populator.devicetree._lvs_cache = lv_info
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ helper = self.helper_class(populator, data, device)
+ helper.run()
+ self.assertEqual(device.format.type,
+ self.blivet_type,
+ msg="Wrong format type after FormatPopulator.run on %s" % self.udev_type)
+
+ self.assertEqual(get_device_by_uuid.call_count, 3) # one for vg and one for each lv
+ get_device_by_uuid.assert_has_calls([call(pv_info.vg_uuid, incomplete=True),
+ call(lv1.uuid),
+ call(lv2.uuid)],
+ any_order=True)
+ vg_device = populator.devicetree.get_device_by_name(pv_info.vg_name)
+ self.assertTrue(vg_device is not None)
+
+ lv1_device = populator.devicetree.get_device_by_name(lv1_name)
+ self.assertEqual(lv1_device.uuid, lv1.uuid)
+ lv2_device = populator.devicetree.get_device_by_name(lv2_name)
+ self.assertEqual(lv2_device.uuid, lv2.uuid)
+
+
+class MDFormatPopulatorTestCase(FormatPopulatorTestCase):
+ helper_class = MDFormatPopulator
+ udev_type = "linux_raid_member"
+ blivet_type = "mdmember"
+
+ def _clean_up(self):
+ blockdev.md.examine = self._examine
+
+ @patch("blivet.udev.device_get_name")
+ @patch("blivet.util.canonicalize_UUID", side_effect=lambda x: x)
+ @patch.object(MDRaidArrayDevice, "mdadm_format_uuid", None)
+ @patch("blivet.udev.device_is_md")
+ @patch("blivet.udev.get_devices")
+ @patch.object(DeviceTree, "get_device_by_uuid")
+ def test_run(self, *args):
+ """Test md format populator."""
+ get_device_by_uuid = args[0]
+ get_devices = args[1]
+ device_is_md = args[2]
+
+ populator = Populator(devicetree=DeviceTree())
+ data = dict()
+ device = Mock()
+ device.name = sentinel.dev1_name
+ device.parents = []
+ device.size = Size("10g")
+ populator.devicetree._add_device(device)
+
+ # pylint: disable=attribute-defined-outside-init
+ self._examine = blockdev.md.examine
+ blockdev.md.examine = Mock()
+ self.addCleanup(self._clean_up)
+
+ # member belongs to a valid array which is already in the tree
+ md_info = Mock()
+ md_info.uuid = sentinel.md_uuid
+ blockdev.md.examine.return_value = md_info
+
+ md_device = Mock()
+ get_device_by_uuid.return_value = md_device
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ helper = self.helper_class(populator, data, device)
+ helper.run()
+ self.assertEqual(device.format.type,
+ self.blivet_type,
+ msg="Wrong format type after FormatPopulator.run on %s" % self.udev_type)
+
+ self.assertEqual(get_device_by_uuid.call_count, 1)
+ get_device_by_uuid.assert_called_with(md_info.uuid, incomplete=True)
+ md_device.parents.append.assert_called_once_with(device)
+
+ get_device_by_uuid.reset_mock()
+ get_device_by_uuid.return_value = None
+
+ # first of two members belonging to a valid array which is not in the tree
+ array_name = "mdtest"
+ md_info.level = "raid1"
+ md_info.num_devices = 2
+ md_info.metadata = "1.2"
+ md_info.device = "/dev/md/" + array_name
+ blockdev.md.examine.return_value = md_info
+
+ device_is_md.return_value = True
+ md_udev = {"MD_LEVEL": md_info.level, "MD_UUID": sentinel.md_uuid, "MD_DEVNAME": array_name}
+ get_devices.return_value = [md_udev]
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ helper = self.helper_class(populator, data, device)
+ helper.run()
+ self.assertEqual(device.format.type,
+ self.blivet_type,
+ msg="Wrong format type after FormatPopulator.run on %s" % self.udev_type)
+
+ self.assertEqual(get_device_by_uuid.call_count, 1)
+ get_device_by_uuid.assert_called_with(md_info.uuid, incomplete=True)
+ array = populator.devicetree.get_device_by_name(array_name)
+ self.assertTrue(array is None)
+ array = populator.devicetree.get_device_by_name(array_name, incomplete=True)
+ self.assertTrue(array is not None)
+ self.assertFalse(array.complete)
+ self.assertEqual(array.name, array_name)
+
+ get_device_by_uuid.reset_mock()
+ array = populator.devicetree.get_device_by_name(array_name, incomplete=True)
+ get_device_by_uuid.return_value = array
+
+ # second of two members belonging to a valid array
+ device2 = Mock()
+ device2.name = sentinel.dev2_name
+ device2.parents = []
+ device2.size = Size("10g")
+ populator.devicetree._add_device(device2)
+
+ with patch("blivet.udev.device_get_format", return_value=self.udev_type):
+ helper = self.helper_class(populator, data, device2)
+ helper.run()
+ self.assertEqual(device2.format.type,
+ self.blivet_type,
+ msg="Wrong format type after FormatPopulator.run on %s" % self.udev_type)
+
+ self.assertEqual(get_device_by_uuid.call_count, 1) # one for the array
+ get_device_by_uuid.assert_called_with(md_info.uuid, incomplete=True)
+
+ array = populator.devicetree.get_device_by_name(array_name)
+ self.assertTrue(array is not None)
+ self.assertTrue(array.complete)
+ self.assertEqual(array.name, array_name)
+
+
+class BootFormatPopulatorTestCase(PopulatorHelperTestCase):
+ def test_match(self):
+ """Test boot format populator helper match method"""
+ if self.helper_class is None:
+ return
+
+ partition = PartitionDevice("testpartitiondev")
+ storagedev = StorageDevice("teststoragedev")
+ storagedev.bootable = True
+ data = dict()
+
+ fmt_class = get_device_format_class(self.helper_class._type_specifier)
+ if fmt_class is None:
+ self.skipTest("failed to look up format class for %s" % self.helper_class._type_specifier)
+
+ data["ID_FS_TYPE"] = self.helper_class._base_type_specifier
+ partition._bootable = self.helper_class._bootable
+ min_size = fmt_class._min_size
+ max_size = fmt_class._max_size
+ partition._size = min_size
+ storagedev._size = min_size
+
+ self.assertTrue(self.helper_class.match(data, partition))
+
+ # These are only valid for partitions.
+ self.assertFalse(self.helper_class.match(data, storagedev))
+
+ data["ID_FS_TYPE"] += "x"
+ self.assertFalse(self.helper_class.match(data, partition))
+ data["ID_FS_TYPE"] = self.helper_class._base_type_specifier
+
+ if self.helper_class._bootable:
+ partition._bootable = False
+ self.assertFalse(self.helper_class.match(data, partition))
+ partition._bootable = True
+
+ if max_size:
+ partition._size = max_size + 1
+ elif min_size:
+ partition._size = min_size - 1
+
+ self.assertFalse(self.helper_class.match(data, partition))
+ partition._size = min_size
+
+ @patch("blivet.udev.device_get_disklabel_type", return_value=None)
+ # pylint: disable=unused-argument
+ def test_get_helper(self, *args):
+ if self.helper_class is None:
+ return
+
+ partition = PartitionDevice("testpartitiondev")
+ data = dict()
+
+ fmt_class = get_device_format_class(self.helper_class._type_specifier)
+ if fmt_class is None:
+ self.skipTest("failed to look up format class for %s" % self.helper_class._type_specifier)
+
+ data["ID_FS_TYPE"] = self.helper_class._base_type_specifier
+ data["DEVTYPE"] = "partition"
+ partition._bootable = self.helper_class._bootable
+ partition._size = fmt_class._min_size
+ self.assertEqual(get_format_helper(data, partition), self.helper_class)
+
+
+class EFIFormatPopulatorTestCase(BootFormatPopulatorTestCase):
+ helper_class = EFIFormatPopulator
+
+
+class MacEFIFormatPopulatorTestCase(BootFormatPopulatorTestCase):
+ helper_class = MacEFIFormatPopulator
+
+
+class AppleBootFormatPopulatorTestCase(BootFormatPopulatorTestCase):
+ helper_class = AppleBootFormatPopulator
+
+
if __name__ == "__main__":
unittest.main()
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/98d03c658987aaa4e46c52976918...
8 years, 5 months
[master 10/27] Use helper getter functions to find populator helpers.
by dashea
From: David Lehman <dlehman(a)redhat.com>
---
blivet/populator/helpers/formatpopulator.py | 2 --
blivet/populator/populator.py | 34 ++++++-----------------------
2 files changed, 7 insertions(+), 29 deletions(-)
diff --git a/blivet/populator/helpers/formatpopulator.py b/blivet/populator/helpers/formatpopulator.py
index ea4b3d3..6eb0ffc 100644
--- a/blivet/populator/helpers/formatpopulator.py
+++ b/blivet/populator/helpers/formatpopulator.py
@@ -89,8 +89,6 @@ def run(self):
try:
log.info("type detected on '%s' is '%s'", self.device.name, type_spec)
self.device.format = formats.get_format(type_spec, **kwargs)
- if self.device.format.type:
- log.info("got format: %s", self.device.format)
except FSError:
log.warning("type '%s' on '%s' invalid, assuming no format",
type_spec, self.device.name)
diff --git a/blivet/populator/populator.py b/blivet/populator/populator.py
index 8341393..4592b3b 100644
--- a/blivet/populator/populator.py
+++ b/blivet/populator/populator.py
@@ -43,6 +43,7 @@
from ..util import open # pylint: disable=redefined-builtin
from ..flags import flags
from ..storage_log import log_method_call
+from .helpers import get_device_helper, get_format_helper
import logging
log = logging.getLogger("blivet")
@@ -357,33 +358,8 @@ def add_udev_device(self, info, update_orig_fmt=False):
helper_class = None
if device:
device_added = False
- elif udev.device_is_loop(info):
- log.info("%s is a loop device", name)
- device = LoopDevicePopulator(self, info).run()
- elif udev.device_is_dm_mpath(info) and \
- not udev.device_is_dm_partition(info):
- log.info("%s is a multipath device", name)
- helper_class = MultipathDevicePopulator
- elif udev.device_is_dm_lvm(info):
- log.info("%s is an lvm logical volume", name)
- helper_class = LVMDevicePopulator
- elif udev.device_is_dm(info):
- log.info("%s is a device-mapper device", name)
- helper_class = DMDevicePopulator
- elif udev.device_is_md(info) and not udev.device_get_md_container(info):
- log.info("%s is an md device", name)
- helper_class = MDDevicePopulator
- elif udev.device_is_cdrom(info):
- log.info("%s is a cdrom", name)
- helper_class = OpticalDevicePopulator
- elif udev.device_is_disk(info):
- helper_class = DiskDevicePopulator
- elif udev.device_is_partition(info):
- log.info("%s is a partition", name)
- helper_class = PartitionDevicePopulator
else:
- log.error("Unknown block device type for: %s", name)
- return
+ helper_class = get_device_helper(info)
if helper_class is not None:
device = helper_class(self, info).run()
@@ -443,7 +419,11 @@ def handle_udev_device_format(self, info, device):
log.debug("no type or existing type for %s, bailing", name)
return
- # TODO: set up the formatting
+ helper_class = get_format_helper(info, device)
+ if helper_class is not None:
+ helper_class(self, info, device).run()
+
+ log.info("got format: %s", device.format)
def update_device_format(self, device):
log.info("updating format of device: %s", device)
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/b4654b2ac0549298978af122166e...
8 years, 5 months
[master 9/27] Add functions to return a helper class based on device
data.
by dashea
From: David Lehman <dlehman(a)redhat.com>
This is intended to be the complete API of populator.helpers.
---
blivet/populator/helpers/__init__.py | 48 ++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/blivet/populator/helpers/__init__.py b/blivet/populator/helpers/__init__.py
index 4a8069f..2f01f6e 100644
--- a/blivet/populator/helpers/__init__.py
+++ b/blivet/populator/helpers/__init__.py
@@ -1,3 +1,8 @@
+import inspect as _inspect
+
+from .devicepopulator import DevicePopulator
+from .formatpopulator import FormatPopulator
+
from .btrfs import BTRFSFormatPopulator
from .boot import AppleBootFormatPopulator, EFIFormatPopulator, MacEFIFormatPopulator
from .disk import DiskDevicePopulator
@@ -11,3 +16,46 @@
from .multipath import MultipathDevicePopulator
from .optical import OpticalDevicePopulator
from .partition import PartitionDevicePopulator
+
+__all__ = ["get_device_helper", "get_format_helper"]
+
+_device_helpers = []
+_format_helpers = []
+
+
+def _build_helper_lists():
+ """Build lists of known device and format helper classes."""
+ global _device_helpers # pylint: disable=global-variable-undefined
+ global _format_helpers # pylint: disable=global-variable-undefined
+ _device_helpers = []
+ _format_helpers = []
+ for obj in globals().values():
+ if not _inspect.isclass(obj):
+ continue
+ elif issubclass(obj, DevicePopulator):
+ _device_helpers.append(obj)
+ elif issubclass(obj, FormatPopulator):
+ _format_helpers.append(obj)
+
+ _device_helpers.sort(key=lambda h: h.priority, reverse=True)
+ _format_helpers.sort(key=lambda h: h.priority, reverse=True)
+
+_build_helper_lists()
+
+
+def get_device_helper(data):
+ """ Return the device helper class appropriate for the specified data.
+
+ The helper lists are sorted according to priorities defined within each
+ class. This function returns the first matching class.
+ """
+ return next((h for h in _device_helpers if h.match(data)), None)
+
+
+def get_format_helper(data, device):
+ """ Return the device helper class appropriate for the specified data.
+
+ The helper lists are sorted according to priorities defined within each
+ class. This function returns the first matching class.
+ """
+ return next((h for h in _format_helpers if h.match(data, device=device)), None)
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/aded1c825385817dc39d8ff99c16...
8 years, 5 months
[master 8/27] Move format-type-specific bits into populator helpers.
by dashea
From: David Lehman <dlehman(a)redhat.com>
Disklabels are now handled using the same mechanism as all other
formats.
---
blivet/populator/helpers/__init__.py | 9 +-
blivet/populator/helpers/boot.py | 61 +++
blivet/populator/helpers/btrfs.py | 104 +++++
blivet/populator/helpers/disklabel.py | 101 +++++
blivet/populator/helpers/dmraid.py | 83 ++++
blivet/populator/helpers/luks.py | 103 +++++
blivet/populator/helpers/lvm.py | 300 +++++++++++++-
blivet/populator/helpers/mdraid.py | 126 ++++++
blivet/populator/helpers/multipath.py | 15 +
blivet/populator/populator.py | 723 +---------------------------------
10 files changed, 906 insertions(+), 719 deletions(-)
create mode 100644 blivet/populator/helpers/boot.py
create mode 100644 blivet/populator/helpers/btrfs.py
create mode 100644 blivet/populator/helpers/disklabel.py
create mode 100644 blivet/populator/helpers/dmraid.py
create mode 100644 blivet/populator/helpers/luks.py
diff --git a/blivet/populator/helpers/__init__.py b/blivet/populator/helpers/__init__.py
index b90a5a5..4a8069f 100644
--- a/blivet/populator/helpers/__init__.py
+++ b/blivet/populator/helpers/__init__.py
@@ -1,8 +1,13 @@
+from .btrfs import BTRFSFormatPopulator
+from .boot import AppleBootFormatPopulator, EFIFormatPopulator, MacEFIFormatPopulator
from .disk import DiskDevicePopulator
+from .disklabel import DiskLabelFormatPopulator
from .dm import DMDevicePopulator
+from .dmraid import DMRaidFormatPopulator
from .loop import LoopDevicePopulator
-from .lvm import LVMDevicePopulator
-from .mdraid import MDDevicePopulator
+from .luks import LUKSFormatPopulator
+from .lvm import LVMDevicePopulator, LVMFormatPopulator
+from .mdraid import MDDevicePopulator, MDFormatPopulator
from .multipath import MultipathDevicePopulator
from .optical import OpticalDevicePopulator
from .partition import PartitionDevicePopulator
diff --git a/blivet/populator/helpers/boot.py b/blivet/populator/helpers/boot.py
new file mode 100644
index 0000000..b679b3b
--- /dev/null
+++ b/blivet/populator/helpers/boot.py
@@ -0,0 +1,61 @@
+# populator/helpers/boot.py
+# Platform-specific boot format helpers for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from ... import formats
+from ... import udev
+from ...devices import PartitionDevice
+
+from .formatpopulator import FormatPopulator
+
+
+class BootFormatPopulator(FormatPopulator):
+ priority = 500
+ _base_type_specifier = None
+ _bootable = False
+
+ @classmethod
+ def match(cls, data, device):
+ if cls == BootFormatPopulator:
+ return False
+
+ fmt = formats.get_format(cls._type_specifier)
+ return (udev.device_get_format(data) == cls._base_type_specifier and
+ isinstance(device, PartitionDevice) and
+ (device.bootable or not cls._bootable) and
+ fmt.min_size <= device.size <= fmt.max_size)
+
+
+class EFIFormatPopulator(BootFormatPopulator):
+ _type_specifier = "efi"
+ _base_type_specifier = "vfat"
+ _bootable = True
+
+
+class MacEFIFormatPopulator(BootFormatPopulator):
+ _type_specifier = "macefi"
+ _base_type_specifier = "hfsplus"
+
+
+class AppleBootFormatPopulator(BootFormatPopulator):
+ _type_specifier = "appleboot"
+ _base_type_specifier = "hfs"
+ _bootable = True
diff --git a/blivet/populator/helpers/btrfs.py b/blivet/populator/helpers/btrfs.py
new file mode 100644
index 0000000..b93e262
--- /dev/null
+++ b/blivet/populator/helpers/btrfs.py
@@ -0,0 +1,104 @@
+# populator/helpers/btrfs.py
+# BTTRFS backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from ... import formats
+from ... import udev
+from ...devices import BTRFSVolumeDevice, BTRFSSnapShotDevice, BTRFSSubVolumeDevice
+from ...errors import DeviceTreeError
+from .formatpopulator import FormatPopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class BTRFSFormatPopulator(FormatPopulator):
+ priority = 100
+ _type_specifier = "btrfs"
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+ # the format's uuid attr will contain the UUID_SUB, while the
+ # overarching volume UUID will be stored as vol_uuid
+ kwargs["uuid"] = self.data["ID_FS_UUID_SUB"]
+ kwargs["vol_uuid"] = udev.device_get_uuid(self.data)
+ return kwargs
+
+ def run(self):
+ super().run()
+ uuid = udev.device_get_uuid(self.data)
+
+ btrfs_dev = None
+ for d in self._populator.devicetree.devices:
+ if isinstance(d, BTRFSVolumeDevice) and d.uuid == uuid:
+ btrfs_dev = d
+ break
+
+ if btrfs_dev:
+ log.info("found btrfs volume %s", btrfs_dev.name)
+ btrfs_dev.parents.append(self.device)
+ else:
+ label = udev.device_get_label(self.data)
+ log.info("creating btrfs volume btrfs.%s", label)
+ btrfs_dev = BTRFSVolumeDevice(label, parents=[self.device], uuid=uuid,
+ exists=True)
+ self._populator.devicetree._add_device(btrfs_dev)
+
+ if not btrfs_dev.subvolumes:
+ snapshots = btrfs_dev.list_subvolumes(snapshots_only=True)
+ snapshot_ids = [s.id for s in snapshots]
+ for subvol_dict in btrfs_dev.list_subvolumes():
+ vol_id = subvol_dict.id
+ vol_path = subvol_dict.path
+ parent_id = subvol_dict.parent_id
+ if vol_path in [sv.name for sv in btrfs_dev.subvolumes]:
+ continue
+
+ # look up the parent subvol
+ parent = None
+ subvols = [btrfs_dev] + btrfs_dev.subvolumes
+ for sv in subvols:
+ if sv.vol_id == parent_id:
+ parent = sv
+ break
+
+ if parent is None:
+ log.error("failed to find parent (%d) for subvol %s",
+ parent_id, vol_path)
+ raise DeviceTreeError("could not find parent for subvol")
+
+ fmt = formats.get_format("btrfs",
+ device=btrfs_dev.path,
+ exists=True,
+ vol_uuid=btrfs_dev.format.vol_uuid,
+ subvolspec=vol_path,
+ mountopts="subvol=%s" % vol_path)
+ if vol_id in snapshot_ids:
+ device_class = BTRFSSnapShotDevice
+ else:
+ device_class = BTRFSSubVolumeDevice
+
+ subvol = device_class(vol_path,
+ vol_id=vol_id,
+ fmt=fmt,
+ parents=[parent],
+ exists=True)
+ self._populator.devicetree._add_device(subvol)
diff --git a/blivet/populator/helpers/disklabel.py b/blivet/populator/helpers/disklabel.py
new file mode 100644
index 0000000..e9aa8b2
--- /dev/null
+++ b/blivet/populator/helpers/disklabel.py
@@ -0,0 +1,101 @@
+# populator/helpers/disklabel.py
+# Disklabel backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+import gi
+gi.require_version("BlockDev", "1.0")
+
+from gi.repository import BlockDev as blockdev
+
+from ... import formats
+from ... import udev
+from ...errors import InvalidDiskLabelError
+from ...i18n import _
+from ...storage_log import log_exception_info, log_method_call
+from .formatpopulator import FormatPopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class DiskLabelFormatPopulator(FormatPopulator):
+ priority = 100
+ _type_specifier = "disklabel"
+
+ @classmethod
+ def match(cls, data, device):
+ is_multipath_member = (device.is_disk and
+ blockdev.mpath_is_mpath_member(device.path))
+
+ # XXX ignore disklabels on multipath or biosraid member disks
+ return (bool(udev.device_get_disklabel_type(data)) and
+ not udev.device_is_biosraid_member(data) and
+ not is_multipath_member and
+ udev.device_get_format(data) != "iso9660")
+
+ def run(self):
+ disklabel_type = udev.device_get_disklabel_type(self.data)
+ log_method_call(self, device=self.device.name, label_type=disklabel_type)
+ # if there is no disklabel on the device
+ # blkid doesn't understand dasd disklabels, so bypass for dasd
+ if disklabel_type is None and not \
+ (self.device.is_disk and udev.device_is_dasd(self.data)):
+ log.debug("device %s does not contain a disklabel", self.device.name)
+ return
+
+ if self.device.partitioned:
+ # this device is already set up
+ log.debug("disklabel format on %s already set up", self.device.name)
+ return
+
+ try:
+ self.device.setup()
+ except Exception: # pylint: disable=broad-except
+ log_exception_info(log.warning,
+ "setup of %s failed, aborting disklabel handler",
+ [self.device.name])
+ return
+
+ # special handling for unsupported partitioned devices
+ if not self.device.partitionable:
+ try:
+ fmt = formats.get_format("disklabel",
+ device=self.device.path,
+ label_type=disklabel_type,
+ exists=True)
+ except InvalidDiskLabelError:
+ log.warning("disklabel detected but not usable on %s",
+ self.device.name)
+ else:
+ self.device.format = fmt
+ return
+
+ try:
+ fmt = formats.get_format("disklabel",
+ device=self.device.path,
+ exists=True)
+ except InvalidDiskLabelError as e:
+ log.info("no usable disklabel on %s", self.device.name)
+ if disklabel_type == "gpt":
+ log.debug(e)
+ self.device.format = formats.get_format(_("Invalid Disk Label"))
+ else:
+ self.device.format = fmt
diff --git a/blivet/populator/helpers/dmraid.py b/blivet/populator/helpers/dmraid.py
new file mode 100644
index 0000000..c2ed941
--- /dev/null
+++ b/blivet/populator/helpers/dmraid.py
@@ -0,0 +1,83 @@
+# populator/helpers/dmraid.py
+# DM RAID backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+import gi
+gi.require_version("BlockDev", "1.0")
+
+from gi.repository import BlockDev as blockdev
+
+from ... import udev
+from ...devices import DMRaidArrayDevice
+from ...flags import flags
+from ...storage_log import log_method_call
+from .formatpopulator import FormatPopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class DMRaidFormatPopulator(FormatPopulator):
+ priority = 100
+ _type_specifier = "dmraidmember"
+
+ def run(self):
+ super().run()
+
+ # if dmraid usage is disabled skip any dmraid set activation
+ if not flags.dmraid:
+ return
+
+ log_method_call(self, name=self.device.name)
+ name = udev.device_get_name(self.data)
+ uuid = udev.device_get_uuid(self.data)
+ major = udev.device_get_major(self.data)
+ minor = udev.device_get_minor(self.data)
+
+ # Have we already created the DMRaidArrayDevice?
+ rs_names = blockdev.dm.get_member_raid_sets(uuid, name, major, minor)
+ if len(rs_names) == 0:
+ log.warning("dmraid member %s does not appear to belong to any "
+ "array", self.device.name)
+ return
+
+ for rs_name in rs_names:
+ dm_array = self._populator.devicetree.get_device_by_name(rs_name, incomplete=True)
+ if dm_array is not None:
+ # We add the new device.
+ dm_array.parents.append(self.device)
+ else:
+ # Activate the Raid set.
+ blockdev.dm.activate_raid_set(rs_name)
+ dm_array = DMRaidArrayDevice(rs_name,
+ parents=[self.device])
+
+ self._populator.devicetree._add_device(dm_array)
+
+ # Wait for udev to scan the just created nodes, to avoid a race
+ # with the udev.get_device() call below.
+ udev.settle()
+
+ # Get the DMRaidArrayDevice a DiskLabel format *now*, in case
+ # its partitions get scanned before it does.
+ dm_array.update_sysfs_path()
+ dm_array_info = udev.get_device(dm_array.sysfs_path)
+ self._populator.add_udev_device(dm_array_info, update_orig_fmt=True)
diff --git a/blivet/populator/helpers/luks.py b/blivet/populator/helpers/luks.py
new file mode 100644
index 0000000..1edd56c
--- /dev/null
+++ b/blivet/populator/helpers/luks.py
@@ -0,0 +1,103 @@
+# populator/helpers/luks.py
+# LUKS backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+import gi
+gi.require_version("BlockDev", "1.0")
+
+from gi.repository import BlockDev as blockdev
+
+from ... import udev
+from ...devices import LUKSDevice
+from ...errors import DeviceError, LUKSError
+from ...flags import flags
+from .formatpopulator import FormatPopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class LUKSFormatPopulator(FormatPopulator):
+ priority = 100
+ _type_specifier = "luks"
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+ kwargs["name"] = "luks-%s" % udev.device_get_uuid(self.data)
+ return kwargs
+
+ def run(self):
+ super().run()
+ if not self.device.format.uuid:
+ log.info("luks device %s has no uuid", self.device.path)
+ return
+
+ # look up or create the mapped device
+ if not self._populator.devicetree.get_device_by_name(self.device.format.map_name):
+ passphrase = self._populator._Populator__luks_devs.get(self.device.format.uuid)
+ if self.device.format.configured:
+ pass
+ elif passphrase:
+ self.device.format.passphrase = passphrase
+ elif self.device.format.uuid in self._populator._Populator__luks_devs:
+ log.info("skipping previously-skipped luks device %s",
+ self.device.name)
+ elif self._populator._cleanup or flags.testing:
+ # if we're only building the devicetree so that we can
+ # tear down all of the devices we don't need a passphrase
+ if self.device.format.status:
+ # this makes device.configured return True
+ self.device.format.passphrase = 'yabbadabbadoo'
+ else:
+ # Try each known passphrase. Include luks_devs values in case a
+ # passphrase has been set for a specific device without a full
+ # reset/populate, in which case the new passphrase would not be
+ # in self.__passphrases.
+ passphrases = self._populator._Populator__passphrases + list(self._populator._Populator__luks_devs.values())
+ for passphrase in passphrases:
+ self.device.format.passphrase = passphrase
+ try:
+ self.device.format.setup()
+ except blockdev.BlockDevError:
+ self.device.format.passphrase = None
+ else:
+ break
+
+ luks_device = LUKSDevice(self.device.format.map_name,
+ parents=[self.device],
+ exists=True)
+ try:
+ luks_device.setup()
+ except (LUKSError, blockdev.CryptoError, DeviceError) as e:
+ log.info("setup of %s failed: %s", self.device.format.map_name, e)
+ self.device.remove_child()
+ else:
+ luks_device.update_sysfs_path()
+ self._populator.devicetree._add_device(luks_device)
+ luks_info = udev.get_device(luks_device.sysfs_path)
+ if not luks_info:
+ log.error("failed to get udev data for %s", luks_device.name)
+ return
+
+ self._populator.add_udev_device(luks_info, update_orig_fmt=True)
+ else:
+ log.warning("luks device %s already in the tree",
+ self.device.format.map_name)
diff --git a/blivet/populator/helpers/lvm.py b/blivet/populator/helpers/lvm.py
index 5b389fa..5020abb 100644
--- a/blivet/populator/helpers/lvm.py
+++ b/blivet/populator/helpers/lvm.py
@@ -20,10 +20,22 @@
# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
#
+import gi
+gi.require_version("BlockDev", "1.0")
+
+from gi.repository import BlockDev as blockdev
+
from ... import udev
-from ...devices import LVMVolumeGroupDevice
+from ...devicelibs import lvm
+from ...devices import LVMVolumeGroupDevice, LVMLogicalVolumeDevice, LVMSnapShotDevice
+from ...devices import LVMThinPoolDevice, LVMThinSnapShotDevice, LVMThinLogicalVolumeDevice
+from ...devices.lvm import get_internal_lv_class
+from ...errors import DeviceTreeError, DuplicateVGError
+from ...flags import flags
+from ...size import Size
from ...storage_log import log_method_call
from .devicepopulator import DevicePopulator
+from .formatpopulator import FormatPopulator
import logging
log = logging.getLogger("blivet")
@@ -54,3 +66,289 @@ def run(self):
log.error("failed to find vg '%s' after scanning pvs", vg_name)
return self._populator.devicetree.get_device_by_name(name)
+
+
+class LVMFormatPopulator(FormatPopulator):
+ priority = 100
+ _type_specifier = "lvmpv"
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+
+ pv_info = self._populator.devicetree.pv_info.get(self.device.path, None)
+ name = udev.device_get_name(self.data)
+ if pv_info:
+ if pv_info.vg_name:
+ kwargs["vg_name"] = pv_info.vg_name
+ else:
+ log.warning("PV %s has no vg_name", name)
+ if pv_info.vg_uuid:
+ kwargs["vg_uuid"] = pv_info.vg_uuid
+ else:
+ log.warning("PV %s has no vg_uuid", name)
+ if pv_info.pe_start:
+ kwargs["pe_start"] = Size(pv_info.pe_start)
+ else:
+ log.warning("PV %s has no pe_start", name)
+
+ return kwargs
+
+ def _handle_vg_lvs(self, vg_device):
+ """ Handle setup of the LV's in the vg_device. """
+ vg_name = vg_device.name
+ lv_info = dict((k, v) for (k, v) in iter(self._populator.devicetree.lv_info.items())
+ if v.vg_name == vg_name)
+
+ self._populator.names.extend(n for n in lv_info.keys() if n not in self._populator.names)
+
+ if not vg_device.complete:
+ log.warning("Skipping LVs for incomplete VG %s", vg_name)
+ return
+
+ if not lv_info:
+ log.debug("no LVs listed for VG %s", vg_name)
+ return
+
+ all_lvs = []
+ internal_lvs = []
+
+ def add_required_lv(name, msg):
+ """ Add a prerequisite/parent LV.
+
+ The parent is strictly required in order to be able to add
+ some other LV that depends on it. For this reason, failure to
+ add the specified LV results in a DeviceTreeError with the
+ message string specified in the msg parameter.
+
+ :param str name: the full name of the LV (including vgname)
+ :param str msg: message to pass DeviceTreeError ctor on error
+ :returns: None
+ :raises: :class:`~.errors.DeviceTreeError` on failure
+
+ """
+ vol = self._populator.devicetree.get_device_by_name(name)
+ if vol is None:
+ new_lv = add_lv(lv_info[name])
+ if new_lv:
+ all_lvs.append(new_lv)
+ vol = self._populator.devicetree.get_device_by_name(name)
+
+ if vol is None:
+ log.error("%s: %s", msg, name)
+ raise DeviceTreeError(msg)
+
+ def add_lv(lv):
+ """ Instantiate and add an LV based on data from the VG. """
+ lv_name = lv.lv_name
+ lv_uuid = lv.uuid
+ lv_attr = lv.attr
+ lv_size = Size(lv.size)
+ lv_type = lv.segtype
+
+ lv_class = LVMLogicalVolumeDevice
+ lv_parents = [vg_device]
+ lv_kwargs = {}
+ name = "%s-%s" % (vg_name, lv_name)
+
+ if self._populator.devicetree.get_device_by_name(name):
+ # some lvs may have been added on demand below
+ log.debug("already added %s", name)
+ return
+
+ if lv_attr[0] in 'Ss':
+ log.info("found lvm snapshot volume '%s'", name)
+ origin_name = blockdev.lvm.lvorigin(vg_name, lv_name)
+ if not origin_name:
+ log.error("lvm snapshot '%s-%s' has unknown origin",
+ vg_name, lv_name)
+ return
+
+ if origin_name.endswith("_vorigin]"):
+ lv_kwargs["vorigin"] = True
+ origin = None
+ else:
+ origin_device_name = "%s-%s" % (vg_name, origin_name)
+ add_required_lv(origin_device_name,
+ "failed to locate origin lv")
+ origin = self._populator.devicetree.get_device_by_name(origin_device_name)
+
+ lv_kwargs["origin"] = origin
+ lv_class = LVMSnapShotDevice
+ elif lv_attr[0] == 'v':
+ # skip vorigins
+ return
+ elif lv_attr[0] in 'IielTCo' and lv_name.endswith(']'):
+ # an internal LV, add the an instance of the appropriate class
+ # to internal_lvs for later processing when non-internal LVs are
+ # processed
+ internal_lvs.append(lv_name)
+ return
+ elif lv_attr[0] == 't':
+ # thin pool
+ lv_class = LVMThinPoolDevice
+ elif lv_attr[0] == 'V':
+ # thin volume
+ pool_name = blockdev.lvm.thlvpoolname(vg_name, lv_name)
+ pool_device_name = "%s-%s" % (vg_name, pool_name)
+ add_required_lv(pool_device_name, "failed to look up thin pool")
+
+ origin_name = blockdev.lvm.lvorigin(vg_name, lv_name)
+ if origin_name:
+ origin_device_name = "%s-%s" % (vg_name, origin_name)
+ add_required_lv(origin_device_name, "failed to locate origin lv")
+ origin = self._populator.devicetree.get_device_by_name(origin_device_name)
+ lv_kwargs["origin"] = origin
+ lv_class = LVMThinSnapShotDevice
+ else:
+ lv_class = LVMThinLogicalVolumeDevice
+
+ lv_parents = [self._populator.devicetree.get_device_by_name(pool_device_name)]
+ elif lv_name.endswith(']'):
+ # unrecognized Internal LVM2 device
+ return
+ elif lv_attr[0] not in '-mMrRoOC':
+ # Ignore anything else except for the following:
+ # - normal lv
+ # m mirrored
+ # M mirrored without initial sync
+ # r raid
+ # R raid without initial sync
+ # o origin
+ # O origin with merging snapshot
+ # C cached LV
+ return
+
+ lv_dev = self._populator.devicetree.get_device_by_uuid(lv_uuid)
+ if lv_dev is None:
+ lv_device = lv_class(lv_name, parents=lv_parents,
+ uuid=lv_uuid, size=lv_size, seg_type=lv_type,
+ exists=True, **lv_kwargs)
+ self._populator.devicetree._add_device(lv_device)
+ if flags.installer_mode:
+ lv_device.setup()
+
+ if lv_device.status:
+ lv_device.update_sysfs_path()
+ lv_device.update_size()
+ lv_info = udev.get_device(lv_device.sysfs_path)
+ if not lv_info:
+ log.error("failed to get udev data for lv %s", lv_device.name)
+ return lv_device
+
+ # do format handling now
+ self._populator.add_udev_device(lv_info, update_orig_fmt=True)
+
+ return lv_device
+
+ return None
+
+ def create_internal_lv(lv):
+ lv_name = lv.lv_name
+ lv_uuid = lv.uuid
+ lv_attr = lv.attr
+ lv_size = Size(lv.size)
+ lv_type = lv.segtype
+
+ matching_cls = get_internal_lv_class(lv_attr)
+ if matching_cls is None:
+ raise DeviceTreeError("No internal LV class supported for type '%s'" % lv_attr[0])
+
+ # strip the "[]"s marking the LV as internal
+ lv_name = lv_name.strip("[]")
+
+ # don't know the parent LV yet, will be set later
+ new_lv = matching_cls(lv_name, vg_device, parent_lv=None, size=lv_size, uuid=lv_uuid, exists=True, seg_type=lv_type)
+ if new_lv.status:
+ new_lv.update_sysfs_path()
+ new_lv.update_size()
+
+ lv_info = udev.get_device(new_lv.sysfs_path)
+ if not lv_info:
+ log.error("failed to get udev data for lv %s", new_lv.name)
+ return new_lv
+
+ return new_lv
+
+ # add LVs
+ for lv in lv_info.values():
+ # add the LV to the DeviceTree
+ new_lv = add_lv(lv)
+
+ if new_lv:
+ # save the reference for later use
+ all_lvs.append(new_lv)
+
+ # Instead of doing a topological sort on internal LVs to make sure the
+ # parent LV is always created before its internal LVs (an internal LV
+ # can have internal LVs), we just create all the instances here and
+ # assign their parents later. Those who are not assinged a parent (which
+ # would hold a reference to them) will get eaten by the garbage
+ # collector.
+
+ # create device instances for the internal LVs
+ orphan_lvs = dict()
+ for lv_name in internal_lvs:
+ full_name = "%s-%s" % (vg_name, lv_name)
+ try:
+ new_lv = create_internal_lv(lv_info[full_name])
+ except DeviceTreeError as e:
+ log.warning("Failed to process an internal LV '%s': %s", full_name, e)
+ else:
+ orphan_lvs[full_name] = new_lv
+ all_lvs.append(new_lv)
+
+ # assign parents to internal LVs (and vice versa, see
+ # :class:`~.devices.lvm.LVMInternalLogicalVolumeDevice`)
+ for lv in orphan_lvs.values():
+ parent_lv = lvm.determine_parent_lv(vg_name, lv, all_lvs)
+ if parent_lv:
+ lv.parent_lv = parent_lv
+ else:
+ log.warning("Failed to determine parent LV for an internal LV '%s'", lv.name)
+
+ def run(self):
+ super().run()
+ pv_info = self._populator.devicetree.pv_info.get(self.device.path, None)
+ if pv_info:
+ vg_name = pv_info.vg_name
+ vg_uuid = pv_info.vg_uuid
+ else:
+ # no info about the PV -> we're done
+ return
+
+ if not vg_name:
+ log.info("lvm pv %s has no vg", self.device.name)
+ return
+
+ vg_device = self._populator.devicetree.get_device_by_uuid(vg_uuid, incomplete=True)
+ if vg_device:
+ vg_device.parents.append(self.device)
+ else:
+ same_name = self._populator.devicetree.get_device_by_name(vg_name)
+ if isinstance(same_name, LVMVolumeGroupDevice):
+ raise DuplicateVGError("multiple LVM volume groups with the same name (%s)" % vg_name)
+
+ try:
+ vg_size = Size(pv_info.vg_size)
+ vg_free = Size(pv_info.vg_free)
+ pe_size = Size(pv_info.vg_extent_size)
+ pe_count = pv_info.vg_extent_count
+ pe_free = pv_info.vg_free_count
+ pv_count = pv_info.vg_pv_count
+ except (KeyError, ValueError) as e:
+ log.warning("invalid data for %s: %s", self.device.name, e)
+ return
+
+ vg_device = LVMVolumeGroupDevice(vg_name,
+ parents=[self.device],
+ uuid=vg_uuid,
+ size=vg_size,
+ free=vg_free,
+ pe_size=pe_size,
+ pe_count=pe_count,
+ pe_free=pe_free,
+ pv_count=pv_count,
+ exists=True)
+ self._populator.devicetree._add_device(vg_device)
+
+ self._handle_vg_lvs(vg_device)
diff --git a/blivet/populator/helpers/mdraid.py b/blivet/populator/helpers/mdraid.py
index 1ed790b..6653abb 100644
--- a/blivet/populator/helpers/mdraid.py
+++ b/blivet/populator/helpers/mdraid.py
@@ -25,10 +25,17 @@
from gi.repository import BlockDev as blockdev
+import re
+
from ... import udev
+from ...devicelibs import raid
+from ...devices import MDRaidArrayDevice, MDContainerDevice
+from ...devices import device_path_to_name
+from ...errors import DeviceError
from ...flags import flags
from ...storage_log import log_method_call
from .devicepopulator import DevicePopulator
+from .formatpopulator import FormatPopulator
import logging
log = logging.getLogger("blivet")
@@ -79,3 +86,122 @@ def run(self):
log.error("failed to stop broken md array %s", name)
return device
+
+
+class MDFormatPopulator(FormatPopulator):
+ priority = 100
+ _type_specifier = "mdmember"
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+ try:
+ # ID_FS_UUID contains the array UUID
+ kwargs["md_uuid"] = udev.device_get_uuid(self.data)
+ except KeyError:
+ log.warning("mdraid member %s has no md uuid", udev.device_get_name(self.data))
+
+ # 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(self.data)
+
+ kwargs["biosraid"] = udev.device_is_biosraid_member(self.data)
+ return kwargs
+
+ def run(self):
+ super().run()
+ md_info = blockdev.md.examine(self.device.path)
+
+ # Use mdadm info if udev info is missing
+ md_uuid = md_info.uuid
+ self.device.format.md_uuid = self.device.format.md_uuid or md_uuid
+ md_array = self._populator.devicetree.get_device_by_uuid(self.device.format.md_uuid, incomplete=True)
+
+ if md_array:
+ md_array.parents.append(self.device)
+ else:
+ # create the array with just this one member
+ # level is reported as, eg: "raid1"
+ md_level = md_info.level
+ md_devices = md_info.num_devices
+
+ if md_level is None:
+ log.warning("invalid data for %s: no RAID level", self.device.name)
+ return
+
+ # md_examine yields metadata (MD_METADATA) only for metadata version > 0.90
+ # if MD_METADATA is missing, assume metadata version is 0.90
+ md_metadata = md_info.metadata or "0.90"
+ md_name = None
+
+ # check the list of devices udev knows about to see if the array
+ # this device belongs to is already active
+ # XXX This is mainly for containers now since their name/device is
+ # not given by mdadm examine as we run it.
+ for dev in udev.get_devices():
+ if not udev.device_is_md(dev):
+ continue
+
+ try:
+ dev_uuid = udev.device_get_md_uuid(dev)
+ dev_level = udev.device_get_md_level(dev)
+ except KeyError:
+ continue
+
+ if dev_uuid is None or dev_level is None:
+ continue
+
+ if dev_uuid == md_uuid and dev_level == md_level:
+ md_name = udev.device_get_md_name(dev)
+ break
+
+ md_path = md_info.device or ""
+ if md_path and not md_name:
+ md_name = device_path_to_name(md_path)
+ if re.match(r'md\d+$', md_name):
+ # md0 -> 0
+ md_name = md_name[2:]
+
+ if md_name:
+ array = self._populator.devicetree.get_device_by_name(md_name, incomplete=True)
+ if array and array.uuid != md_uuid:
+ log.error("found multiple devices with the name %s", md_name)
+
+ if md_name:
+ log.info("using name %s for md array containing member %s",
+ md_name, self.device.name)
+ else:
+ log.error("failed to determine name for the md array %s", (md_uuid or "unknown"))
+ return
+
+ array_type = MDRaidArrayDevice
+ try:
+ if raid.get_raid_level(md_level) is raid.Container and \
+ getattr(self.device.format, "biosraid", False):
+ array_type = MDContainerDevice
+ except raid.RaidError as e:
+ log.error("failed to create md array: %s", e)
+ return
+
+ try:
+ md_array = array_type(
+ md_name,
+ level=md_level,
+ member_devices=md_devices,
+ uuid=md_uuid,
+ metadata_version=md_metadata,
+ exists=True
+ )
+ except (ValueError, DeviceError) as e:
+ log.error("failed to create md array: %s", e)
+ return
+
+ md_array.update_sysfs_path()
+ md_array.parents.append(self.device)
+ self._populator.devicetree._add_device(md_array)
+ if md_array.status:
+ array_info = udev.get_device(md_array.sysfs_path)
+ if not array_info:
+ log.error("failed to get udev data for %s", md_array.name)
+ return
+
+ self._populator.add_udev_device(array_info, update_orig_fmt=True)
diff --git a/blivet/populator/helpers/multipath.py b/blivet/populator/helpers/multipath.py
index accc6c9..8405cec 100644
--- a/blivet/populator/helpers/multipath.py
+++ b/blivet/populator/helpers/multipath.py
@@ -25,6 +25,7 @@
from ...errors import DeviceTreeError
from ...storage_log import log_method_call
from .devicepopulator import DevicePopulator
+from .formatpopulator import FormatPopulator
import logging
log = logging.getLogger("blivet")
@@ -56,3 +57,17 @@ def run(self):
self._populator.devicetree._add_device(device)
return device
+
+
+class MultipathFormatPopulator(FormatPopulator):
+ priority = 100
+ _type_specifier = "multipath_member"
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+ # blkid does not care that the UUID it sees on a multipath member is
+ # for the multipath set's (and not the member's) formatting, so we
+ # have to discard it.
+ kwargs.pop("uuid")
+ kwargs.pop("label")
+ return kwargs
diff --git a/blivet/populator/populator.py b/blivet/populator/populator.py
index b0b16c9..8341393 100644
--- a/blivet/populator/populator.py
+++ b/blivet/populator/populator.py
@@ -32,33 +32,17 @@
from gi.repository import BlockDev as blockdev
-from ..errors import DeviceError, DeviceTreeError, DuplicateVGError, FSError, InvalidDiskLabelError, LUKSError
-from ..devices import BTRFSSubVolumeDevice, BTRFSVolumeDevice, BTRFSSnapShotDevice
+from ..errors import DeviceError, DeviceTreeError
from ..devices import DMLinearDevice, DMRaidArrayDevice
-from ..devices import FileDevice, LoopDevice, LUKSDevice
-from ..devices import LVMLogicalVolumeDevice, LVMVolumeGroupDevice
-from ..devices import LVMThinPoolDevice, LVMThinLogicalVolumeDevice
-from ..devices import LVMSnapShotDevice, LVMThinSnapShotDevice
+from ..devices import FileDevice, LoopDevice
from ..devices import MDRaidArrayDevice
-from ..devices import MDContainerDevice
from ..devices import MultipathDevice
-from ..devices import PartitionDevice
-from ..devices import device_path_to_name
-from ..devices.lvm import get_internal_lv_class
-from .. import formats
from ..devicelibs import lvm
-from ..devicelibs import raid
from .. import udev
from .. import util
from ..util import open # pylint: disable=redefined-builtin
from ..flags import flags
-from ..storage_log import log_exception_info, log_method_call
-from ..i18n import _
-from ..size import Size
-
-from .helpers import DiskDevicePopulator, DMDevicePopulator, LoopDevicePopulator
-from .helpers import LVMDevicePopulator, MDDevicePopulator, MultipathDevicePopulator
-from .helpers import OpticalDevicePopulator, PartitionDevicePopulator
+from ..storage_log import log_method_call
import logging
log = logging.getLogger("blivet")
@@ -441,582 +425,6 @@ def add_udev_device(self, info, update_orig_fmt=False):
device.original_format = copy.deepcopy(device.format)
device.device_links = udev.device_get_symlinks(info)
- def handle_udev_disk_label_format(self, info, device):
- disklabel_type = udev.device_get_disklabel_type(info)
- log_method_call(self, device=device.name, label_type=disklabel_type)
- # if there is no disklabel on the device
- # blkid doesn't understand dasd disklabels, so bypass for dasd
- if disklabel_type is None and not \
- (device.is_disk and udev.device_is_dasd(info)):
- log.debug("device %s does not contain a disklabel", device.name)
- return
-
- if device.partitioned:
- # this device is already set up
- log.debug("disklabel format on %s already set up", device.name)
- return
-
- try:
- device.setup()
- except Exception: # pylint: disable=broad-except
- log_exception_info(log.warning, "setup of %s failed, aborting disklabel handler", [device.name])
- return
-
- # special handling for unsupported partitioned devices
- if not device.partitionable:
- try:
- fmt = formats.get_format("disklabel",
- device=device.path,
- label_type=disklabel_type,
- exists=True)
- except InvalidDiskLabelError:
- log.warning("disklabel detected but not usable on %s",
- device.name)
- else:
- device.format = fmt
- return
-
- try:
- fmt = formats.get_format("disklabel",
- device=device.path,
- exists=True)
- except InvalidDiskLabelError as e:
- log.info("no usable disklabel on %s", device.name)
- if disklabel_type == "gpt":
- log.debug(e)
- device.format = formats.get_format(_("Invalid Disk Label"))
- else:
- device.format = fmt
-
- def handle_udev_luks_format(self, info, device):
- # pylint: disable=unused-argument
- log_method_call(self, name=device.name, type=device.format.type)
- if not device.format.uuid:
- log.info("luks device %s has no uuid", device.path)
- return
-
- # look up or create the mapped device
- if not self.get_device_by_name(device.format.map_name):
- passphrase = self.__luks_devs.get(device.format.uuid)
- if device.format.configured:
- pass
- elif passphrase:
- device.format.passphrase = passphrase
- elif device.format.uuid in self.__luks_devs:
- log.info("skipping previously-skipped luks device %s",
- device.name)
- elif self._cleanup or flags.testing:
- # if we're only building the devicetree so that we can
- # tear down all of the devices we don't need a passphrase
- if device.format.status:
- # this makes device.configured return True
- device.format.passphrase = 'yabbadabbadoo'
- else:
- # Try each known passphrase. Include luks_devs values in case a
- # passphrase has been set for a specific device without a full
- # reset/populate, in which case the new passphrase would not be
- # in self.__passphrases.
- for passphrase in self.__passphrases + list(self.__luks_devs.values()):
- device.format.passphrase = passphrase
- try:
- device.format.setup()
- except LUKSError:
- device.format.passphrase = None
- else:
- break
-
- luks_device = LUKSDevice(device.format.map_name,
- parents=[device],
- exists=True)
- try:
- luks_device.setup()
- except (LUKSError, blockdev.CryptoError, DeviceError) as e:
- log.info("setup of %s failed: %s", device.format.map_name, e)
- device.remove_child()
- else:
- luks_device.update_sysfs_path()
- self.devicetree._add_device(luks_device)
- luks_info = udev.get_device(luks_device.sysfs_path)
- if not luks_info:
- log.error("failed to get udev data for %s", luks_device.name)
- return
-
- self.add_udev_device(luks_info, update_orig_fmt=True)
- else:
- log.warning("luks device %s already in the tree",
- device.format.map_name)
-
- def handle_vg_lvs(self, vg_device):
- """ Handle setup of the LV's in the vg_device. """
- vg_name = vg_device.name
- lv_info = dict((k, v) for (k, v) in iter(self.devicetree.lv_info.items())
- if v.vg_name == vg_name)
-
- self.names.extend(n for n in lv_info.keys() if n not in self.names)
-
- if not vg_device.complete:
- log.warning("Skipping LVs for incomplete VG %s", vg_name)
- return
-
- if not lv_info:
- log.debug("no LVs listed for VG %s", vg_name)
- return
-
- all_lvs = []
- internal_lvs = []
-
- def add_required_lv(name, msg):
- """ Add a prerequisite/parent LV.
-
- The parent is strictly required in order to be able to add
- some other LV that depends on it. For this reason, failure to
- add the specified LV results in a DeviceTreeError with the
- message string specified in the msg parameter.
-
- :param str name: the full name of the LV (including vgname)
- :param str msg: message to pass DeviceTreeError ctor on error
- :returns: None
- :raises: :class:`~.errors.DeviceTreeError` on failure
-
- """
- vol = self.get_device_by_name(name)
- if vol is None:
- new_lv = add_lv(lv_info[name])
- if new_lv:
- all_lvs.append(new_lv)
- vol = self.get_device_by_name(name)
-
- if vol is None:
- log.error("%s: %s", msg, name)
- raise DeviceTreeError(msg)
-
- def add_lv(lv):
- """ Instantiate and add an LV based on data from the VG. """
- lv_name = lv.lv_name
- lv_uuid = lv.uuid
- lv_attr = lv.attr
- lv_size = Size(lv.size)
- lv_type = lv.segtype
-
- lv_class = LVMLogicalVolumeDevice
- lv_parents = [vg_device]
- lv_kwargs = {}
- name = "%s-%s" % (vg_name, lv_name)
-
- if self.get_device_by_name(name):
- # some lvs may have been added on demand below
- log.debug("already added %s", name)
- return
-
- if lv_attr[0] in 'Ss':
- log.info("found lvm snapshot volume '%s'", name)
- origin_name = blockdev.lvm.lvorigin(vg_name, lv_name)
- if not origin_name:
- log.error("lvm snapshot '%s-%s' has unknown origin",
- vg_name, lv_name)
- return
-
- if origin_name.endswith("_vorigin]"):
- lv_kwargs["vorigin"] = True
- origin = None
- else:
- origin_device_name = "%s-%s" % (vg_name, origin_name)
- add_required_lv(origin_device_name,
- "failed to locate origin lv")
- origin = self.get_device_by_name(origin_device_name)
-
- lv_kwargs["origin"] = origin
- lv_class = LVMSnapShotDevice
- elif lv_attr[0] == 'v':
- # skip vorigins
- return
- elif lv_attr[0] in 'IielTCo' and lv_name.endswith(']'):
- # an internal LV, add the an instance of the appropriate class
- # to internal_lvs for later processing when non-internal LVs are
- # processed
- internal_lvs.append(lv_name)
- return
- elif lv_attr[0] == 't':
- # thin pool
- lv_class = LVMThinPoolDevice
- elif lv_attr[0] == 'V':
- # thin volume
- pool_name = blockdev.lvm.thlvpoolname(vg_name, lv_name)
- pool_device_name = "%s-%s" % (vg_name, pool_name)
- add_required_lv(pool_device_name, "failed to look up thin pool")
-
- origin_name = blockdev.lvm.lvorigin(vg_name, lv_name)
- if origin_name:
- origin_device_name = "%s-%s" % (vg_name, origin_name)
- add_required_lv(origin_device_name, "failed to locate origin lv")
- origin = self.get_device_by_name(origin_device_name)
- lv_kwargs["origin"] = origin
- lv_class = LVMThinSnapShotDevice
- else:
- lv_class = LVMThinLogicalVolumeDevice
-
- lv_parents = [self.get_device_by_name(pool_device_name)]
- elif lv_name.endswith(']'):
- # unrecognized Internal LVM2 device
- return
- elif lv_attr[0] not in '-mMrRoOC':
- # Ignore anything else except for the following:
- # - normal lv
- # m mirrored
- # M mirrored without initial sync
- # r raid
- # R raid without initial sync
- # o origin
- # O origin with merging snapshot
- # C cached LV
- return
-
- lv_dev = self.get_device_by_uuid(lv_uuid)
- if lv_dev is None:
- lv_device = lv_class(lv_name, parents=lv_parents,
- uuid=lv_uuid, size=lv_size, seg_type=lv_type,
- exists=True, **lv_kwargs)
- self.devicetree._add_device(lv_device)
- if flags.installer_mode:
- lv_device.setup()
-
- if lv_device.status:
- lv_device.update_sysfs_path()
- lv_device.update_size()
- lv_info = udev.get_device(lv_device.sysfs_path)
- if not lv_info:
- log.error("failed to get udev data for lv %s", lv_device.name)
- return lv_device
-
- # do format handling now
- self.add_udev_device(lv_info, update_orig_fmt=True)
-
- return lv_device
-
- return None
-
- def create_internal_lv(lv):
- lv_name = lv.lv_name
- lv_uuid = lv.uuid
- lv_attr = lv.attr
- lv_size = Size(lv.size)
- lv_type = lv.segtype
-
- matching_cls = get_internal_lv_class(lv_attr)
- if matching_cls is None:
- raise DeviceTreeError("No internal LV class supported for type '%s'" % lv_attr[0])
-
- # strip the "[]"s marking the LV as internal
- lv_name = lv_name.strip("[]")
-
- # don't know the parent LV yet, will be set later
- new_lv = matching_cls(lv_name, vg_device, parent_lv=None, size=lv_size, uuid=lv_uuid, exists=True, seg_type=lv_type)
- if new_lv.status:
- new_lv.update_sysfs_path()
- new_lv.update_size()
-
- lv_info = udev.get_device(new_lv.sysfs_path)
- if not lv_info:
- log.error("failed to get udev data for lv %s", new_lv.name)
- return new_lv
-
- return new_lv
-
- # add LVs
- for lv in lv_info.values():
- # add the LV to the DeviceTree
- new_lv = add_lv(lv)
-
- if new_lv:
- # save the reference for later use
- all_lvs.append(new_lv)
-
- # Instead of doing a topological sort on internal LVs to make sure the
- # parent LV is always created before its internal LVs (an internal LV
- # can have internal LVs), we just create all the instances here and
- # assign their parents later. Those who are not assinged a parent (which
- # would hold a reference to them) will get eaten by the garbage
- # collector.
-
- # create device instances for the internal LVs
- orphan_lvs = dict()
- for lv_name in internal_lvs:
- full_name = "%s-%s" % (vg_name, lv_name)
- try:
- new_lv = create_internal_lv(lv_info[full_name])
- except DeviceTreeError as e:
- log.warning("Failed to process an internal LV '%s': %s", full_name, e)
- else:
- orphan_lvs[full_name] = new_lv
- all_lvs.append(new_lv)
-
- # assign parents to internal LVs (and vice versa, see
- # :class:`~.devices.lvm.LVMInternalLogicalVolumeDevice`)
- for lv in orphan_lvs.values():
- parent_lv = lvm.determine_parent_lv(vg_name, lv, all_lvs)
- if parent_lv:
- lv.parent_lv = parent_lv
- else:
- log.warning("Failed to determine parent LV for an internal LV '%s'", lv.name)
-
- def handle_udev_lvm_pv_format(self, info, device):
- # pylint: disable=unused-argument
- log_method_call(self, name=device.name, type=device.format.type)
- pv_info = self.devicetree.pv_info.get(device.path, None)
- if pv_info:
- vg_name = pv_info.vg_name
- vg_uuid = pv_info.vg_uuid
- else:
- # no info about the PV -> we're done
- return
-
- if not vg_name:
- log.info("lvm pv %s has no vg", device.name)
- return
-
- vg_device = self.get_device_by_uuid(vg_uuid, incomplete=True)
- if vg_device:
- vg_device.parents.append(device)
- else:
- same_name = self.get_device_by_name(vg_name)
- if isinstance(same_name, LVMVolumeGroupDevice):
- raise DuplicateVGError("multiple LVM volume groups with the same name (%s)" % vg_name)
-
- try:
- vg_size = Size(pv_info.vg_size)
- vg_free = Size(pv_info.vg_free)
- pe_size = Size(pv_info.vg_extent_size)
- pe_count = pv_info.vg_extent_count
- pe_free = pv_info.vg_free_count
- pv_count = pv_info.vg_pv_count
- except (KeyError, ValueError) as e:
- log.warning("invalid data for %s: %s", device.name, e)
- return
-
- vg_device = LVMVolumeGroupDevice(vg_name,
- parents=[device],
- uuid=vg_uuid,
- size=vg_size,
- free=vg_free,
- pe_size=pe_size,
- pe_count=pe_count,
- pe_free=pe_free,
- pv_count=pv_count,
- exists=True)
- self.devicetree._add_device(vg_device)
-
- self.handle_vg_lvs(vg_device)
-
- def handle_udev_md_member_format(self, info, device):
- # pylint: disable=unused-argument
- log_method_call(self, name=device.name, type=device.format.type)
- md_info = blockdev.md.examine(device.path)
-
- # Use mdadm info if udev info is missing
- md_uuid = md_info.uuid
- device.format.md_uuid = device.format.md_uuid or md_uuid
- md_array = self.get_device_by_uuid(device.format.md_uuid, incomplete=True)
-
- if md_array:
- md_array.parents.append(device)
- else:
- # create the array with just this one member
- # level is reported as, eg: "raid1"
- md_level = md_info.level
- md_devices = md_info.num_devices
-
- if md_level is None:
- log.warning("invalid data for %s: no RAID level", device.name)
- return
-
- # md_examine yields metadata (MD_METADATA) only for metadata version > 0.90
- # if MD_METADATA is missing, assume metadata version is 0.90
- md_metadata = md_info.metadata or "0.90"
- md_name = None
-
- # check the list of devices udev knows about to see if the array
- # this device belongs to is already active
- # XXX This is mainly for containers now since their name/device is
- # not given by mdadm examine as we run it.
- for dev in udev.get_devices():
- if not udev.device_is_md(dev):
- continue
-
- try:
- dev_uuid = udev.device_get_md_uuid(dev)
- dev_level = udev.device_get_md_level(dev)
- except KeyError:
- continue
-
- if dev_uuid is None or dev_level is None:
- continue
-
- if dev_uuid == md_uuid and dev_level == md_level:
- md_name = udev.device_get_md_name(dev)
- break
-
- md_path = md_info.device or ""
- if md_path and not md_name:
- md_name = device_path_to_name(md_path)
- if re.match(r'md\d+$', md_name):
- # md0 -> 0
- md_name = md_name[2:]
-
- if md_name:
- array = self.get_device_by_name(md_name, incomplete=True)
- if array and array.uuid != md_uuid:
- log.error("found multiple devices with the name %s", md_name)
-
- if md_name:
- log.info("using name %s for md array containing member %s",
- md_name, device.name)
- else:
- log.error("failed to determine name for the md array %s", (md_uuid or "unknown"))
- return
-
- array_type = MDRaidArrayDevice
- try:
- if raid.get_raid_level(md_level) is raid.Container and \
- getattr(device.format, "biosraid", False):
- array_type = MDContainerDevice
- except raid.RaidError as e:
- log.error("failed to create md array: %s", e)
- return
-
- try:
- md_array = array_type(
- md_name,
- level=md_level,
- member_devices=md_devices,
- uuid=md_uuid,
- metadata_version=md_metadata,
- exists=True
- )
- except (ValueError, DeviceError) as e:
- log.error("failed to create md array: %s", e)
- return
-
- md_array.update_sysfs_path()
- md_array.parents.append(device)
- self.devicetree._add_device(md_array)
- if md_array.status:
- array_info = udev.get_device(md_array.sysfs_path)
- if not array_info:
- log.error("failed to get udev data for %s", md_array.name)
- return
-
- self.add_udev_device(array_info, update_orig_fmt=True)
-
- def handle_udev_dmraid_member_format(self, info, device):
- # if dmraid usage is disabled skip any dmraid set activation
- if not flags.dmraid:
- return
-
- log_method_call(self, name=device.name, type=device.format.type)
- name = udev.device_get_name(info)
- uuid = udev.device_get_uuid(info)
- major = udev.device_get_major(info)
- minor = udev.device_get_minor(info)
-
- # Have we already created the DMRaidArrayDevice?
- rs_names = blockdev.dm.get_member_raid_sets(uuid, name, major, minor)
- if len(rs_names) == 0:
- log.warning("dmraid member %s does not appear to belong to any "
- "array", device.name)
- return
-
- for rs_name in rs_names:
- dm_array = self.get_device_by_name(rs_name, incomplete=True)
- if dm_array is not None:
- # We add the new device.
- dm_array.parents.append(device)
- else:
- # Activate the Raid set.
- blockdev.dm.activate_raid_set(rs_name)
- dm_array = DMRaidArrayDevice(rs_name,
- parents=[device])
-
- self.devicetree._add_device(dm_array)
-
- # Wait for udev to scan the just created nodes, to avoid a race
- # with the udev.get_device() call below.
- udev.settle()
-
- # Get the DMRaidArrayDevice a DiskLabel format *now*, in case
- # its partitions get scanned before it does.
- dm_array.update_sysfs_path()
- dm_array_info = udev.get_device(dm_array.sysfs_path)
- self.handle_udev_disk_label_format(dm_array_info, dm_array)
-
- # Use the rs's object on the device.
- # pyblock can return the memebers of a set and the
- # device has the attribute to hold it. But ATM we
- # are not really using it. Commenting this out until
- # we really need it.
- # device.format.raidmem = block.getMemFromRaidSet(dm_array,
- # major=major, minor=minor, uuid=uuid, name=name)
-
- def handle_btrfs_format(self, info, device):
- log_method_call(self, name=device.name)
- uuid = udev.device_get_uuid(info)
-
- btrfs_dev = None
- for d in self.devicetree.devices:
- if isinstance(d, BTRFSVolumeDevice) and d.uuid == uuid:
- btrfs_dev = d
- break
-
- if btrfs_dev:
- log.info("found btrfs volume %s", btrfs_dev.name)
- btrfs_dev.parents.append(device)
- else:
- label = udev.device_get_label(info)
- log.info("creating btrfs volume btrfs.%s", label)
- btrfs_dev = BTRFSVolumeDevice(label, parents=[device], uuid=uuid,
- exists=True)
- self.devicetree._add_device(btrfs_dev)
-
- if not btrfs_dev.subvolumes:
- snapshots = btrfs_dev.list_subvolumes(snapshots_only=True)
- snapshot_ids = [s.id for s in snapshots]
- for subvol_dict in btrfs_dev.list_subvolumes():
- vol_id = subvol_dict.id
- vol_path = subvol_dict.path
- parent_id = subvol_dict.parent_id
- if vol_path in [sv.name for sv in btrfs_dev.subvolumes]:
- continue
-
- # look up the parent subvol
- parent = None
- subvols = [btrfs_dev] + btrfs_dev.subvolumes
- for sv in subvols:
- if sv.vol_id == parent_id:
- parent = sv
- break
-
- if parent is None:
- log.error("failed to find parent (%d) for subvol %s",
- parent_id, vol_path)
- raise DeviceTreeError("could not find parent for subvol")
-
- fmt = formats.get_format("btrfs",
- device=btrfs_dev.path,
- exists=True,
- vol_uuid=btrfs_dev.format.vol_uuid,
- subvolspec=vol_path,
- mountopts="subvol=%s" % vol_path)
- if vol_id in snapshot_ids:
- device_class = BTRFSSnapShotDevice
- else:
- device_class = BTRFSSubVolumeDevice
-
- subvol = device_class(vol_path,
- vol_id=vol_id,
- fmt=fmt,
- parents=[parent],
- exists=True)
- self.devicetree._add_device(subvol)
-
def handle_udev_device_format(self, info, device):
log_method_call(self, name=getattr(device, "name", None))
@@ -1028,131 +436,14 @@ def handle_udev_device_format(self, info, device):
return
name = udev.device_get_name(info)
- uuid = udev.device_get_uuid(info)
- label = udev.device_get_label(info)
- format_type = udev.device_get_format(info)
- serial = udev.device_get_serial(info)
-
- is_multipath_member = (device.is_disk and
- blockdev.mpath_is_mpath_member(device.path))
-
- # Now, if the device is a disk, see if there is a usable disklabel.
- # If not, see if the user would like to create one.
- # XXX ignore disklabels on multipath or biosraid member disks
- if not udev.device_is_biosraid_member(info) and \
- not is_multipath_member and \
- format_type != "iso9660":
- self.handle_udev_disk_label_format(info, device)
- if device.partitioned or self.is_ignored(info) or \
- (not device.partitionable and
- device.format.type == "disklabel"):
- # If the device has a disklabel, or the user chose not to
- # create one, we are finished with this device. Otherwise
- # it must have some non-disklabel formatting, in which case
- # we fall through to handle that.
- return
-
- if (not device) or (not format_type) or device.format.type:
+ if (not device or
+ (not udev.device_get_format(info) and not udev.device_get_disklabel_type(info)) or
+ device.format.type):
# this device has no formatting or it has already been set up
- # FIXME: this probably needs something special for disklabels
log.debug("no type or existing type for %s, bailing", name)
return
- # set up the common arguments for the format constructor
- format_designator = format_type
- kwargs = {"uuid": uuid,
- "label": label,
- "device": device.path,
- "serial": serial,
- "exists": True}
-
- # set up type-specific arguments for the format constructor
- if format_type == "crypto_LUKS":
- # luks/dmcrypt
- kwargs["name"] = "luks-%s" % uuid
- elif format_type in formats.mdraid.MDRaidMember._udev_types:
- # mdraid
- try:
- # ID_FS_UUID contains the array UUID
- kwargs["md_uuid"] = udev.device_get_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["biosraid"] = udev.device_is_biosraid_member(info)
- elif format_type == "LVM2_member":
- # lvm
- pv_info = self.devicetree.pv_info.get(device.path, None)
- if pv_info:
- if pv_info.vg_name:
- kwargs["vg_name"] = pv_info.vg_name
- else:
- log.warning("PV %s has no vg_name", name)
- if pv_info.vg_uuid:
- kwargs["vg_uuid"] = pv_info.vg_uuid
- else:
- log.warning("PV %s has no vg_uuid", name)
- if pv_info.pe_start:
- kwargs["pe_start"] = Size(pv_info.pe_start)
- else:
- log.warning("PV %s has no pe_start", name)
- elif format_type == "vfat":
- # efi magic
- if isinstance(device, PartitionDevice) and device.bootable:
- efi = formats.get_format("efi")
- if efi.min_size <= device.size <= efi.max_size:
- format_designator = "efi"
- elif format_type == "hfsplus":
- if isinstance(device, PartitionDevice):
- macefi = formats.get_format("macefi")
- if macefi.min_size <= device.size <= macefi.max_size and \
- device.parted_partition.name == macefi.name:
- format_designator = "macefi"
- elif format_type == "hfs":
- # apple bootstrap magic
- if isinstance(device, PartitionDevice) and device.bootable:
- apple = formats.get_format("appleboot")
- if apple.min_size <= device.size <= apple.max_size:
- format_designator = "appleboot"
- elif format_type == "btrfs":
- # the format's uuid attr will contain the UUID_SUB, while the
- # overarching volume UUID will be stored as vol_uuid
- kwargs["uuid"] = info["ID_FS_UUID_SUB"]
- kwargs["vol_uuid"] = uuid
- elif format_type == "multipath_member":
- # blkid does not care that the UUID it sees on a multipath member is
- # for the multipath set's (and not the member's) formatting, so we
- # have to discard it.
- kwargs.pop("uuid")
- kwargs.pop("label")
-
- try:
- log.info("type detected on '%s' is '%s'", name, format_designator)
- device.format = formats.get_format(format_designator, **kwargs)
- if device.format.type:
- log.info("got format: %s", device.format)
- except FSError:
- log.warning("type '%s' on '%s' invalid, assuming no format",
- format_designator, name)
- device.format = formats.DeviceFormat()
- return
-
- #
- # now do any special handling required for the device's format
- #
- if device.format.type == "luks":
- self.handle_udev_luks_format(info, device)
- elif device.format.type == "mdmember":
- self.handle_udev_md_member_format(info, device)
- elif device.format.type == "dmraidmember":
- self.handle_udev_dmraid_member_format(info, device)
- elif device.format.type == "lvmpv":
- self.handle_udev_lvm_pv_format(info, device)
- elif device.format.type == "btrfs":
- self.handle_btrfs_format(info, device)
+ # TODO: set up the formatting
def update_device_format(self, device):
log.info("updating format of device: %s", device)
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/368a4db6141c7fdcb31ed45fe6be...
8 years, 5 months
[master 7/27] Split DiskDevicePopulator up by disk type.
by dashea
From: David Lehman <dlehman(a)redhat.com>
---
blivet/populator/helpers/disk.py | 254 +++++++++++++++++++++++++--------------
1 file changed, 164 insertions(+), 90 deletions(-)
diff --git a/blivet/populator/helpers/disk.py b/blivet/populator/helpers/disk.py
index aa50da1..fbadda5 100644
--- a/blivet/populator/helpers/disk.py
+++ b/blivet/populator/helpers/disk.py
@@ -39,6 +39,7 @@
class DiskDevicePopulator(DevicePopulator):
priority = 10
+ _device_class = DiskDevice
@classmethod
def match(cls, data):
@@ -48,101 +49,174 @@ def match(cls, data):
not udev.device_is_dm(data) and
not (udev.device_is_md(data) and not udev.device_get_md_container(data)))
+ def _get_kwargs(self):
+ sysfs_path = udev.device_get_sysfs_path(self.data)
+ kwargs = {
+ "sysfs_path": sysfs_path,
+ "serial": udev.device_get_serial(self.data),
+ "vendor": util.get_sysfs_attr(sysfs_path, "device/vendor"),
+ "model": util.get_sysfs_attr(sysfs_path, "device/model"),
+ "bus": udev.device_get_bus(self.data)
+ }
+
+ if self._device_class == DiskDevice:
+ kwargs["major"] = udev.device_get_major(self.data)
+ kwargs["minor"] = udev.device_get_minor(self.data)
+ log.info("%s is a disk", udev.device_get_name(self.data))
+
+ return kwargs
+
def run(self):
name = udev.device_get_name(self.data)
log_method_call(self, name=name)
- sysfs_path = udev.device_get_sysfs_path(self.data)
- serial = udev.device_get_serial(self.data)
- bus = udev.device_get_bus(self.data)
-
- vendor = util.get_sysfs_attr(sysfs_path, "device/vendor")
- model = util.get_sysfs_attr(sysfs_path, "device/model")
-
- kwargs = {"serial": serial, "vendor": vendor, "model": model, "bus": bus}
- if udev.device_is_iscsi(self.data) and not self._populator._cleanup:
- disk_type = iScsiDiskDevice
- initiator = udev.device_get_iscsi_initiator(self.data)
- target = udev.device_get_iscsi_name(self.data)
- address = udev.device_get_iscsi_address(self.data)
- port = udev.device_get_iscsi_port(self.data)
- nic = udev.device_get_iscsi_nic(self.data)
- kwargs["initiator"] = initiator
- if initiator == self._populator.iscsi.initiator:
- node = self._populator.iscsi.get_node(target, address, port, nic)
- kwargs["node"] = node
- kwargs["ibft"] = node in self._populator.iscsi.ibft_nodes
- kwargs["nic"] = self._populator.iscsi.ifaces.get(node.iface, node.iface)
- log.info("%s is an iscsi disk", name)
- else:
- # qla4xxx partial offload
- kwargs["node"] = None
- kwargs["ibft"] = False
- kwargs["nic"] = "offload:not_accessible_via_iscsiadm"
- kwargs["fw_address"] = address
- kwargs["fw_port"] = port
- kwargs["fw_name"] = name
- elif udev.device_is_fcoe(self.data):
- disk_type = FcoeDiskDevice
- kwargs["nic"] = udev.device_get_fcoe_nic(self.data)
- kwargs["identifier"] = udev.device_get_fcoe_identifier(self.data)
- log.info("%s is an fcoe disk", name)
- elif udev.device_get_md_container(self.data):
- name = udev.device_get_md_name(self.data)
- disk_type = MDBiosRaidArrayDevice
- parent_path = udev.device_get_md_container(self.data)
- parent_name = device_path_to_name(parent_path)
- container = self._populator.devicetree.get_device_by_name(parent_name)
- if not container:
- parent_sys_name = blockdev.md.node_from_name(parent_name)
- container_sysfs = "/sys/class/block/" + parent_sys_name
- container_info = udev.get_device(container_sysfs)
- if not container_info:
- log.error("failed to find md container %s at %s",
- parent_name, container_sysfs)
- return
-
- self._populator.add_udev_device(container_info)
- container = self._populator.devicetree.get_device_by_name(parent_name)
- if not container:
- log.error("failed to scan md container %s", parent_name)
- return
-
- kwargs["parents"] = [container]
- kwargs["level"] = udev.device_get_md_level(self.data)
- kwargs["member_devices"] = udev.device_get_md_devices(self.data)
- kwargs["uuid"] = udev.device_get_md_uuid(self.data)
- kwargs["exists"] = True
- del kwargs["model"]
- del kwargs["serial"]
- del kwargs["vendor"]
- del kwargs["bus"]
- elif udev.device_is_dasd(self.data) and not self._populator._cleanup:
- disk_type = DASDDevice
- kwargs["busid"] = udev.device_get_dasd_bus_id(self.data)
- kwargs["opts"] = {}
-
- for attr in ['readonly', 'use_diag', 'erplog', 'failfast']:
- kwargs["opts"][attr] = udev.device_get_dasd_flag(self.data, attr)
-
- log.info("%s is a dasd device", name)
- elif udev.device_is_zfcp(self.data):
- disk_type = ZFCPDiskDevice
-
- for attr in ['hba_id', 'wwpn', 'fcp_lun']:
- kwargs[attr] = udev.device_get_zfcp_attribute(self.data, attr=attr)
-
- log.info("%s is a zfcp device", name)
- else:
- disk_type = DiskDevice
- log.info("%s is a disk", name)
-
- device = disk_type(name,
- major=udev.device_get_major(self.data),
- minor=udev.device_get_minor(self.data),
- sysfs_path=sysfs_path, **kwargs)
- if disk_type == DASDDevice:
+ kwargs = self._get_kwargs()
+ device = self._device_class(name, **kwargs)
+ if self._device_class == DASDDevice:
self._populator.dasd.append(device)
self._populator.devicetree._add_device(device)
return device
+
+
+class iScsiDevicePopulator(DiskDevicePopulator):
+ from ...iscsi import iscsi as _iscsi
+
+ priority = 20
+ _device_class = iScsiDiskDevice
+
+ @classmethod
+ def match(cls, data):
+ return (super().match(data) and
+ udev.device_is_iscsi(data) and cls._iscsi.initiator and
+ cls._iscsi.initiator == udev.device_get_iscsi_initiator(data))
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+ name = udev.device_get_name(self.data)
+ initiator = udev.device_get_iscsi_initiator(self.data)
+ target = udev.device_get_iscsi_name(self.data)
+ address = udev.device_get_iscsi_address(self.data)
+ port = udev.device_get_iscsi_port(self.data)
+ nic = udev.device_get_iscsi_nic(self.data)
+ kwargs["initiator"] = initiator
+ if initiator == self._iscsi.initiator:
+ node = self._iscsi.get_node(target, address, port, nic)
+ kwargs["node"] = node
+ kwargs["ibft"] = node in self._iscsi.ibft_nodes
+ kwargs["nic"] = self._iscsi.ifaces.get(node.iface, node.iface)
+ else:
+ # qla4xxx partial offload
+ kwargs["node"] = None
+ kwargs["ibft"] = False
+ kwargs["nic"] = "offload:not_accessible_via_iscsiadm"
+ kwargs["fw_address"] = address
+ kwargs["fw_port"] = port
+ kwargs["fw_name"] = name
+
+ log.info("%s is an iscsi disk", name)
+ return kwargs
+
+
+class FCoEDevicePopulator(DiskDevicePopulator):
+ priority = 20
+
+ _device_class = FcoeDiskDevice
+
+ @classmethod
+ def match(cls, data):
+ return (super().match(data) and
+ udev.device_is_fcoe(data))
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+ kwargs["nic"] = udev.device_get_fcoe_nic(self.data)
+ kwargs["identifier"] = udev.device_get_fcoe_identifier(self.data)
+ log.info("%s is an fcoe disk", udev.device_get_name(self.data))
+ return kwargs
+
+
+class MDBiosRaidDevicePopulator(DiskDevicePopulator):
+ priority = 20
+
+ _device_class = MDBiosRaidArrayDevice
+
+ @classmethod
+ def match(cls, data):
+ return (super().match(data) and
+ udev.device_get_md_container(data))
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+ parent_path = udev.device_get_md_container(self.data)
+ parent_name = device_path_to_name(parent_path)
+ container = self._populator.devicetree.get_device_by_name(parent_name)
+
+ # FIXME: Move this whole block to an add_parent_devices method or similar
+ if not container:
+ parent_sys_name = blockdev.md.node_from_name(parent_name)
+ container_sysfs = "/sys/class/block/" + parent_sys_name
+ container_info = udev.get_device(container_sysfs)
+ if not container_info:
+ log.error("failed to find md container %s at %s",
+ parent_name, container_sysfs)
+ return
+
+ self._populator.add_udev_device(container_info)
+ container = self._populator.devicetree.get_device_by_name(parent_name)
+ if not container:
+ log.error("failed to scan md container %s", parent_name)
+ return
+
+ kwargs["parents"] = [container]
+ kwargs["level"] = udev.device_get_md_level(self.data)
+ kwargs["member_devices"] = udev.device_get_md_devices(self.data)
+ kwargs["uuid"] = udev.device_get_md_uuid(self.data)
+ kwargs["exists"] = True
+ # remove some kwargs that don't make sense for md
+ del kwargs["model"]
+ del kwargs["serial"]
+ del kwargs["vendor"]
+ del kwargs["bus"]
+ return kwargs
+
+
+class DASDDevicePopulator(DiskDevicePopulator):
+ priority = 20
+
+ _device_class = DASDDevice
+
+ @classmethod
+ def match(cls, data):
+ return (super().match(data) and
+ udev.device_is_dasd(data))
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+ kwargs["busid"] = udev.device_get_dasd_bus_id(self.data)
+ kwargs["opts"] = {}
+ for attr in ['readonly', 'use_diag', 'erplog', 'failfast']:
+ kwargs["opts"][attr] = udev.device_get_dasd_flag(self.data, attr)
+
+ log.info("%s is a dasd device", udev.device_get_name(self.data))
+ return kwargs
+
+
+class ZFCPDevicePopulator(DiskDevicePopulator):
+ priority = 20
+
+ _device_class = ZFCPDiskDevice
+
+ @classmethod
+ def match(cls, data):
+ return (super().match(data) and
+ udev.device_is_zfcp(data))
+
+ def _get_kwargs(self):
+ kwargs = super()._get_kwargs()
+
+ for attr in ['hba_id', 'wwpn', 'fcp_lun']:
+ kwargs[attr] = udev.device_get_zfcp_attribute(self.data, attr=attr)
+
+ log.info("%s is a zfcp device", udev.device_get_name(self.data))
+ return kwargs
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/832fe67f6e8d0cb1a38e84b86410...
8 years, 5 months
[master 6/27] Move device-type-specific populator bits to populator
helpers.
by dashea
From: David Lehman <dlehman(a)redhat.com>
---
blivet/populator/helpers/__init__.py | 8 +
blivet/populator/helpers/devicepopulator.py | 5 +-
blivet/populator/helpers/disk.py | 148 +++++++++++
blivet/populator/helpers/dm.py | 67 +++++
blivet/populator/helpers/formatpopulator.py | 4 +-
blivet/populator/helpers/loop.py | 53 ++++
blivet/populator/helpers/lvm.py | 56 +++++
blivet/populator/helpers/mdraid.py | 81 ++++++
blivet/populator/helpers/multipath.py | 58 +++++
blivet/populator/helpers/optical.py | 46 ++++
blivet/populator/helpers/partition.py | 121 +++++++++
blivet/populator/helpers/populatorhelper.py | 2 +-
blivet/populator/populator.py | 374 ++--------------------------
13 files changed, 668 insertions(+), 355 deletions(-)
create mode 100644 blivet/populator/helpers/disk.py
create mode 100644 blivet/populator/helpers/dm.py
create mode 100644 blivet/populator/helpers/loop.py
create mode 100644 blivet/populator/helpers/lvm.py
create mode 100644 blivet/populator/helpers/mdraid.py
create mode 100644 blivet/populator/helpers/multipath.py
create mode 100644 blivet/populator/helpers/optical.py
create mode 100644 blivet/populator/helpers/partition.py
diff --git a/blivet/populator/helpers/__init__.py b/blivet/populator/helpers/__init__.py
index e69de29..b90a5a5 100644
--- a/blivet/populator/helpers/__init__.py
+++ b/blivet/populator/helpers/__init__.py
@@ -0,0 +1,8 @@
+from .disk import DiskDevicePopulator
+from .dm import DMDevicePopulator
+from .loop import LoopDevicePopulator
+from .lvm import LVMDevicePopulator
+from .mdraid import MDDevicePopulator
+from .multipath import MultipathDevicePopulator
+from .optical import OpticalDevicePopulator
+from .partition import PartitionDevicePopulator
diff --git a/blivet/populator/helpers/devicepopulator.py b/blivet/populator/helpers/devicepopulator.py
index 14e23ac..f96acc3 100644
--- a/blivet/populator/helpers/devicepopulator.py
+++ b/blivet/populator/helpers/devicepopulator.py
@@ -1,4 +1,4 @@
-# populator/helpers/device.py
+# populator/helpers/devicepopulator.py
# Base class for device-type-specific helpers for populating a DeviceTree.
#
# Copyright (C) 2009-2015 Red Hat, Inc.
@@ -20,7 +20,8 @@
# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
#
-from .helper import PopulatorHelper
+from .populatorhelper import PopulatorHelper
+
# pylint: disable=abstract-method
class DevicePopulator(PopulatorHelper):
diff --git a/blivet/populator/helpers/disk.py b/blivet/populator/helpers/disk.py
new file mode 100644
index 0000000..aa50da1
--- /dev/null
+++ b/blivet/populator/helpers/disk.py
@@ -0,0 +1,148 @@
+# populator/helpers/disk.py
+# Disk backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+import gi
+gi.require_version("BlockDev", "1.0")
+
+from gi.repository import BlockDev as blockdev
+
+from ... import udev
+from ... import util
+from ...devices import DASDDevice, DiskDevice, FcoeDiskDevice, iScsiDiskDevice
+from ...devices import MDBiosRaidArrayDevice, ZFCPDiskDevice
+from ...devices import device_path_to_name
+from ...storage_log import log_method_call
+from .devicepopulator import DevicePopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class DiskDevicePopulator(DevicePopulator):
+ priority = 10
+
+ @classmethod
+ def match(cls, data):
+ return (udev.device_is_disk(data) and
+ not udev.device_is_cdrom(data) and
+ not udev.device_is_partition(data) and
+ not udev.device_is_dm(data) and
+ not (udev.device_is_md(data) and not udev.device_get_md_container(data)))
+
+ def run(self):
+ name = udev.device_get_name(self.data)
+ log_method_call(self, name=name)
+ sysfs_path = udev.device_get_sysfs_path(self.data)
+ serial = udev.device_get_serial(self.data)
+ bus = udev.device_get_bus(self.data)
+
+ vendor = util.get_sysfs_attr(sysfs_path, "device/vendor")
+ model = util.get_sysfs_attr(sysfs_path, "device/model")
+
+ kwargs = {"serial": serial, "vendor": vendor, "model": model, "bus": bus}
+ if udev.device_is_iscsi(self.data) and not self._populator._cleanup:
+ disk_type = iScsiDiskDevice
+ initiator = udev.device_get_iscsi_initiator(self.data)
+ target = udev.device_get_iscsi_name(self.data)
+ address = udev.device_get_iscsi_address(self.data)
+ port = udev.device_get_iscsi_port(self.data)
+ nic = udev.device_get_iscsi_nic(self.data)
+ kwargs["initiator"] = initiator
+ if initiator == self._populator.iscsi.initiator:
+ node = self._populator.iscsi.get_node(target, address, port, nic)
+ kwargs["node"] = node
+ kwargs["ibft"] = node in self._populator.iscsi.ibft_nodes
+ kwargs["nic"] = self._populator.iscsi.ifaces.get(node.iface, node.iface)
+ log.info("%s is an iscsi disk", name)
+ else:
+ # qla4xxx partial offload
+ kwargs["node"] = None
+ kwargs["ibft"] = False
+ kwargs["nic"] = "offload:not_accessible_via_iscsiadm"
+ kwargs["fw_address"] = address
+ kwargs["fw_port"] = port
+ kwargs["fw_name"] = name
+ elif udev.device_is_fcoe(self.data):
+ disk_type = FcoeDiskDevice
+ kwargs["nic"] = udev.device_get_fcoe_nic(self.data)
+ kwargs["identifier"] = udev.device_get_fcoe_identifier(self.data)
+ log.info("%s is an fcoe disk", name)
+ elif udev.device_get_md_container(self.data):
+ name = udev.device_get_md_name(self.data)
+ disk_type = MDBiosRaidArrayDevice
+ parent_path = udev.device_get_md_container(self.data)
+ parent_name = device_path_to_name(parent_path)
+ container = self._populator.devicetree.get_device_by_name(parent_name)
+ if not container:
+ parent_sys_name = blockdev.md.node_from_name(parent_name)
+ container_sysfs = "/sys/class/block/" + parent_sys_name
+ container_info = udev.get_device(container_sysfs)
+ if not container_info:
+ log.error("failed to find md container %s at %s",
+ parent_name, container_sysfs)
+ return
+
+ self._populator.add_udev_device(container_info)
+ container = self._populator.devicetree.get_device_by_name(parent_name)
+ if not container:
+ log.error("failed to scan md container %s", parent_name)
+ return
+
+ kwargs["parents"] = [container]
+ kwargs["level"] = udev.device_get_md_level(self.data)
+ kwargs["member_devices"] = udev.device_get_md_devices(self.data)
+ kwargs["uuid"] = udev.device_get_md_uuid(self.data)
+ kwargs["exists"] = True
+ del kwargs["model"]
+ del kwargs["serial"]
+ del kwargs["vendor"]
+ del kwargs["bus"]
+ elif udev.device_is_dasd(self.data) and not self._populator._cleanup:
+ disk_type = DASDDevice
+ kwargs["busid"] = udev.device_get_dasd_bus_id(self.data)
+ kwargs["opts"] = {}
+
+ for attr in ['readonly', 'use_diag', 'erplog', 'failfast']:
+ kwargs["opts"][attr] = udev.device_get_dasd_flag(self.data, attr)
+
+ log.info("%s is a dasd device", name)
+ elif udev.device_is_zfcp(self.data):
+ disk_type = ZFCPDiskDevice
+
+ for attr in ['hba_id', 'wwpn', 'fcp_lun']:
+ kwargs[attr] = udev.device_get_zfcp_attribute(self.data, attr=attr)
+
+ log.info("%s is a zfcp device", name)
+ else:
+ disk_type = DiskDevice
+ log.info("%s is a disk", name)
+
+ device = disk_type(name,
+ major=udev.device_get_major(self.data),
+ minor=udev.device_get_minor(self.data),
+ sysfs_path=sysfs_path, **kwargs)
+
+ if disk_type == DASDDevice:
+ self._populator.dasd.append(device)
+
+ self._populator.devicetree._add_device(device)
+ return device
diff --git a/blivet/populator/helpers/dm.py b/blivet/populator/helpers/dm.py
new file mode 100644
index 0000000..2e90d68
--- /dev/null
+++ b/blivet/populator/helpers/dm.py
@@ -0,0 +1,67 @@
+# populator/helpers/dm.py
+# Device-mapper backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from ... import udev
+from ...devices import DMDevice
+from ...devicelibs import lvm
+from ...storage_log import log_method_call
+from .devicepopulator import DevicePopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class DMDevicePopulator(DevicePopulator):
+ priority = 50
+
+ @classmethod
+ def match(cls, data):
+ return (udev.device_is_dm(data) and
+ not udev.device_is_dm_partition(data) and
+ not udev.device_is_dm_lvm(data) and
+ not udev.device_is_dm_mpath(data) and
+ not udev.device_is_dm_raid(data))
+
+ def run(self):
+ name = udev.device_get_name(self.data)
+ log_method_call(self, name=name)
+ sysfs_path = udev.device_get_sysfs_path(self.data)
+ slave_devices = self._populator._add_slave_devices(self.data)
+ device = self._populator.devicetree.get_device_by_name(name)
+
+ # create a device for the livecd OS image(s)
+ if device is None and udev.device_is_dm_livecd(self.data):
+ device = DMDevice(name, dm_uuid=self.data.get('DM_UUID'),
+ sysfs_path=sysfs_path, exists=True,
+ parents=[slave_devices[0]])
+ device.protected = True
+ device.controllable = False
+ self._populator.devicetree._add_device(device)
+
+ # if we get here, we found all of the slave devices and
+ # something must be wrong -- if all of the slaves are in
+ # the tree, this device should be as well
+ if device is None:
+ lvm.lvm_cc_addFilterRejectRegexp(name)
+ log.warning("ignoring dm device %s", name)
+
+ return device
diff --git a/blivet/populator/helpers/formatpopulator.py b/blivet/populator/helpers/formatpopulator.py
index e19dfab..ea4b3d3 100644
--- a/blivet/populator/helpers/formatpopulator.py
+++ b/blivet/populator/helpers/formatpopulator.py
@@ -1,4 +1,4 @@
-# populator/helpers/format.py
+# populator/helpers/formatpopulator.py
# Base classes for type-specific helpers for populating a DeviceTree.
#
# Copyright (C) 2009-2015 Red Hat, Inc.
@@ -24,7 +24,7 @@
from ... import udev
from ...errors import FSError
-from .helper import PopulatorHelper
+from .populatorhelper import PopulatorHelper
import logging
log = logging.getLogger("blivet")
diff --git a/blivet/populator/helpers/loop.py b/blivet/populator/helpers/loop.py
new file mode 100644
index 0000000..5f1cf2b
--- /dev/null
+++ b/blivet/populator/helpers/loop.py
@@ -0,0 +1,53 @@
+# populator/helpers/loop.py
+# Loop device backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from ... import udev
+from ...devices import FileDevice, LoopDevice
+from ...storage_log import log_method_call
+from .devicepopulator import DevicePopulator
+
+
+class LoopDevicePopulator(DevicePopulator):
+ @classmethod
+ def match(cls, data):
+ return udev.device_is_loop(data)
+
+ def run(self):
+ name = udev.device_get_name(self.data)
+ log_method_call(self, name=name)
+ sysfs_path = udev.device_get_sysfs_path(self.data)
+ backing_file = blockdev.loop.get_backing_file(name)
+ file_device = self._populator.devicetree.get_device_by_name(backing_file)
+ if not file_device:
+ file_device = FileDevice(backing_file, exists=True)
+ self._populator.devicetree._add_device(file_device)
+ device = LoopDevice(name,
+ parents=[file_device],
+ sysfs_path=sysfs_path,
+ exists=True)
+ if not self._populator._cleanup or file_device not in self._populator.disk_images.values():
+ # don't allow manipulation of loop devices other than those
+ # associated with disk images, and then only during cleanup
+ file_device.controllable = False
+ device.controllable = False
+ self._populator.devicetree._add_device(device)
+ return device
diff --git a/blivet/populator/helpers/lvm.py b/blivet/populator/helpers/lvm.py
new file mode 100644
index 0000000..5b389fa
--- /dev/null
+++ b/blivet/populator/helpers/lvm.py
@@ -0,0 +1,56 @@
+# populator/helpers/lvm.py
+# LVM backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from ... import udev
+from ...devices import LVMVolumeGroupDevice
+from ...storage_log import log_method_call
+from .devicepopulator import DevicePopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class LVMDevicePopulator(DevicePopulator):
+ @classmethod
+ def match(cls, data):
+ return udev.device_is_dm_lvm(data)
+
+ def run(self):
+ name = udev.device_get_name(self.data)
+ log_method_call(self, name=name)
+
+ vg_name = udev.device_get_lv_vg_name(self.data)
+ device = self._populator.devicetree.get_device_by_name(vg_name, hidden=True)
+ if device and not isinstance(device, LVMVolumeGroupDevice):
+ log.warning("found non-vg device with name %s", vg_name)
+ device = None
+
+ self._populator._add_slave_devices(self.data)
+
+ # LVM provides no means to resolve conflicts caused by duplicated VG
+ # names, so we're just being optimistic here. Woo!
+ vg_name = udev.device_get_lv_vg_name(self.data)
+ vg_device = self._populator.devicetree.get_device_by_name(vg_name)
+ if not vg_device:
+ log.error("failed to find vg '%s' after scanning pvs", vg_name)
+
+ return self._populator.devicetree.get_device_by_name(name)
diff --git a/blivet/populator/helpers/mdraid.py b/blivet/populator/helpers/mdraid.py
new file mode 100644
index 0000000..1ed790b
--- /dev/null
+++ b/blivet/populator/helpers/mdraid.py
@@ -0,0 +1,81 @@
+# populator/helpers/mdraid.py
+# MD RAID backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+import gi
+gi.require_version("BlockDev", "1.0")
+
+from gi.repository import BlockDev as blockdev
+
+from ... import udev
+from ...flags import flags
+from ...storage_log import log_method_call
+from .devicepopulator import DevicePopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class MDDevicePopulator(DevicePopulator):
+ @classmethod
+ def match(cls, data):
+ return (udev.device_is_md(data) and
+ not udev.device_get_md_container(data))
+
+ def run(self):
+ name = udev.device_get_md_name(self.data)
+ log_method_call(self, name=name)
+
+ self._populator._add_slave_devices(self.data)
+
+ # try to get the device again now that we've got all the slaves
+ device = self._populator.devicetree.get_device_by_name(name, incomplete=flags.allow_imperfect_devices)
+
+ if device is None:
+ try:
+ uuid = udev.device_get_md_uuid(self.data)
+ except KeyError:
+ log.warning("failed to obtain uuid for mdraid device")
+ else:
+ device = self._populator.devicetree.get_device_by_uuid(uuid, incomplete=flags.allow_imperfect_devices)
+
+ if device and name:
+ # update the device instance with the real name in case we had to
+ # look it up by something other than name
+ device.name = name
+
+ if device is None:
+ # if we get here, we found all of the slave devices and
+ # something must be wrong -- if all of the slaves are in
+ # the tree, this device should be as well
+ if name is None:
+ name = udev.device_get_name(self.data)
+ path = "/dev/" + name
+ else:
+ path = "/dev/md/" + name
+
+ log.error("failed to scan md array %s", name)
+ try:
+ blockdev.md.deactivate(path)
+ except blockdev.MDRaidError:
+ log.error("failed to stop broken md array %s", name)
+
+ return device
diff --git a/blivet/populator/helpers/multipath.py b/blivet/populator/helpers/multipath.py
new file mode 100644
index 0000000..accc6c9
--- /dev/null
+++ b/blivet/populator/helpers/multipath.py
@@ -0,0 +1,58 @@
+# populator/helpers/multipath.py
+# Multipath backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from ... import udev
+from ...devices import MultipathDevice
+from ...errors import DeviceTreeError
+from ...storage_log import log_method_call
+from .devicepopulator import DevicePopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class MultipathDevicePopulator(DevicePopulator):
+ @classmethod
+ def match(cls, data):
+ return (udev.device_is_dm_mpath(data) and
+ not udev.device_is_dm_partition(data))
+
+ def run(self):
+ name = udev.device_get_name(self.data)
+ log_method_call(self, name=name)
+
+ slave_devices = self._populator._add_slave_devices(self.data)
+
+ device = None
+ if slave_devices:
+ try:
+ serial = self.data["DM_UUID"].split("-", 1)[1]
+ except (IndexError, AttributeError):
+ log.error("multipath device %s has no DM_UUID", name)
+ raise DeviceTreeError("multipath %s has no DM_UUID" % name)
+
+ device = MultipathDevice(name, parents=slave_devices,
+ sysfs_path=udev.device_get_sysfs_path(self.data),
+ serial=serial)
+ self._populator.devicetree._add_device(device)
+
+ return device
diff --git a/blivet/populator/helpers/optical.py b/blivet/populator/helpers/optical.py
new file mode 100644
index 0000000..c0466bf
--- /dev/null
+++ b/blivet/populator/helpers/optical.py
@@ -0,0 +1,46 @@
+# populator.py
+# Backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from ... import udev
+from ...devices import OpticalDevice
+from ...storage_log import log_method_call
+from .devicepopulator import DevicePopulator
+
+
+class OpticalDevicePopulator(DevicePopulator):
+ @classmethod
+ def match(cls, data):
+ return udev.device_is_cdrom(data)
+
+ def run(self):
+ log_method_call(self)
+ # XXX should this be RemovableDevice instead?
+ #
+ # Looks like if it has ID_INSTANCE=0:1 we can ignore it.
+ device = OpticalDevice(udev.device_get_name(self.data),
+ major=udev.device_get_major(self.data),
+ minor=udev.device_get_minor(self.data),
+ sysfs_path=udev.device_get_sysfs_path(self.data),
+ vendor=udev.device_get_vendor(self.data),
+ model=udev.device_get_model(self.data))
+ self._populator.devicetree._add_device(device)
+ return device
diff --git a/blivet/populator/helpers/partition.py b/blivet/populator/helpers/partition.py
new file mode 100644
index 0000000..52d94f7
--- /dev/null
+++ b/blivet/populator/helpers/partition.py
@@ -0,0 +1,121 @@
+# populator/helpers/partition.py
+# Partition backend code for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+import os
+
+import gi
+gi.require_version("BlockDev", "1.0")
+from gi.repository import BlockDev as blockdev
+
+from ... import udev
+from ...devicelibs import lvm
+from ...devices import PartitionDevice
+from ...errors import CorruptGPTError, DeviceError, DiskLabelScanError
+from ...storage_log import log_method_call
+from .devicepopulator import DevicePopulator
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class PartitionDevicePopulator(DevicePopulator):
+ priority = 0
+
+ @classmethod
+ def match(cls, data):
+ return (udev.device_is_partition(data) or udev.device_is_dm_partition(data))
+
+ def run(self):
+ name = udev.device_get_name(self.data)
+ log_method_call(self, name=name)
+ sysfs_path = udev.device_get_sysfs_path(self.data)
+
+ if name.startswith("md"):
+ name = blockdev.md.name_from_node(name)
+ device = self._populator.devicetree.get_device_by_name(name)
+ if device:
+ return device
+
+ disk_name = udev.device_get_partition_disk(self.data)
+ if disk_name.startswith("md"):
+ disk_name = blockdev.md.name_from_node(disk_name)
+ disk = self._populator.devicetree.get_device_by_name(disk_name)
+
+ if disk is None:
+ # create a device instance for the disk
+ new_info = udev.get_device(os.path.dirname(sysfs_path))
+ if new_info:
+ self._populator.add_udev_device(new_info)
+ disk = self._populator.devicetree.get_device_by_name(disk_name)
+
+ if disk is None:
+ # if the current device is still not in
+ # the tree, something has gone wrong
+ log.error("failure scanning device %s", disk_name)
+ lvm.lvm_cc_addFilterRejectRegexp(name)
+ return
+
+ if not disk.partitioned:
+ # Ignore partitions on:
+ # - devices we do not support partitioning of, like logical volumes
+ # - devices that do not have a usable disklabel
+ # - devices that contain disklabels made by isohybrid
+ #
+ if disk.partitionable and \
+ disk.format.type != "iso9660" and \
+ not disk.format.hidden and \
+ not self._populator._is_ignored_disk(disk):
+ if self.data.get("ID_PART_TABLE_TYPE") == "gpt":
+ msg = "corrupt gpt disklabel on disk %s" % disk.name
+ cls = CorruptGPTError
+ else:
+ msg = "failed to scan disk %s" % disk.name
+ cls = DiskLabelScanError
+
+ raise cls(msg)
+
+ # there's no need to filter partitions on members of multipaths or
+ # fwraid members from lvm since multipath and dmraid are already
+ # active and lvm should therefore know to ignore them
+ if not disk.format.hidden:
+ lvm.lvm_cc_addFilterRejectRegexp(name)
+
+ log.debug("ignoring partition %s on %s", name, disk.format.type)
+ return
+
+ device = None
+ try:
+ device = PartitionDevice(name, sysfs_path=sysfs_path,
+ major=udev.device_get_major(self.data),
+ minor=udev.device_get_minor(self.data),
+ exists=True, parents=[disk])
+ except DeviceError as e:
+ # corner case sometime the kernel accepts a partition table
+ # which gets rejected by parted, in this case we will
+ # prompt to re-initialize the disk, so simply skip the
+ # faulty partitions.
+ # XXX not sure about this
+ log.error("Failed to instantiate PartitionDevice: %s", e)
+ return
+
+ self._populator.devicetree._add_device(device)
+ return device
diff --git a/blivet/populator/helpers/populatorhelper.py b/blivet/populator/helpers/populatorhelper.py
index edcd0da..5d1b66d 100644
--- a/blivet/populator/helpers/populatorhelper.py
+++ b/blivet/populator/helpers/populatorhelper.py
@@ -1,4 +1,4 @@
-# populator/helpers/helper.py
+# populator/helpers/populatorhelper.py
# Base classes for type-specific helpers for populating a DeviceTree.
#
# Copyright (C) 2009-2015 Red Hat, Inc.
diff --git a/blivet/populator/populator.py b/blivet/populator/populator.py
index aae5c3d..b0b16c9 100644
--- a/blivet/populator/populator.py
+++ b/blivet/populator/populator.py
@@ -32,17 +32,17 @@
from gi.repository import BlockDev as blockdev
-from ..errors import CorruptGPTError, DeviceError, DeviceTreeError, DiskLabelScanError, DuplicateVGError, FSError, InvalidDiskLabelError, LUKSError
+from ..errors import DeviceError, DeviceTreeError, DuplicateVGError, FSError, InvalidDiskLabelError, LUKSError
from ..devices import BTRFSSubVolumeDevice, BTRFSVolumeDevice, BTRFSSnapShotDevice
-from ..devices import DASDDevice, DMDevice, DMLinearDevice, DMRaidArrayDevice, DiskDevice
-from ..devices import FcoeDiskDevice, FileDevice, LoopDevice, LUKSDevice
+from ..devices import DMLinearDevice, DMRaidArrayDevice
+from ..devices import FileDevice, LoopDevice, LUKSDevice
from ..devices import LVMLogicalVolumeDevice, LVMVolumeGroupDevice
from ..devices import LVMThinPoolDevice, LVMThinLogicalVolumeDevice
from ..devices import LVMSnapShotDevice, LVMThinSnapShotDevice
-from ..devices import MDRaidArrayDevice, MDBiosRaidArrayDevice
+from ..devices import MDRaidArrayDevice
from ..devices import MDContainerDevice
-from ..devices import MultipathDevice, OpticalDevice
-from ..devices import PartitionDevice, ZFCPDiskDevice, iScsiDiskDevice
+from ..devices import MultipathDevice
+from ..devices import PartitionDevice
from ..devices import device_path_to_name
from ..devices.lvm import get_internal_lv_class
from .. import formats
@@ -56,6 +56,10 @@
from ..i18n import _
from ..size import Size
+from .helpers import DiskDevicePopulator, DMDevicePopulator, LoopDevicePopulator
+from .helpers import LVMDevicePopulator, MDDevicePopulator, MultipathDevicePopulator
+from .helpers import OpticalDevicePopulator, PartitionDevicePopulator
+
import logging
log = logging.getLogger("blivet")
@@ -279,340 +283,6 @@ def _add_slave_devices(self, info):
return slave_devices
- def add_udev_lv_device(self, info):
- name = udev.device_get_name(info)
- log_method_call(self, name=name)
-
- vg_name = udev.device_get_lv_vg_name(info)
- device = self.get_device_by_name(vg_name, hidden=True)
- if device and not isinstance(device, LVMVolumeGroupDevice):
- log.warning("found non-vg device with name %s", vg_name)
- device = None
-
- self._add_slave_devices(info)
-
- # LVM provides no means to resolve conflicts caused by duplicated VG
- # names, so we're just being optimistic here. Woo!
- vg_name = udev.device_get_lv_vg_name(info)
- vg_device = self.get_device_by_name(vg_name)
- if not vg_device:
- log.error("failed to find vg '%s' after scanning pvs", vg_name)
-
- return self.get_device_by_name(name)
-
- def add_udev_dm_device(self, info):
- name = udev.device_get_name(info)
- log_method_call(self, name=name)
- sysfs_path = udev.device_get_sysfs_path(info)
- slave_devices = self._add_slave_devices(info)
- device = self.get_device_by_name(name)
-
- # if this is a luks device whose map name is not what we expect,
- # fix up the map name and see if that sorts us out
- handle_luks = (udev.device_is_dm_luks(info) and
- (self._cleanup or not flags.installer_mode))
- if device is None and handle_luks and slave_devices:
- slave_dev = slave_devices[0]
- slave_dev.format.map_name = name
- slave_info = udev.get_device(slave_dev.sysfs_path)
- self.handle_udev_luks_format(slave_info, slave_dev)
-
- # try once more to get the device
- device = self.get_device_by_name(name)
-
- # create a device for the livecd OS image(s)
- if device is None and udev.device_is_dm_livecd(info):
- device = DMDevice(name, dm_uuid=info.get('DM_UUID'),
- sysfs_path=sysfs_path, exists=True,
- parents=[slave_devices[0]])
- device.protected = True
- device.controllable = False
- self.devicetree._add_device(device)
-
- # if we get here, we found all of the slave devices and
- # something must be wrong -- if all of the slaves are in
- # the tree, this device should be as well
- if device is None:
- lvm.lvm_cc_addFilterRejectRegexp(name)
- log.warning("ignoring dm device %s", name)
-
- return device
-
- def add_udev_multipath_device(self, info):
- name = udev.device_get_name(info)
- log_method_call(self, name=name)
-
- slave_devices = self._add_slave_devices(info)
-
- device = None
- if slave_devices:
- try:
- serial = info["DM_UUID"].split("-", 1)[1]
- except (IndexError, AttributeError):
- log.error("multipath device %s has no DM_UUID", name)
- raise DeviceTreeError("multipath %s has no DM_UUID" % name)
-
- device = MultipathDevice(name, parents=slave_devices,
- sysfs_path=udev.device_get_sysfs_path(info),
- serial=serial)
- self.devicetree._add_device(device)
-
- return device
-
- def add_udev_md_device(self, info):
- name = udev.device_get_md_name(info)
- log_method_call(self, name=name)
-
- self._add_slave_devices(info)
-
- # try to get the device again now that we've got all the slaves
- device = self.get_device_by_name(name, incomplete=flags.allow_imperfect_devices)
-
- if device is None:
- try:
- uuid = udev.device_get_md_uuid(info)
- except KeyError:
- log.warning("failed to obtain uuid for mdraid device")
- else:
- device = self.get_device_by_uuid(uuid, incomplete=flags.allow_imperfect_devices)
-
- if device and name:
- # update the device instance with the real name in case we had to
- # look it up by something other than name
- device.name = name
-
- if device is None:
- # if we get here, we found all of the slave devices and
- # something must be wrong -- if all of the slaves are in
- # the tree, this device should be as well
- if name is None:
- name = udev.device_get_name(info)
- path = "/dev/" + name
- else:
- path = "/dev/md/" + name
-
- log.error("failed to scan md array %s", name)
- try:
- blockdev.md.deactivate(path)
- except blockdev.MDRaidError:
- log.error("failed to stop broken md array %s", name)
-
- return device
-
- def add_udev_partition_device(self, info, disk=None):
- name = udev.device_get_name(info)
- log_method_call(self, name=name)
- sysfs_path = udev.device_get_sysfs_path(info)
-
- if name.startswith("md"):
- name = blockdev.md.name_from_node(name)
- device = self.get_device_by_name(name)
- if device:
- return device
-
- if disk is None:
- disk_name = os.path.basename(os.path.dirname(sysfs_path))
- disk_name = disk_name.replace('!', '/')
- if disk_name.startswith("md"):
- disk_name = blockdev.md.name_from_node(disk_name)
-
- disk = self.get_device_by_name(disk_name)
-
- if disk is None:
- # create a device instance for the disk
- new_info = udev.get_device(os.path.dirname(sysfs_path))
- if new_info:
- self.add_udev_device(new_info)
- disk = self.get_device_by_name(disk_name)
-
- if disk is None:
- # if the current device is still not in
- # the tree, something has gone wrong
- log.error("failure scanning device %s", disk_name)
- lvm.lvm_cc_addFilterRejectRegexp(name)
- return
-
- if not disk.partitioned:
- # Ignore partitions on:
- # - devices we do not support partitioning of, like logical volumes
- # - devices that do not have a usable disklabel
- # - devices that contain disklabels made by isohybrid
- #
- if disk.partitionable and \
- disk.format.type != "iso9660" and \
- not disk.format.hidden and \
- not self._is_ignored_disk(disk):
- if info.get("ID_PART_TABLE_TYPE") == "gpt":
- msg = "corrupt gpt disklabel on disk %s" % disk.name
- cls = CorruptGPTError
- else:
- msg = "failed to scan disk %s" % disk.name
- cls = DiskLabelScanError
-
- raise cls(msg)
-
- # there's no need to filter partitions on members of multipaths or
- # fwraid members from lvm since multipath and dmraid are already
- # active and lvm should therefore know to ignore them
- if not disk.format.hidden:
- lvm.lvm_cc_addFilterRejectRegexp(name)
-
- log.debug("ignoring partition %s on %s", name, disk.format.type)
- return
-
- device = None
- try:
- device = PartitionDevice(name, sysfs_path=sysfs_path,
- major=udev.device_get_major(info),
- minor=udev.device_get_minor(info),
- exists=True, parents=[disk])
- except DeviceError as e:
- # corner case sometime the kernel accepts a partition table
- # which gets rejected by parted, in this case we will
- # prompt to re-initialize the disk, so simply skip the
- # faulty partitions.
- # XXX not sure about this
- log.error("Failed to instantiate PartitionDevice: %s", e)
- return
-
- self.devicetree._add_device(device)
- return device
-
- def add_udev_disk_device(self, info):
- name = udev.device_get_name(info)
- log_method_call(self, name=name)
- sysfs_path = udev.device_get_sysfs_path(info)
- serial = udev.device_get_serial(info)
- bus = udev.device_get_bus(info)
-
- vendor = util.get_sysfs_attr(sysfs_path, "device/vendor")
- model = util.get_sysfs_attr(sysfs_path, "device/model")
-
- kwargs = {"serial": serial, "vendor": vendor, "model": model, "bus": bus}
- if udev.device_is_iscsi(info) and not self._cleanup:
- disk_type = iScsiDiskDevice
- initiator = udev.device_get_iscsi_initiator(info)
- target = udev.device_get_iscsi_name(info)
- address = udev.device_get_iscsi_address(info)
- port = udev.device_get_iscsi_port(info)
- nic = udev.device_get_iscsi_nic(info)
- kwargs["initiator"] = initiator
- if initiator == self.iscsi.initiator:
- node = self.iscsi.get_node(target, address, port, nic)
- kwargs["node"] = node
- kwargs["ibft"] = node in self.iscsi.ibft_nodes
- kwargs["nic"] = self.iscsi.ifaces.get(node.iface, node.iface)
- log.info("%s is an iscsi disk", name)
- else:
- # qla4xxx partial offload
- kwargs["node"] = None
- kwargs["ibft"] = False
- kwargs["nic"] = "offload:not_accessible_via_iscsiadm"
- kwargs["fw_address"] = address
- kwargs["fw_port"] = port
- kwargs["fw_name"] = name
- elif udev.device_is_fcoe(info):
- disk_type = FcoeDiskDevice
- kwargs["nic"] = udev.device_get_fcoe_nic(info)
- kwargs["identifier"] = udev.device_get_fcoe_identifier(info)
- log.info("%s is an fcoe disk", name)
- elif udev.device_get_md_container(info):
- name = udev.device_get_md_name(info)
- disk_type = MDBiosRaidArrayDevice
- parent_path = udev.device_get_md_container(info)
- parent_name = device_path_to_name(parent_path)
- container = self.get_device_by_name(parent_name)
- if not container:
- parent_sys_name = blockdev.md.node_from_name(parent_name)
- container_sysfs = "/sys/class/block/" + parent_sys_name
- container_info = udev.get_device(container_sysfs)
- if not container_info:
- log.error("failed to find md container %s at %s",
- parent_name, container_sysfs)
- return
-
- self.add_udev_device(container_info)
- container = self.get_device_by_name(parent_name)
- if not container:
- log.error("failed to scan md container %s", parent_name)
- return
-
- kwargs["parents"] = [container]
- kwargs["level"] = udev.device_get_md_level(info)
- kwargs["member_devices"] = udev.device_get_md_devices(info)
- kwargs["uuid"] = udev.device_get_md_uuid(info)
- kwargs["exists"] = True
- del kwargs["model"]
- del kwargs["serial"]
- del kwargs["vendor"]
- del kwargs["bus"]
- elif udev.device_is_dasd(info) and not self._cleanup:
- disk_type = DASDDevice
- kwargs["busid"] = udev.device_get_dasd_bus_id(info)
- kwargs["opts"] = {}
-
- for attr in ['readonly', 'use_diag', 'erplog', 'failfast']:
- kwargs["opts"][attr] = udev.device_get_dasd_flag(info, attr)
-
- log.info("%s is a dasd device", name)
- elif udev.device_is_zfcp(info):
- disk_type = ZFCPDiskDevice
-
- for attr in ['hba_id', 'wwpn', 'fcp_lun']:
- kwargs[attr] = udev.device_get_zfcp_attribute(info, attr=attr)
-
- log.info("%s is a zfcp device", name)
- else:
- disk_type = DiskDevice
- log.info("%s is a disk", name)
-
- device = disk_type(name,
- major=udev.device_get_major(info),
- minor=udev.device_get_minor(info),
- sysfs_path=sysfs_path, **kwargs)
-
- if disk_type == DASDDevice:
- self.dasd.append(device)
-
- self.devicetree._add_device(device)
- return device
-
- def add_udev_optical_device(self, info):
- log_method_call(self)
- # XXX should this be RemovableDevice instead?
- #
- # Looks like if it has ID_INSTANCE=0:1 we can ignore it.
- device = OpticalDevice(udev.device_get_name(info),
- major=udev.device_get_major(info),
- minor=udev.device_get_minor(info),
- sysfs_path=udev.device_get_sysfs_path(info),
- vendor=udev.device_get_vendor(info),
- model=udev.device_get_model(info))
- self.devicetree._add_device(device)
- return device
-
- def add_udev_loop_device(self, info):
- name = udev.device_get_name(info)
- log_method_call(self, name=name)
- sysfs_path = udev.device_get_sysfs_path(info)
- sys_file = "%s/loop/backing_file" % sysfs_path
- backing_file = open(sys_file).read().strip()
- file_device = self.get_device_by_name(backing_file)
- if not file_device:
- file_device = FileDevice(backing_file, exists=True)
- self.devicetree._add_device(file_device)
- device = LoopDevice(name,
- parents=[file_device],
- sysfs_path=sysfs_path,
- exists=True)
- if not self._cleanup or file_device not in self.disk_images.values():
- # don't allow manipulation of loop devices other than those
- # associated with disk images, and then only during cleanup
- file_device.controllable = False
- device.controllable = False
- self.devicetree._add_device(device)
- return device
-
def add_udev_device(self, info, update_orig_fmt=False):
"""
:param :class:`pyudev.Device` info: udev info for the device
@@ -700,40 +370,46 @@ def add_udev_device(self, info, update_orig_fmt=False):
# The first step is to either look up or create the device
#
device_added = True
+ helper_class = None
if device:
device_added = False
elif udev.device_is_loop(info):
log.info("%s is a loop device", name)
- device = self.add_udev_loop_device(info)
+ device = LoopDevicePopulator(self, info).run()
elif udev.device_is_dm_mpath(info) and \
not udev.device_is_dm_partition(info):
log.info("%s is a multipath device", name)
- device = self.add_udev_multipath_device(info)
+ helper_class = MultipathDevicePopulator
elif udev.device_is_dm_lvm(info):
log.info("%s is an lvm logical volume", name)
- device = self.add_udev_lv_device(info)
+ helper_class = LVMDevicePopulator
elif udev.device_is_dm(info):
log.info("%s is a device-mapper device", name)
- device = self.add_udev_dm_device(info)
+ helper_class = DMDevicePopulator
elif udev.device_is_md(info) and not udev.device_get_md_container(info):
log.info("%s is an md device", name)
- device = self.add_udev_md_device(info)
+ helper_class = MDDevicePopulator
elif udev.device_is_cdrom(info):
log.info("%s is a cdrom", name)
- device = self.add_udev_optical_device(info)
+ helper_class = OpticalDevicePopulator
elif udev.device_is_disk(info):
- device = self.add_udev_disk_device(info)
+ helper_class = DiskDevicePopulator
elif udev.device_is_partition(info):
log.info("%s is a partition", name)
- device = self.add_udev_partition_device(info)
+ helper_class = PartitionDevicePopulator
else:
log.error("Unknown block device type for: %s", name)
return
+ if helper_class is not None:
+ device = helper_class(self, info).run()
+
if not device:
log.debug("no device obtained for %s", name)
return
+ log.info("got device: %r", device)
+
# If this device is read-only, mark it as such now.
if self.udev_device_is_disk(info) and \
util.get_sysfs_attr(udev.device_get_sysfs_path(info), 'ro') == '1':
@@ -759,8 +435,6 @@ def add_udev_device(self, info, update_orig_fmt=False):
if parent.name not in self.exclusive_disks:
self.exclusive_disks.append(parent.name)
- log.info("got device: %r", device)
-
# now handle the device's formatting
self.handle_udev_device_format(info, device)
if device_added or update_orig_fmt:
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/46fc277b4c48a01aa83df7001c85...
8 years, 5 months
[master 5/27] Add base classes for type-specific Populator helpers.
by dashea
From: David Lehman <dlehman(a)redhat.com>
The main components of each class are the 'match' class method and
the 'run' method. The 'match' method is used to determine which
helper class is appropriate for the given udev data set. The 'run'
method is what instantiates the StorageDevice or DeviceFormat
instance and updates the DeviceTree as necessary.
---
blivet/populator/helpers/__init__.py | 0
blivet/populator/helpers/devicepopulator.py | 34 ++++++++++
blivet/populator/helpers/formatpopulator.py | 98 +++++++++++++++++++++++++++++
blivet/populator/helpers/populatorhelper.py | 57 +++++++++++++++++
4 files changed, 189 insertions(+)
create mode 100644 blivet/populator/helpers/__init__.py
create mode 100644 blivet/populator/helpers/devicepopulator.py
create mode 100644 blivet/populator/helpers/formatpopulator.py
create mode 100644 blivet/populator/helpers/populatorhelper.py
diff --git a/blivet/populator/helpers/__init__.py b/blivet/populator/helpers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/blivet/populator/helpers/devicepopulator.py b/blivet/populator/helpers/devicepopulator.py
new file mode 100644
index 0000000..14e23ac
--- /dev/null
+++ b/blivet/populator/helpers/devicepopulator.py
@@ -0,0 +1,34 @@
+# populator/helpers/device.py
+# Base class for device-type-specific helpers for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from .helper import PopulatorHelper
+
+# pylint: disable=abstract-method
+class DevicePopulator(PopulatorHelper):
+ """ Populator helper base class for devices.
+
+ Subclasses must define a match method and, if they want to instantiate
+ a device, a run method.
+ """
+ @classmethod
+ def match(cls, data):
+ return False
diff --git a/blivet/populator/helpers/formatpopulator.py b/blivet/populator/helpers/formatpopulator.py
new file mode 100644
index 0000000..e19dfab
--- /dev/null
+++ b/blivet/populator/helpers/formatpopulator.py
@@ -0,0 +1,98 @@
+# populator/helpers/format.py
+# Base classes for type-specific helpers for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+from ... import formats
+from ... import udev
+from ...errors import FSError
+
+from .helper import PopulatorHelper
+
+import logging
+log = logging.getLogger("blivet")
+
+
+class FormatPopulator(PopulatorHelper):
+ priority = 0
+ _type_specifier = None
+
+ def __init__(self, populator, data, device):
+ """
+ :param :class:`~.Populator` populator: the calling populator
+ :param :class:`pyudev.Device` data: udev data describing a device
+ :param device: device instance corresponding to the udev data
+ :type device: :class:`~.devices.StorageDevice`
+ """
+ super().__init__(populator, data)
+ self.device = device
+
+ @classmethod
+ def match(cls, data, device): # pylint: disable=arguments-differ,unused-argument
+ """ Return True if this helper is appropriate for the given device.
+
+ :param :class:`pyudev.Device` data: udev data describing a device
+ :param device: device instance corresponding to the udev data
+ :type device: :class:`~.devices.StorageDevice`
+ :returns: whether this class is appropriate for the specified device
+ :rtype: bool
+ """
+ ret = False
+ if cls is FormatPopulator:
+ ret = True
+ else:
+ format_class = formats.get_device_format_class(cls._type_specifier)
+ fmt_types = []
+ if format_class is not None:
+ fmt_types = format_class._udev_types[:]
+ if cls._type_specifier and cls._type_specifier not in fmt_types:
+ fmt_types.append(cls._type_specifier)
+
+ ret = (udev.device_get_format(data) in fmt_types)
+
+ return ret
+
+ def _get_kwargs(self):
+ """ Return a kwargs dict to pass to DeviceFormat constructor. """
+ kwargs = {"uuid": udev.device_get_uuid(self.data),
+ "label": udev.device_get_label(self.data),
+ "device": self.device.path,
+ "serial": udev.device_get_serial(self.data),
+ "exists": True}
+ return kwargs
+
+ def run(self):
+ """ Create a format instance and associate it with the device instance. """
+ kwargs = self._get_kwargs()
+ if self._type_specifier is not None:
+ type_spec = self._type_specifier
+ else:
+ type_spec = udev.device_get_format(self.data)
+
+ try:
+ log.info("type detected on '%s' is '%s'", self.device.name, type_spec)
+ self.device.format = formats.get_format(type_spec, **kwargs)
+ if self.device.format.type:
+ log.info("got format: %s", self.device.format)
+ except FSError:
+ log.warning("type '%s' on '%s' invalid, assuming no format",
+ type_spec, self.device.name)
+ self.device.format = formats.DeviceFormat(device=self.device.path, exists=True)
+ return
diff --git a/blivet/populator/helpers/populatorhelper.py b/blivet/populator/helpers/populatorhelper.py
new file mode 100644
index 0000000..edcd0da
--- /dev/null
+++ b/blivet/populator/helpers/populatorhelper.py
@@ -0,0 +1,57 @@
+# populator/helpers/helper.py
+# Base classes for type-specific helpers for populating a DeviceTree.
+#
+# Copyright (C) 2009-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 Lesser 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 Lesser General Public License for more details. You should have
+# received a copy of the GNU Lesser 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 Lesser General Public License and may only be used or
+# replicated with the express permission of Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+
+class PopulatorHelper:
+ """ Class to hold type-specific code for populating the devicetree. """
+
+ priority = 100
+ """ Higher priority value gets checked for match first. """
+
+ def __init__(self, populator, data):
+ """
+ :param :class:`~.Populator` populator: the calling populator
+ :param :class:`pyudev.Device` data: udev data describing a device
+ """
+ self._populator = populator
+ self.data = data
+
+ @classmethod
+ def match(cls, data):
+ """ Return True if this helper is appropriate for the given device.
+
+ :param :class:`pyudev.Device` data: udev data describing a device
+ :returns: whether this class is appropriate for the specified device
+ :rtype: bool
+ """
+ raise NotImplementedError()
+
+ def run(self):
+ """ Run type-specific processing.
+
+ For device handlers, this method should instantiate the appropriate
+ device type and add the instance to the device tree. For format
+ handlers, this method should instantiate the appropriate format
+ type, associate the instance with the appropriate device, and
+ perform all processing related to the device's formatting.
+ """
+ raise NotImplementedError()
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/c6313157d9578985cf821a9d2e7f...
8 years, 5 months
[master 4/27] Use the mpath_member ID_FS_TYPE set up by the multipath
udev
rules.
by dashea
From: David Lehman <dlehman(a)redhat.com>
---
blivet/formats/multipath.py | 2 +-
blivet/populator/populator.py | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/blivet/formats/multipath.py b/blivet/formats/multipath.py
index 064b171..938c5b4 100644
--- a/blivet/formats/multipath.py
+++ b/blivet/formats/multipath.py
@@ -38,7 +38,7 @@ class MultipathMember(DeviceFormat):
""" A multipath member disk. """
_type = "multipath_member"
_name = N_("multipath member device")
- _udev_types = ["multipath_member"]
+ _udev_types = ["mpath_member"]
_supported = True # is supported
_packages = ["device-mapper-multipath"] # required packages
_hidden = True # hide devices with this formatting?
diff --git a/blivet/populator/populator.py b/blivet/populator/populator.py
index 76073dc..aae5c3d 100644
--- a/blivet/populator/populator.py
+++ b/blivet/populator/populator.py
@@ -1361,8 +1361,6 @@ def handle_udev_device_format(self, info, device):
is_multipath_member = (device.is_disk and
blockdev.mpath_is_mpath_member(device.path))
- if is_multipath_member:
- format_type = "multipath_member"
# Now, if the device is a disk, see if there is a usable disklabel.
# If not, see if the user would like to create one.
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/a5c443f326b42bca8a3318c242a0...
8 years, 5 months