-
Notifications
You must be signed in to change notification settings - Fork 18
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # DbusNetwork driver | ||
|
|
||
| The DbusNetwork driver is a driver for transparently accessing the dbus on the remote machine. | ||
|
|
||
| ## Driver configuration | ||
|
|
||
| ```{literalinclude} dbus.yaml | ||
| :language: yaml | ||
| ``` | ||
|
|
||
| ```{doctest} | ||
| :hide: | ||
| >>> from jumpstarter.config import ExporterConfigV1Alpha1DriverInstance | ||
| >>> ExporterConfigV1Alpha1DriverInstance.from_path("source/api-reference/drivers/dbus.yaml").instantiate() | ||
| DbusNetwork(...) | ||
| ``` | ||
|
|
||
| ## Client API | ||
|
|
||
| ```{eval-rst} | ||
| .. autoclass:: jumpstarter_driver_network.client.DbusNetworkClient() | ||
| :members: | ||
| ``` | ||
|
|
||
| Get machine id of the remote machine | ||
|
|
||
| ```{doctest} | ||
| >>> with dbus: | ||
| ... print(subprocess.run([ | ||
| ... "busctl", | ||
| ... "call", | ||
| ... "org.freedesktop.systemd1", | ||
| ... "/org/freedesktop/systemd1", | ||
| ... "org.freedesktop.DBus.Peer", | ||
| ... "GetMachineId" | ||
| ... ], stdout=subprocess.PIPE).stdout.decode()) # s "34df62c767c846d5a93eb2d6f05d9e1d" | ||
| s ... | ||
| ``` | ||
|
|
||
| ```{testsetup} * | ||
| from jumpstarter_driver_network.driver import DbusNetwork | ||
| from jumpstarter.common.utils import serve | ||
| import subprocess | ||
|
|
||
| instance = serve(DbusNetwork(kind="session")) | ||
|
|
||
| dbus = instance.__enter__() | ||
| ``` | ||
|
|
||
| ```{testcleanup} * | ||
| instance.__exit__(None, None, None) | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| type: "jumpstarter_driver_network.driver.DbusNetwork" | ||
| config: | ||
| kind: "system" # which bus to connect to, system or session |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,4 +17,5 @@ snmp.md | |
| tftp.md | ||
| ustreamer.md | ||
| yepkit.md | ||
| dbus.md | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,14 @@ | ||
| from .dbus import DbusAdapter | ||
| from .fabric import FabricAdapter | ||
| from .novnc import NovncAdapter | ||
| from .pexpect import PexpectAdapter | ||
| from .portforward import TcpPortforwardAdapter, UnixPortforwardAdapter | ||
|
|
||
| __all__ = ["FabricAdapter", "NovncAdapter", "PexpectAdapter", "TcpPortforwardAdapter", "UnixPortforwardAdapter"] | ||
| __all__ = [ | ||
| "DbusAdapter", | ||
| "FabricAdapter", | ||
| "NovncAdapter", | ||
| "PexpectAdapter", | ||
| "TcpPortforwardAdapter", | ||
| "UnixPortforwardAdapter", | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| from dataclasses import dataclass | ||
| from os import environ, getenv | ||
|
|
||
| from .portforward import TcpPortforwardAdapter | ||
|
|
||
|
|
||
| @dataclass(kw_only=True) | ||
| class DbusAdapter(TcpPortforwardAdapter): | ||
| async def __aenter__(self): | ||
| addr = await super().__aenter__() | ||
| match self.client.kind: | ||
| case "system": | ||
| self.varname = "DBUS_SYSTEM_BUS_ADDRESS" | ||
| pass | ||
| case "session": | ||
| self.varname = "DBUS_SESSION_BUS_ADDRESS" | ||
| pass | ||
| case _: | ||
| raise ValueError(f"invalid bus type: {self.client.kind}") | ||
| self.oldenv = getenv(self.varname) | ||
| environ[self.varname] = f"tcp:host={addr[0]},port={addr[1]}" | ||
|
|
||
| async def __aexit__(self, exc_type, exc_value, traceback): | ||
| await super().__aexit__(exc_type, exc_value, traceback) | ||
| if self.oldenv is None: | ||
| del environ[self.varname] | ||
| else: | ||
| environ[self.varname] = self.oldenv |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,22 @@ | ||
| from contextlib import AbstractContextManager | ||
|
|
||
| from .adapters import DbusAdapter | ||
| from .driver import DbusNetwork | ||
| from jumpstarter.client import DriverClient | ||
|
|
||
|
|
||
| class NetworkClient(DriverClient): | ||
| pass | ||
|
|
||
|
|
||
| class DbusNetworkClient(NetworkClient, AbstractContextManager): | ||
| def __enter__(self): | ||
| self.adapter = DbusAdapter(client=self) | ||
| self.adapter.__enter__() | ||
|
|
||
| def __exit__(self, exc_type, exc_value, traceback): | ||
| self.adapter.__exit__(exc_type, exc_value, traceback) | ||
|
|
||
| @property | ||
| def kind(self): | ||
| return self.labels[DbusNetwork.KIND_LABEL] |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,8 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from abc import ABCMeta, abstractmethod | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from contextlib import asynccontextmanager | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from dataclasses import dataclass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from dataclasses import dataclass, field | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from os import getenv, getuid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import ClassVar, Literal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from anyio import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| connect_tcp, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -102,6 +104,76 @@ async def connect(self): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yield stream | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @dataclass(kw_only=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class DbusNetwork(NetworkInterface, Driver): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kind: Literal["system", "session"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| scheme: str | None = field(init=False, default=None) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| args: dict[str, str] = field(init=False, default_factory=dict) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| KIND_LABEL: ClassVar[str] = "jumpstarter.dev/dbusnetwork/kind" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @classmethod | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def client(cls) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "jumpstarter_driver_network.client.DbusNetworkClient" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def extra_labels(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {self.KIND_LABEL: self.kind} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __post_init__(self): # noqa: C901 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if hasattr(super(), "__post_init__"): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super().__post_init__() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match self.kind: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "system": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bus = getenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "session": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bus = getenv("DBUS_SESSION_BUS_ADDRESS", f"unix:path=/run/user/{getuid()}/bus") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case _: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"invalid bus type: {self.kind}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.scheme, sep, rem = bus.partition(":") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not sep: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"invalid bus addr: {bus}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for part in rem.split(","): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key, sep, value = part.partition("=") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not sep: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"invalid bus addr: {bus}, missing separator in arguments") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.args[key] = value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match self.scheme: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "unix": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if "path" not in self.args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"invalid bus addr: {bus}, missing path argument") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "tcp": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if "host" not in self.args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"invalid bus addr: {bus}, missing host argument") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if "port" not in self.args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"invalid bus addr: {bus}, missing port argument") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| port = int(self.args["port"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"invalid bus addr: {bus}, invalid port argument") from e | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.args["port"] = port | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case _: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError(f"invalid bus scheme: {self.scheme}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @exportstream | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @asynccontextmanager | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def connect(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match self.scheme: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "unix": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.logger.debug("Connecting UDS path=%s", self.args["path"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with await connect_unix(path=self.args["path"]) as stream: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yield stream | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "tcp": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.logger.debug("Connecting TCP host=%s port=%d", self.args["host"], self.args["port"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with await connect_tcp(remote_host=self.args["host"], remote_port=self.args["port"]) as stream: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yield stream | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+163
to
+175
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling for connection failures. The connection logic should handle potential network errors and provide meaningful error messages. @exportstream
@asynccontextmanager
async def connect(self):
+ try:
match self.scheme:
case "unix":
self.logger.debug("Connecting UDS path=%s", self.args["path"])
async with await connect_unix(path=self.args["path"]) as stream:
yield stream
case "tcp":
self.logger.debug("Connecting TCP host=%s port=%d", self.args["host"], self.args["port"])
async with await connect_tcp(remote_host=self.args["host"], remote_port=self.args["port"]) as stream:
yield stream
+ except ConnectionError as e:
+ self.logger.error("Failed to connect to %s DBus: %s", self.kind, e)
+ raise ValueError(f"Failed to connect to {self.kind} DBus: {e}") from e📝 Committable suggestion
Suggested change
🛠️ Refactor suggestion Consider adding error handling for connection failures. The @exportstream
@asynccontextmanager
async def connect(self):
+ try:
match self.scheme:
case "unix":
self.logger.debug("Connecting UDS path=%s", self.args["path"])
async with await connect_unix(path=self.args["path"]) as stream:
yield stream
case "tcp":
self.logger.debug("Connecting TCP host=%s port=%d", self.args["host"], self.args["port"])
async with await connect_tcp(remote_host=self.args["host"], remote_port=self.args["port"]) as stream:
yield stream
+ except OSError as e:
+ raise ConnectionError(f"Failed to connect to {self.kind} bus: {e}") from e📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class EchoNetwork(NetworkInterface, Driver): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ''' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EchoNetwork is a mock driver implementing the NetworkInterface | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.