From: David Lehman <dlehman(a)redhat.com>
A subtree will be added at /com/redhat/Blivet1/Devices/ with entries
for individual devices using the device's id, eg:
/com/redhat/Blivet1/Devices/10
referring to the StorageDevice whose 'id' attribute is equal to 10.
---
blivet/dbus/__init__.py | 5 ---
blivet/dbus/blivet.py | 41 +++++++++++++++++++-----
blivet/dbus/constants.py | 2 ++
blivet/dbus/device.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++
dbus/blivetd | 6 ++--
tests/dbus_test.py | 61 ++++++++++++++++++++++++++---------
6 files changed, 168 insertions(+), 29 deletions(-)
create mode 100644 blivet/dbus/device.py
diff --git a/blivet/dbus/__init__.py b/blivet/dbus/__init__.py
index 6f2ea76..7e00e5a 100644
--- a/blivet/dbus/__init__.py
+++ b/blivet/dbus/__init__.py
@@ -17,8 +17,3 @@
#
# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
#
-
-from .object import DBusObject
-from .blivet import DBusBlivet
-
-__all__ = ["DBusObject", "DBusBlivet"]
diff --git a/blivet/dbus/blivet.py b/blivet/dbus/blivet.py
index 7c16cd3..659c389 100644
--- a/blivet/dbus/blivet.py
+++ b/blivet/dbus/blivet.py
@@ -23,6 +23,7 @@
from blivet import Blivet
from .constants import BLIVET_INTERFACE, BLIVET_OBJECT_PATH
+from .device import DBusDevice
from .object import DBusObject
@@ -30,12 +31,12 @@ class DBusBlivet(DBusObject):
""" This class provides the main entry point to the Blivet1 service.
It provides methods for controlling the blivet service and querying its
- state. It will eventually implement the org.freedesktop.DBus.ObjectManager
- interface once we export objects for devices, formats, and scheduled
- actions.
+ state.
"""
- def __init__(self):
+ def __init__(self, manager):
super().__init__()
+ self._dbus_devices = list()
+ self._manager = manager # provides ObjectManager interface
self._blivet = Blivet()
@property
@@ -51,22 +52,46 @@ def properties(self):
props = {"Devices": self.ListDevices()}
return props
+ def _device_removed(self, device):
+ """ Update ObjectManager interface after a device is removed. """
+ removed_object_path = DBusDevice.get_object_path_by_id(device.id)
+ removed = next((d for d in self._dbus_devices if d.object_path == removed_object_path))
+ self._manager.remove_object(removed)
+ self._dbus_devices.remove(removed)
+
+ def _device_added(self, device):
+ """ Update ObjectManager interface after a device is added. """
+ added = DBusDevice(device)
+ self._dbus_devices.append(added)
+ self._manager.add_object(added)
+
@dbus.service.method(dbus_interface=BLIVET_INTERFACE)
def Reset(self):
""" Reset the Blivet instance and populate the device tree. """
+ old_devices = self._blivet.devices[:]
self._blivet.reset()
+ for removed in old_devices:
+ self._device_removed(removed)
+
+ for added in self._blivet.devices:
+ self._device_added(added)
@dbus.service.method(dbus_interface=BLIVET_INTERFACE)
def Exit(self):
""" Stop the blivet service. """
sys.exit(0)
- @dbus.service.method(dbus_interface=BLIVET_INTERFACE, out_signature='as')
+ @dbus.service.method(dbus_interface=BLIVET_INTERFACE, out_signature='ao')
def ListDevices(self):
""" Return a list of strings describing the devices in this system. """
- return dbus.Array([str(d) for d in self._blivet.devices], signature='s')
+ return dbus.Array([d.object_path for d in self._dbus_devices], signature='o')
- @dbus.service.method(dbus_interface=BLIVET_INTERFACE, in_signature='s', out_signature='s')
+ @dbus.service.method(dbus_interface=BLIVET_INTERFACE, in_signature='s', out_signature='o')
def ResolveDevice(self, spec):
""" Return a string describing the device the given specifier resolves to. """
- return str(self._blivet.devicetree.resolve_device(spec) or "")
+ device = self._blivet.devicetree.resolve_device(spec)
+ object_path = ""
+ if device is not None:
+ dbus_device = next(d for d in self._dbus_devices if d._device == device)
+ object_path = dbus_device.object_path
+ return object_path
diff --git a/blivet/dbus/constants.py b/blivet/dbus/constants.py
index 2baf618..8ba6f2f 100644
--- a/blivet/dbus/constants.py
+++ b/blivet/dbus/constants.py
@@ -22,6 +22,8 @@
BASE_OBJECT_PATH = "/com/redhat/Blivet1"
BLIVET_INTERFACE = "%s.Blivet" % BUS_NAME
BLIVET_OBJECT_PATH = "%s/Blivet" % BASE_OBJECT_PATH
+DEVICE_INTERFACE = "%s.Device" % BUS_NAME
+DEVICE_OBJECT_PATH_BASE = "%s/Devices" % BASE_OBJECT_PATH
OBJECT_MANAGER_PATH = BASE_OBJECT_PATH
OBJECT_MANAGER_INTERFACE = "org.freedesktop.DBus.ObjectManager"
diff --git a/blivet/dbus/device.py b/blivet/dbus/device.py
new file mode 100644
index 0000000..e9ea330
--- /dev/null
+++ b/blivet/dbus/device.py
@@ -0,0 +1,82 @@
+#
+# Copyright (C) 2016 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+import dbus
+
+from .constants import DEVICE_INTERFACE, DEVICE_OBJECT_PATH_BASE
+from .object import DBusObject
+
+
+class DBusDevice(DBusObject):
+ def __init__(self, device):
+ self._device = device
+ self._object_path = self.get_object_path_by_id(self._device.id)
+ super().__init__()
+
+ @staticmethod
+ def get_object_path_by_id(object_id):
+ return "%s/%d" % (DEVICE_OBJECT_PATH_BASE, object_id)
+
+ @property
+ def object_path(self):
+ return self._object_path
+
+ @property
+ def interface(self):
+ return DEVICE_INTERFACE
+
+ @property
+ def properties(self):
+ props = {"Name": self._device.name,
+ "Path": self._device.path,
+ "Type": self._device.type,
+ "Size": dbus.UInt64(self._device.size),
+ "ID": self._device.id,
+ "UUID": self._device.uuid or "",
+ "Status": self._device.status or False,
+ "RaidLevel": self._get_raid_level(),
+ "Parents": dbus.Array((self.get_object_path_by_id(d.id) for d in self._device.parents), signature='o'),
+ "Children": dbus.Array((self.get_object_path_by_id(d.id) for d in self._device.children), signature='o'),
+ "FormatType": self._device.format.type or "",
+ "FormatUUID": self._device.format.uuid or "",
+ "FormatMountpoint": getattr(self._device.format, "mountpoint", "") or "",
+ "FormatLabel": getattr(self._device.format, "label", "") or "",
+ "FormatID": self._device.format.id,
+ }
+
+ return props
+
+ def _get_raid_level(self):
+ level = ""
+ if hasattr(self._device, "level"):
+ level = str(self._device.level)
+ elif hasattr(self._device, "data_level"):
+ level = str(self._device.data_level)
+
+ return level
+
+ @dbus.service.method(dbus_interface=DEVICE_INTERFACE)
+ def Setup(self):
+ """ Activate this device. """
+ self._device.setup()
+
+ @dbus.service.method(dbus_interface=DEVICE_INTERFACE)
+ def Teardown(self):
+ """ Deactivate this device. """
+ self._device.teardown()
diff --git a/dbus/blivetd b/dbus/blivetd
index f9f659f..6505a80 100755
--- a/dbus/blivetd
+++ b/dbus/blivetd
@@ -3,11 +3,13 @@ import dbus
import dbus.mainloop.glib
from gi.repository import GLib
-from blivet.dbus import DBusBlivet
+from blivet.dbus.blivet import DBusBlivet
+from blivet.dbus.manager import ObjectManager
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
system_bus = dbus.SystemBus()
mainloop = GLib.MainLoop()
-blivet_obj = DBusBlivet()
+manager = ObjectManager()
+blivet_obj = DBusBlivet(manager)
mainloop.run()
diff --git a/tests/dbus_test.py b/tests/dbus_test.py
index d6d0a90..9517a8a 100644
--- a/tests/dbus_test.py
+++ b/tests/dbus_test.py
@@ -1,40 +1,73 @@
-import dbus
+import random
from unittest import TestCase
-from unittest.mock import Mock, patch
+from unittest.mock import Mock, patch, sentinel
+
+import dbus
from blivet import Blivet
from blivet.dbus.blivet import DBusBlivet
-from blivet.dbus.blivet import DBusObject
-from blivet.dbus.constants import BLIVET_INTERFACE
+from blivet.dbus.device import DBusDevice
+from blivet.dbus.object import DBusObject
+from blivet.dbus.constants import BLIVET_INTERFACE, DEVICE_INTERFACE
class UDevBlivetTestCase(TestCase):
@patch.object(DBusObject, '__init__', return_value=None)
def setUp(self, *args): # pylint: disable=unused-argument
- self.dbus_object = DBusBlivet()
+ self.dbus_object = DBusBlivet(Mock(name="ObjectManager"))
self.dbus_object._blivet = Mock(spec=Blivet)
def test_ListDevices(self):
""" Verify that ListDevices returns what it should.
- It should return a dbus.Array w/ signature 's' containing the
- string representations of the contents of the underlying
- Blivet's devices property.
+ It should return a dbus.Array w/ signature 'o' containing the
+ dbus object path of each device in the DBusBlivet.
"""
- devices = ['a', 'b', 22, False]
- dbus_devices = dbus.Array((str(d) for d in devices), signature='s')
- self.dbus_object._blivet.devices = devices
- self.assertEqual(self.dbus_object.ListDevices(), dbus_devices)
+ object_paths = dbus.Array([sentinel.dev1, sentinel.dev2, sentinel.dev3], signature='o')
+ dbus_devices = [Mock(object_path=p) for p in object_paths]
+ self.dbus_object._dbus_devices = dbus_devices
+ self.assertEqual(self.dbus_object.ListDevices(), object_paths)
# now test the devices property for good measure. it should have the
# same value.
- self.assertEqual(self.dbus_object.Get(BLIVET_INTERFACE, 'Devices'),
- dbus_devices)
+ self.assertEqual(self.dbus_object.Get(BLIVET_INTERFACE, 'Devices'), object_paths)
self.dbus_object._blivet.devices = Mock()
def test_Reset(self):
""" Verify that Reset calls the underlying Blivet's reset method. """
self.dbus_object._blivet.reset_mock()
+ self.dbus_object._blivet.devices = []
self.dbus_object.Reset()
self.dbus_object._blivet.reset.assert_called_once_with()
self.dbus_object._blivet.reset_mock()
+
+
+class DBusObjectTestCase(TestCase):
+ @patch.object(DBusObject, '__init__', return_value=None)
+ def setUp(self, *args): # pylint: disable=unused-argument
+ self.obj = DBusObject()
+
+ def test_properties(self):
+ with self.assertRaises(NotImplementedError):
+ _x = self.obj.properties
+
+ with self.assertRaises(NotImplementedError):
+ _x = self.obj.interface
+
+ with self.assertRaises(NotImplementedError):
+ _x = self.obj.object_path
+
+
+class DBusDeviceTestCase(DBusObjectTestCase):
+ @patch.object(DBusObject, '__init__', return_value=None)
+ def setUp(self, *args):
+ self._device_id = random.randint(0, 500)
+ self.obj = DBusDevice(Mock(name="StorageDevice", id=self._device_id,
+ parents=[], children=[]))
+
+ @patch('dbus.UInt64')
+ def test_properties(self, *args): # pylint: disable=unused-argument
+ self.assertTrue(isinstance(self.obj.properties, dict))
+ self.assertEqual(self.obj.interface, DEVICE_INTERFACE)
+ self.assertEqual(self.obj.object_path,
+ self.obj.get_object_path_by_id(self._device_id))
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/aa96f2eb0a203d662c0dfb251f2795…
From: David Lehman <dlehman(a)redhat.com>
With this patch, the DBusBlivet object moves to
/com/redhat/Blivet1/Blivet to make room for the ObjectManager
at the root (/com/redhat/Blivet1).
---
blivet/dbus/constants.py | 8 +++++--
blivet/dbus/manager.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+), 2 deletions(-)
create mode 100644 blivet/dbus/manager.py
diff --git a/blivet/dbus/constants.py b/blivet/dbus/constants.py
index 3b8b905..2baf618 100644
--- a/blivet/dbus/constants.py
+++ b/blivet/dbus/constants.py
@@ -19,5 +19,9 @@
#
BUS_NAME = "com.redhat.Blivet1"
-BLIVET_OBJECT_PATH = "/com/redhat/Blivet1"
-BLIVET_INTERFACE = "com.redhat.Blivet1"
+BASE_OBJECT_PATH = "/com/redhat/Blivet1"
+BLIVET_INTERFACE = "%s.Blivet" % BUS_NAME
+BLIVET_OBJECT_PATH = "%s/Blivet" % BASE_OBJECT_PATH
+
+OBJECT_MANAGER_PATH = BASE_OBJECT_PATH
+OBJECT_MANAGER_INTERFACE = "org.freedesktop.DBus.ObjectManager"
diff --git a/blivet/dbus/manager.py b/blivet/dbus/manager.py
new file mode 100644
index 0000000..ee47461
--- /dev/null
+++ b/blivet/dbus/manager.py
@@ -0,0 +1,55 @@
+#
+# Copyright (C) 2016 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+import dbus
+
+from .constants import BUS_NAME, OBJECT_MANAGER_INTERFACE, OBJECT_MANAGER_PATH
+
+
+class ObjectManager(dbus.service.Object):
+ """ Class to implement org.freedesktop.DBus.ObjectManager interface.
+
+ Blivet's ObjectManager interface will manage subtrees for objects that
+ variously (and with mutual-exclusivity) implement blivet's Device,
+ Format, Action interfaces.
+ """
+ def __init__(self):
+ self._objects = list()
+ super().__init__(bus_name=dbus.service.BusName(BUS_NAME, dbus.SystemBus()),
+ object_path=OBJECT_MANAGER_PATH)
+
+ @dbus.service.method(dbus_interface=OBJECT_MANAGER_INTERFACE, out_signature='a{oa{sa{sv}}}')
+ def GetManagedObjects(self):
+ return dict((obj.object_path, {obj.interface: obj.properties}) for obj in self._objects)
+
+ def remove_object(self, obj):
+ self._objects.remove(obj)
+ self.InterfacesRemoved(obj.object_path, obj.interface)
+
+ def add_object(self, obj):
+ self._objects.append(obj)
+ self.InterfacesAdded(obj.object_path, {obj.interface: obj.properties})
+
+ @dbus.service.signal(dbus_interface=OBJECT_MANAGER_INTERFACE, signature='oa{sa{sv}}')
+ def InterfacesAdded(self, object_path, ifaces_props_dict):
+ pass
+
+ @dbus.service.signal(dbus_interface=OBJECT_MANAGER_INTERFACE, signature='oas')
+ def InterfacesRemoved(self, object_path, interfaces):
+ pass
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/152b4f56b6e7843eb6cff812cdb94b…
From: David Lehman <dlehman(a)redhat.com>
---
blivet/dbus/blivet.py | 4 +---
blivet/dbus/constants.py | 23 +++++++++++++++++++++++
blivet/dbus/object.py | 2 +-
3 files changed, 25 insertions(+), 4 deletions(-)
create mode 100644 blivet/dbus/constants.py
diff --git a/blivet/dbus/blivet.py b/blivet/dbus/blivet.py
index bf95ce1..b6485ab 100644
--- a/blivet/dbus/blivet.py
+++ b/blivet/dbus/blivet.py
@@ -22,11 +22,9 @@
import dbus
from blivet import Blivet
+from .constants import BLIVET_INTERFACE, BLIVET_OBJECT_PATH
from .object import DBusObject
-BLIVET_OBJECT_PATH = "/com/redhat/Blivet"
-BLIVET_INTERFACE = "com.redhat.Blivet1"
-
class DBusBlivet(DBusObject):
""" This class provides the main entry point to the Blivet1 service.
diff --git a/blivet/dbus/constants.py b/blivet/dbus/constants.py
new file mode 100644
index 0000000..3b8b905
--- /dev/null
+++ b/blivet/dbus/constants.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2016 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): David Lehman <dlehman(a)redhat.com>
+#
+
+BUS_NAME = "com.redhat.Blivet1"
+BLIVET_OBJECT_PATH = "/com/redhat/Blivet1"
+BLIVET_INTERFACE = "com.redhat.Blivet1"
diff --git a/blivet/dbus/object.py b/blivet/dbus/object.py
index 5d0fd17..27c8d76 100644
--- a/blivet/dbus/object.py
+++ b/blivet/dbus/object.py
@@ -21,7 +21,7 @@
import dbus
import dbus.service
-BUS_NAME = "com.redhat.Blivet1"
+from .constants import BUS_NAME
class DBusObject(dbus.service.Object):
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/4087d78b0a771614d3d392d664315e…
This adds a subtree `/com/redhat/Blivet1/Devices/` with individual devices' object paths being based on their `id`, eg: `/com/redhat/Blivet1/Devices/10`. As of now I don't see any need for various interfaces for device and format types, so all devices only implement `com.redhat.Blivet1.Device`.
I also added an `ObjectManager` interface at `/com/redhat/Blivet1` and moved the `Blivet` object to `/com/redhat/Blivet1/Blivet`. Other services use `Manager` for this, so I'm open to doing that instead if people think it would be better.
Things I already know I need to do:
- more unit tests
- class and module docstrings
- be a bit more stringent about checking return values, types, &c
--
To view this pull request on github, visit https://github.com/rhinstaller/blivet/pull/392