Skip to content
This repository has been archived by the owner on Apr 26, 2020. It is now read-only.

Commit

Permalink
First draft of DomainManager
Browse files Browse the repository at this point in the history
  • Loading branch information
kalkin committed Sep 27, 2016
1 parent 3232052 commit 82b079c
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 7 deletions.
3 changes: 3 additions & 0 deletions dbus-1/services/org.qubes.DomainManager1.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[D-BUS Service]
Name=org.qubes.DomainManager1
Exec=/usr/bin/python /usr/lib/python2.7/site-packages/qubesdbus/domain_manager.py
3 changes: 3 additions & 0 deletions qubesdbus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@

from .constants import * # pylint: disable=wildcard-import
from .proxy import QubesDbusProxy
from .domain_manager import DomainManager
from .constants import NAME_PREFIX, PATH_PREFIX, VERSION
from .exceptions import QubesDbusException
162 changes: 162 additions & 0 deletions qubesdbus/domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# -*- encoding: utf8 -*-
# pylint: disable=invalid-name
#
# The Qubes OS Project, https://www.qubes-os.org/
#
# Copyright (C) 2016 Bahtiar `kalkin-` Gadimov <[email protected]>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 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.

import logging

import dbus.service
from systemd.journal import JournalHandler

try:
# Check for mypy dependencies pylint: disable=ungrouped-imports
from typing import Any # pylint: disable=unused-import
except ImportError:
pass

DOMAIN_PROPERTIES = ['attached_volumes',
'autostart',
'backup_timestamp',
# 'block_devices',
'conf_file',
# 'connected_vms',
'default_dispvm',
'default_user',
# 'devices',
'dir_path',
'dir_path_prefix',
'dns',
'features',
'firewall_conf',
'hvm',
'icon_path',
'include_in_backups',
'installed_by_rpm',
'internal',
'ip',
'is_networked',
'is_halted',
'is_paused',
'is_running',
'is_qrexec_running',
'is_outdated',
'kernel',
'kernelopts',
'label',
'mac',
'maxmem',
'memory',
'name',
'netmask',
'netvm',
'qid',
'qrexec_timeout',
# 'storage',
# 'tags',
'template',
'updateable',
'uuid',
'vcpus',
# 'volumes',
'xid', ]

DOMAIN_STATE_PROPERTIES = ['is_halted',
'is_paused',
'is_running',
'is_qrexec_running', ]


class Domain(dbus.service.Object):
def __init__(self, domain, bus, bus_name, path_prefix):
self.bus_path = '/'.join([path_prefix, 'domains', str(domain.qid)])
self.bus_name = bus_name
self.bus = bus
self.properties = {'state': 'halted'}
self.qid = str(domain.qid)
logger_name = 'qubesdbus.domain.' + self.qid
self.log = logging.getLogger(logger_name)
self.log.addHandler(
JournalHandler(level=logging.DEBUG, SYSLOG_IDENTIFIER=logger_name))

for p_name in DOMAIN_PROPERTIES:
try:
value = getattr(domain, p_name)
if callable(value):
value = value()
if isinstance(value, dict):
value = dbus.Dictionary(value)
elif isinstance(value, bool):
value = dbus.Boolean(value)
else:
value = dbus.String(value)
if p_name in DOMAIN_STATE_PROPERTIES:
if value:
self.properties['state'] = p_name.split('_', 1)[1]
elif p_name.startswith("is_"):
_, new_name = p_name.split("_", 1)
self.properties[new_name] = value
else:
self.properties[p_name] = value
except AttributeError:
self.properties[p_name] = ''
dbus.service.Object.__init__(self, self.bus_name, self.bus_path)

@dbus.service.method(dbus_interface="org.freedesktop.DBus.Properties")
def Get(self, _, property_name):
return self.properties[property_name]

@dbus.service.method(dbus_interface="org.freedesktop.DBus.Properties")
def GetAll(self, _):
# According to the dbus spec we should be able to return types not only
# string, but it doesn't work. We need to serialize everything to string
# ☹
return dbus.Dictionary({k: dbus.String(v)
for k, v in self.properties.items()})

@dbus.service.method(dbus_interface="org.freedesktop.DBus.Properties")
def Set(self, _, name, value): # type: (str, dbus.String, Any) -> None
new_value = value
old_value = self.properties[name]
if new_value == old_value:
self.log.info('%s: Property %s not changed (%s)', self.qid, name,
old_value)
else:
self.properties[name] = value
self.PropertiesChanged("org.freedesktop.DBus.Properties",
{name: value}, [])

@dbus.service.signal(dbus_interface='org.freedesktop.DBus.Properties',
signature="sa{sv}as")
def PropertiesChanged(self, iface_name, changed_properties,
invalidated_properties=None):
# type: (str, Dict[dbus.String, Any], List[dbus.String]) -> None
for name, value in changed_properties.items():
self.log.debug('%s: Property %s changed %s', self.qid, name, value)
pass

@dbus.service.signal(
dbus_interface="org.qubes.DomainManager1.domains.Signals",
signature='s')
def StateSignal(self, name):
self.properties['state'] = name

@dbus.service.signal(
dbus_interface="org.qubes.DomainManager1.domains.Signals",
signature='s')
def StartingSignal(self, name):
self.properties['state'] = name
112 changes: 112 additions & 0 deletions qubesdbus/domain_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# -*- encoding: utf8 -*-
# pylint: disable=invalid-name
#
# The Qubes OS Project, https://www.qubes-os.org/
#
# Copyright (C) 2016 Bahtiar `kalkin-` Gadimov <[email protected]>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 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.

from __future__ import absolute_import, print_function

import logging
import sys

import dbus.service
import qubes
from gi.repository import GLib
from systemd.journal import JournalHandler

from qubesdbus.interface import _QubesDbusInterface
from qubesdbus.service import _DbusServiceObject
from qubesdbus.domain import Domain

try:
# Check for mypy dependencies pylint: disable=ungrouped-imports
from typing import Any # pylint: disable=unused-import
except ImportError:
pass

log = logging.getLogger('qubesdbus.DomainManager1')
log.addHandler(
JournalHandler(level=logging.DEBUG,
SYSLOG_IDENTIFIER='qubesdbus.domain_manager'))
log.propagate = True

INTERESTING_PROPERTIES = ['clockvm', 'default_dispvm', 'default_fw_netvm',
'default_kernel', 'default_netvm',
'default_template', 'updatevm']

class DomainManager(_DbusServiceObject, _QubesDbusInterface):
def __init__(self, app):
super(DomainManager, self).__init__()
self.properties = {} # type: Dict[str, Any]
self.identifier = str(app)
self.domains = self.proxify_domains(app.domains)
for p_name in INTERESTING_PROPERTIES:
try:
self.properties[p_name] = str(getattr(app, p_name))
except AttributeError:
self.properties[p_name] = ''

@dbus.service.method(dbus_interface="org.freedesktop.DBus.ObjectManager")
def GetManagedObjects(self):
''' Returns the domain objects paths and their supported interfaces and
properties.
'''
return {"%s/domains/%s" % (self.bus_path, d.qid):
# pylint: disable=protected-access
"%s.domains.%s" % (self.bus_name._name, d.qid)
for d in self.domains}

@dbus.service.method(dbus_interface="org.freedesktop.DBus.Properties")
def Get(self, _, property_name):
return self.properties[property_name]

@dbus.service.method(dbus_interface="org.freedesktop.DBus.Properties")
def GetAll(self, _):
return self.properties

@dbus.service.method(dbus_interface="org.freedesktop.DBus.Properties")
def Set(self, _, property_name, value):
log.info('%s: Property changed %s = %s', self.identifier,
property_name, value)
self.properties[property_name] = value

def proxify_domains(self, domains):
result = []
for vm in domains:
vm_proxy = Domain(vm, self.bus, self.bus_name, self.bus_path)
result.append(vm_proxy)
return result

@dbus.service.method(dbus_interface="org.qubes.Signals1",
in_signature='sava{sv}')
def ForwardSignal(self, event_name, args=None, kwargs=None):
log.warn('Unknown signal %s received %s', event_name, self.identifier)


def main(args=None):
''' Main function ''' # pylint: disable=unused-argument
loop = GLib.MainLoop()
app = qubes.Qubes()
_ = DomainManager(app)
print("Service running...")
loop.run()
print("Service stopped")
return 0


if __name__ == '__main__':
sys.exit(main())
25 changes: 25 additions & 0 deletions qubesdbus/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python2
# -*- encoding: utf8 -*-
#
# The Qubes OS Project, https://www.qubes-os.org/
#
# Copyright (C) 2016 Bahtiar `kalkin-` Gadimov <[email protected]>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 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.

''' qubesdbus exceptions '''

class QubesDbusException(Exception):
''' Base qubesdbus exception '''
pass
16 changes: 9 additions & 7 deletions qubesdbus/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,19 @@

import dbus
import dbus.mainloop.glib
from qubesdbus import NAME_PREFIX, PATH_PREFIX, VERSION

from .constants import NAME_PREFIX, PATH_PREFIX, VERSION

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)


class _DbusServiceObject(dbus.service.Object):
def __init__(self):
self.bus_path = ''.join([PATH_PREFIX, '/', self.__class__.__name__,
str(VERSION)])
self.bus_name = ''.join([NAME_PREFIX, '.', self.__class__.__name__,
str(VERSION)])
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.service.BusName(self.bus_name, dbus.SessionBus())
dbus.service.BusName(self.bus_name, dbus.SessionBus())
bus_name = ''.join([NAME_PREFIX, '.', self.__class__.__name__,
str(VERSION)])
self.bus = dbus.SessionBus()
self.bus_name = dbus.service.BusName(bus_name, self.bus)
# avoid pylint super-on-old-class error
dbus.service.Object.__init__(self, bus, self.bus_path)
dbus.service.Object.__init__(self, self.bus_name, self.bus_path)

0 comments on commit 82b079c

Please sign in to comment.