diff --git a/qubesmgmt/app.py b/qubesmgmt/app.py index 699656f..35268d1 100644 --- a/qubesmgmt/app.py +++ b/qubesmgmt/app.py @@ -28,6 +28,7 @@ import subprocess import qubesmgmt.base import qubesmgmt.vm +import qubesmgmt.label import qubesmgmt.exc import qubesmgmt.utils @@ -100,15 +101,19 @@ class VMCollection(object): return self._vm_list.keys() + class QubesBase(qubesmgmt.base.PropertyHolder): '''Main Qubes application''' #: domains (VMs) collection domains = None + #: labels collection + labels = None def __init__(self): super(QubesBase, self).__init__(self, 'mgmt.property.', 'dom0') self.domains = VMCollection(self) + self.labels = qubesmgmt.label.LabelsCollection(self) class QubesLocal(QubesBase): diff --git a/qubesmgmt/label.py b/qubesmgmt/label.py new file mode 100644 index 0000000..8d6e768 --- /dev/null +++ b/qubesmgmt/label.py @@ -0,0 +1,104 @@ +# -*- encoding: utf8 -*- +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2017 Marek Marczykowski-Górecki +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty 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, see . + +'''VM Labels''' + +import qubesmgmt.exc + +class Label(object): + '''Label definition for virtual machines + + Label specifies colour of the padlock displayed next to VM's name. + + :param str color: colour specification as in HTML (``#abcdef``) + :param str name: label's name like "red" or "green" + ''' + + def __init__(self, app, name): + self.app = app + self._name = name + self._color = None + + @property + def color(self): + '''color specification as in HTML (``#abcdef``)''' + if self._color is None: + try: + qubesd_response = self.app.qubesd_call( + 'dom0', 'mgmt.label.Get', self._name, None) + except qubesmgmt.exc.QubesDaemonNoResponseError: + raise AttributeError + self._color = qubesd_response.decode() + return self._color + + @property + def name(self): + '''label's name like "red" or "green"''' + return self._name + + def __str__(self): + return self._name + + +class LabelsCollection(object): + '''Collection of VMs objects''' + def __init__(self, app): + self.app = app + self._label_list = None + self._label_objects = {} + + def clear_cache(self): + '''Clear cached list of labels''' + self._label_list = None + + def refresh_cache(self, force=False): + '''Refresh cached list of VMs''' + if not force and self._label_list is not None: + return + label_list_data = self.app.qubesd_call('dom0', 'mgmt.label.List') + label_list_data = label_list_data.decode('ascii') + assert label_list_data[-1] == '\n' + self._label_list = label_list_data[:-1].splitlines() + + for name, label in list(self._label_objects.items()): + if label.name not in self._label_list: + # Label no longer exists + del self._label_objects[name] + + def __getitem__(self, item): + if item not in self: + raise KeyError(item) + if item not in self._label_objects: + self._label_objects[item] = Label(self.app, item) + return self._label_objects[item] + + def __contains__(self, item): + self.refresh_cache() + return item in self._label_list + + def __iter__(self): + self.refresh_cache() + for vm in self._label_list: + yield self[vm] + + def keys(self): + '''Get list of label names.''' + self.refresh_cache() + return self._label_list.keys() diff --git a/qubesmgmt/tests/tools/label.py b/qubesmgmt/tests/tools/label.py new file mode 100644 index 0000000..2635a44 --- /dev/null +++ b/qubesmgmt/tests/tools/label.py @@ -0,0 +1,53 @@ +# -*- encoding: utf8 -*- +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2017 Marek Marczykowski-Górecki +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty 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, see . + +import qubesmgmt.tests +from qubesmgmt.label import Label + + +class TC_00_Label(qubesmgmt.tests.QubesTestCase): + def test_000_list(self): + self.app.expected_calls[ + ('dom0', 'mgmt.label.List', None, None)] = \ + b'0\x00green\nred\nblack\n' + seen = set() + for label in self.app.labels: + self.assertNotIn(label.name, seen) + seen.add(label.name) + self.assertEqual(seen, set(['green', 'red', 'black'])) + + def test_010_get(self): + self.app.expected_calls[ + ('dom0', 'mgmt.label.List', None, None)] = \ + b'0\x00green\nred\nblack\n' + label = self.app.labels['green'] + self.assertIsInstance(label, Label) + self.assertEqual(label.name, 'green') + + def test_011_get_color(self): + self.app.expected_calls[ + ('dom0', 'mgmt.label.List', None, None)] = \ + b'0\x00green\nred\nblack\n' + self.app.expected_calls[ + ('dom0', 'mgmt.label.Get', 'green', None)] = \ + b'0\x000x00FF00' + label = self.app.labels['green'] + self.assertEqual(label.color, '0x00FF00') +