From 590c7dd55525f4911d2ec52df5f5387e0bfb7180 Mon Sep 17 00:00:00 2001
From: David Lehman <dlehman@redhat.com>
Date: Thu, 29 May 2014 14:34:03 -0500
Subject: [PATCH 18/18] Add a property indicating whether a device is directly
 accessible.

Directly accessible means there are no layers of abstraction/indirection
between the device and a user. An example of a non-direct device is an LVM
PV that is part of a VG. An example of a direct device is a partition that
contains swap space.

It used to be the case that direct was equivalent to isleaf, but no more.
Actual leaf devices, all btrfs subvolumes (including the top-level), and
snapshot source/origin devices can all be accessed directly by users.
---
 blivet/devices.py     | 32 ++++++++++++++++++++++++++++++++
 tests/devices_test.py | 11 +++++++++++
 2 files changed, 43 insertions(+)

diff --git a/blivet/devices.py b/blivet/devices.py
index d7c0ea4..9ec8e99 100644
--- a/blivet/devices.py
+++ b/blivet/devices.py
@@ -1061,6 +1061,11 @@ class StorageDevice(Device):
         return self._formatImmutable or self.protected
 
     @property
+    def direct(self):
+        """ Is this device directly accessible? """
+        return self.isleaf
+
+    @property
     def isDisk(self):
         return self._isDisk
 
@@ -1569,6 +1574,11 @@ class PartitionDevice(StorageDevice):
                                 (disklabel and disklabel.logicalPartitions))
         return (no_kids and not extended_has_logical)
 
+    @property
+    def direct(self):
+        """ Is this device directly accessible? """
+        return self.isleaf and not self.isExtended
+
     def _setFormat(self, fmt):
         """ Set the Device's format. """
         log_method_call(self, self.name)
@@ -2696,6 +2706,11 @@ class LVMVolumeGroupDevice(ContainerDevice):
 
         return self._complete or not self.exists
 
+    @property
+    def direct(self):
+        """ Is this device directly accessible? """
+        return False
+
     def populateKSData(self, data):
         super(LVMVolumeGroupDevice, self).populateKSData(data)
         data.vgname = self.name
@@ -3022,6 +3037,13 @@ class LVMLogicalVolumeDevice(DMDevice):
         return (super(LVMLogicalVolumeDevice, self).isleaf and
                 not non_thin_snapshots)
 
+    @property
+    def direct(self):
+        """ Is this device directly accessible? """
+        # an LV can contain a direct filesystem if it is a leaf device or if
+        # its only dependent devices are snapshots
+        return super(LVMLogicalVolumeDevice, self).isleaf
+
     def dracutSetupArgs(self):
         # Note no mapName usage here, this is a lvm cmdline name, which
         # is different (ofcourse)
@@ -3357,6 +3379,11 @@ class LVMThinPoolDevice(LVMLogicalVolumeDevice):
     def dracutSetupArgs(self):
         return set()
 
+    @property
+    def direct(self):
+        """ Is this device directly accessible? """
+        return False
+
     def populateKSData(self, data):
         super(LVMThinPoolDevice, self).populateKSData(data)
         data.thin_pool = True
@@ -4929,6 +4956,11 @@ class BTRFSDevice(StorageDevice):
         return self.parents[0].path if self.parents else None
 
     @property
+    def direct(self):
+        """ Is this device directly accessible? """
+        return True
+
+    @property
     def fstabSpec(self):
         if self.format.volUUID:
             spec = "UUID=%s" % self.format.volUUID
diff --git a/tests/devices_test.py b/tests/devices_test.py
index e2ea29d..addd736 100644
--- a/tests/devices_test.py
+++ b/tests/devices_test.py
@@ -494,9 +494,13 @@ class BTRFSDeviceTestCase(DeviceStateTestCase):
            "dev1", parents=parents)
 
         self.assertEqual(self.dev1.isleaf, False)
+        self.assertEqual(self.dev1.direct, True)
         self.assertEqual(self.dev2.isleaf, True)
+        self.assertEqual(self.dev2.direct, True)
+
         member = self.dev1.parents[0]
         self.assertEqual(member.isleaf, False)
+        self.assertEqual(member.direct, False)
 
     def testBTRFSDeviceMethods(self):
         """Test for method calls on initialized BTRFS Devices."""
@@ -543,7 +547,9 @@ class BTRFSDeviceTestCase(DeviceStateTestCase):
         vol.exists = True
         snap = BTRFSSnapShotDevice("snap1", parents=[vol], source=vol)
         self.assertEqual(snap.isleaf, True)
+        self.assertEqual(snap.direct, True)
         self.assertEqual(vol.isleaf, False)
+        self.assertEqual(vol.direct, True)
 
         self.assertEqual(snap.dependsOn(vol), True)
         self.assertEqual(vol.dependsOn(snap), False)
@@ -584,7 +590,9 @@ class LVMDeviceTest(unittest.TestCase):
         self.assertEqual(snap1.format, lv.format)
 
         self.assertEqual(snap1.isleaf, True)
+        self.assertEqual(snap1.direct, True)
         self.assertEqual(lv.isleaf, False)
+        self.assertEqual(lv.direct, True)
 
         self.assertEqual(snap1.dependsOn(lv), True)
         self.assertEqual(lv.dependsOn(snap1), False)
@@ -616,11 +624,14 @@ class LVMDeviceTest(unittest.TestCase):
         thinlv.exists = True
         snap1 = LVMThinSnapShotDevice("snap1", parents=[pool], origin=thinlv)
         self.assertEqual(snap1.isleaf, True)
+        self.assertEqual(snap1.direct, True)
         self.assertEqual(thinlv.isleaf, True)
+        self.assertEqual(thinlv.direct, True)
 
         self.assertEqual(snap1.dependsOn(thinlv), True)
         self.assertEqual(thinlv.dependsOn(snap1), False)
 
+        # existing thin snapshots do not depend on their origin
         snap1.exists = True
         self.assertEqual(snap1.dependsOn(thinlv), False)
 
-- 
1.9.0

