Add systemd dbus

This commit is contained in:
Pascal Vizeli
2018-04-21 23:56:36 +02:00
parent 28f295a1e2
commit 69142b6fb0
3 changed files with 45 additions and 39 deletions

View File

@@ -11,6 +11,7 @@ from voluptuous.humanize import humanize_error
from ..const import ( from ..const import (
JSON_RESULT, JSON_DATA, JSON_MESSAGE, RESULT_OK, RESULT_ERROR, JSON_RESULT, JSON_DATA, JSON_MESSAGE, RESULT_OK, RESULT_ERROR,
CONTENT_TYPE_BINARY) CONTENT_TYPE_BINARY)
from ..exceptions import HassioError
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -33,6 +34,8 @@ def api_process(method):
answer = await method(api, *args, **kwargs) answer = await method(api, *args, **kwargs)
except RuntimeError as err: except RuntimeError as err:
return api_return_error(message=str(err)) return api_return_error(message=str(err))
except HassioError:
return api_return_error()
if isinstance(answer, dict): if isinstance(answer, dict):
return api_return_ok(data=answer) return api_return_ok(data=answer)
@@ -45,30 +48,6 @@ def api_process(method):
return wrap_api return wrap_api
def api_process_hostcontrol(method):
"""Wrap HostControl calls to rest api."""
async def wrap_hostcontrol(api, *args, **kwargs):
"""Return host information."""
# pylint: disable=protected-access
if not api._host_control.active:
raise HTTPServiceUnavailable()
try:
answer = await method(api, *args, **kwargs)
except RuntimeError as err:
return api_return_error(message=str(err))
if isinstance(answer, dict):
return api_return_ok(data=answer)
elif answer is None:
return api_return_error("Function is not supported")
elif answer:
return api_return_ok()
return api_return_error()
return wrap_hostcontrol
def api_process_raw(content): def api_process_raw(content):
"""Wrap content_type into function.""" """Wrap content_type into function."""
def wrap_method(method): def wrap_method(method):
@@ -81,6 +60,9 @@ def api_process_raw(content):
except RuntimeError as err: except RuntimeError as err:
msg_data = str(err).encode() msg_data = str(err).encode()
msg_type = CONTENT_TYPE_BINARY msg_type = CONTENT_TYPE_BINARY
except HassioError:
msg_data = b''
msg_type = CONTENT_TYPE_BINARY
return web.Response(body=msg_data, content_type=msg_type) return web.Response(body=msg_data, content_type=msg_type)

View File

@@ -1,5 +1,5 @@
"""Interface to Systemd over dbus.""" """Interface to Systemd over dbus."""
from logging import logging
from ..exceptions import HassioInternalError from ..exceptions import HassioInternalError
from ..utils.gdbus import DBus, DBusError from ..utils.gdbus import DBus, DBusError
@@ -7,7 +7,7 @@ from ..utils.gdbus import DBus, DBusError
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DBUS_NAME = 'org.freedesktop.systemd1' DBUS_NAME = 'org.freedesktop.systemd1'
DBUS_OBJECT = '/org/freedesktop/systemd1/Manager' DBUS_OBJECT = '/org/freedesktop/systemd1'
class Systemd(object): class Systemd(object):
@@ -17,18 +17,22 @@ class Systemd(object):
"""Initialize systemd.""" """Initialize systemd."""
self.dbus = None self.dbus = None
async def load(self): @property
def is_connected(self):
"""Return True, if they is connected to dbus."""
return self.dbus is not None
async def connect(self):
"""Connect do bus.""" """Connect do bus."""
try: try:
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT) self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
except DBusError: except DBusError:
_LOGGER.warning("Can't connect to systemd") _LOGGER.warning("Can't connect to systemd")
return
async def reboot(self): async def reboot(self):
"""Reboot host computer.""" """Reboot host computer."""
try: try:
await self.dbus.Reboot() await self.dbus.Manager.Reboot()
except DBusError: except DBusError:
_LOGGER.error("Can't reboot host") _LOGGER.error("Can't reboot host")
raise HassioInternalError() from None raise HassioInternalError() from None
@@ -36,7 +40,7 @@ class Systemd(object):
async def shutdown(self): async def shutdown(self):
"""Shutdown host computer.""" """Shutdown host computer."""
try: try:
await self.dbus.PowerOff() await self.dbus.Manager.PowerOff()
except DBusError: except DBusError:
_LOGGER.error("Can't PowerOff host") _LOGGER.error("Can't PowerOff host")
raise HassioInternalError() from None raise HassioInternalError() from None

View File

@@ -8,8 +8,8 @@ _LOGGER = logging.getLogger(__name__)
INTROSPECT = ("gdbus introspect --system --dest {bus} " INTROSPECT = ("gdbus introspect --system --dest {bus} "
"--object-path {obj} --xml") "--object-path {obj} --xml")
CALL = ("gdbus call --system --dest {bus} --object-path {obj} " CALL = ("gdbus call --system --dest {bus} --object-path {inf} "
"--method {obj}.{method} {args}") "--method {inf}.{method} {args}")
class DBusError(Exception): class DBusError(Exception):
@@ -39,7 +39,7 @@ class DBus(object):
"""Initialize dbus object.""" """Initialize dbus object."""
self.bus_name = bus_name self.bus_name = bus_name
self.object_path = object_path self.object_path = object_path
self.methods = [] self.data = {}
@staticmethod @staticmethod
async def connect(bus_name, object_path): async def connect(bus_name, object_path):
@@ -73,19 +73,22 @@ class DBus(object):
raise DBusParseError() from None raise DBusParseError() from None
# Read available methods # Read available methods
for method in xml.findall(".//method"): for interface in xml.findall("/node/interface"):
self.methods.append(method.get('name')) methods = []
for method in interface.findall("/method"):
methods.append(method.get('name'))
self.data[interface.get('name')] = methods
@staticmethod @staticmethod
def _gvariant(raw): def _gvariant(raw):
"""Parse GVariant input to python.""" """Parse GVariant input to python."""
return raw return raw
async def _call_dbus(self, method, *args): async def call_dbus(self, interface, method, *args):
"""Call a dbus method.""" """Call a dbus method."""
command = shlex.split(CALL.format( command = shlex.split(CALL.format(
bus=self.bus_name, bus=self.bus_name,
obj=self.object_path, inf=interface,
method=method, method=method,
args=" ".join(map(str, args)) args=" ".join(map(str, args))
)) ))
@@ -124,9 +127,26 @@ class DBus(object):
# End # End
return data.decode() return data.decode()
def __getattr__(self, interface):
"""Mapping to dbus method."""
interface = f"{self.object_path}.{interface}"
if interface not in self.data:
raise AttributeError()
return DBusCallWrapper(self, interface)
class DBusCallWrapper(object):
"""Wrapper a DBus interface for a call."""
def __init__(self, dbus, interface):
"""Initialize wrapper."""
self.dbus = dbus
self.interface = interface
def __getattr__(self, name): def __getattr__(self, name):
"""Mapping to dbus method.""" """Mapping to dbus method."""
if name not in self.methods: if name not in self.dbus.data[self.interface]:
raise AttributeError() raise AttributeError()
def _method_wrapper(*args): def _method_wrapper(*args):
@@ -134,6 +154,6 @@ class DBus(object):
Return a coroutine Return a coroutine
""" """
return self._call_dbus(name, *args) return self.dbus.call_dbus(self.interface, self.name, *args)
return _method_wrapper return _method_wrapper