weegee: reorganise and add configuration files
This commit is contained in:
parent
f3842bd4dc
commit
2a92a330c7
|
@ -1,588 +1,44 @@
|
|||
from __future__ import annotations
|
||||
from logging import getLogger
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional as O, Any
|
||||
from .dazy import Instance, Config, Meta, Item, Template
|
||||
from .wireguard import (
|
||||
IPAddress, IPNetwork, IPInterface,
|
||||
WireguardHostType, WireguardHost, WireguardPeer, WireguardConnection, which_connection,
|
||||
)
|
||||
from .desc import (
|
||||
WEEGEE_HOOK, WEEGEE_HOST, WEEGEE_INTERFACE, WEEGEE_PEER, WEEGEE_CONNECTION, WEEGEE_CONFIG,
|
||||
WEEGEE_INTERFACE_CONF_WG, WEEGEE_PEER_CONF_WG, WEEGEE_CONF_WG,
|
||||
|
||||
from .dazy import Meta, Template
|
||||
from .desc import WEEGEE_INTERFACE_CONF_WG, WEEGEE_PEER_CONF_WG, WEEGEE_CONF_WG
|
||||
|
||||
from .config import WeegeeConfig, WeegeeContext
|
||||
from .core import (
|
||||
WeegeeBase, WeegeeHook, WeegeeHost, WeegeeInterface, WeegeePeer, WeegeeConnection,
|
||||
do_config_interface, do_discover_interface,
|
||||
find_host_interfaces, find_interface_connections, find_interface_other_peers,
|
||||
do_sync_interface, sync_interface, sync_all_interfaces,
|
||||
)
|
||||
from .extra import WeegeeServer, WeegeeClient
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeBase:
|
||||
BASE = None
|
||||
context: 'WeegeeContext'
|
||||
item: Item
|
||||
meta: Meta = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if not self.meta:
|
||||
self.meta = Meta.load(self.context.instance, self.BASE.get_name())
|
||||
self.item = self.item.resolve(self.meta)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, name: str) -> str:
|
||||
return f'{cls.BASE.item_prefix}/{name}'
|
||||
|
||||
@classmethod
|
||||
def get_meta(cls, context: 'WeegeeContext') -> Meta:
|
||||
return Meta.load(context.instance, cls.BASE.get_name())
|
||||
|
||||
@classmethod
|
||||
def exists(cls, context: 'WeegeeContext', name: str) -> bool:
|
||||
return Item.exists(context.instance, cls.get_name(name))
|
||||
|
||||
@classmethod
|
||||
def create(cls, context: 'WeegeeContext', name: str, **kwargs) -> 'WeegeeBase':
|
||||
meta = cls.get_meta(context)
|
||||
item = Item.make(context.instance, cls.get_name(name), **kwargs)
|
||||
if not item.resolve(meta).is_value_complete(meta):
|
||||
raise TypeError('internal error')
|
||||
return cls(context, item, meta)
|
||||
|
||||
@classmethod
|
||||
def filter(cls, context: 'WeegeeContext', pattern: str) -> list['WeegeeBase']:
|
||||
return [cls(context, Item.load(context.instance, name)) for name in Item.filter(context.instance, cls.get_name(pattern))]
|
||||
|
||||
@classmethod
|
||||
def load(cls, context: 'WeegeeContext', name: str) -> 'WeegeeBase':
|
||||
return cls(context, Item.load(context.instance, cls.get_name(name)))
|
||||
|
||||
def save(self) -> None:
|
||||
self.item.unresolve(self.meta).save()
|
||||
|
||||
def delete(self) -> None:
|
||||
self.item.delete()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.item_name[len(self.get_name('')):]
|
||||
|
||||
@property
|
||||
def item_name(self) -> str:
|
||||
return self.item.name
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
if name not in self.item:
|
||||
raise AttributeError(name)
|
||||
return self.item[name]
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
if 'item' in self.__dict__ and self.item.is_type_resolved(name):
|
||||
self.item[name] = value
|
||||
else:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
return isinstance(other, self.__class__) and self.item.name == other.item.name
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.__class__.__name__, self.item_name))
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeHook(WeegeeBase):
|
||||
BASE = WEEGEE_HOOK
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: 'WeegeeContext', name: str, pre: list[str], post: list[str]) -> 'WeegeeHook':
|
||||
return super().create(ctx, name,
|
||||
pre=pre, post=post,
|
||||
)
|
||||
|
||||
@dataclass
|
||||
class WeegeeHookRunner:
|
||||
hooks: list[WeegeeHook]
|
||||
conn: WireguardConnection
|
||||
kwargs: dict[str, Any]
|
||||
|
||||
def _run(self, cmd: str) -> None:
|
||||
for k, v in self.kwargs.items():
|
||||
cmd = cmd.replace('%' + k, str(v))
|
||||
return self.conn._run(cmd, shell=True)
|
||||
|
||||
def __enter__(self) -> 'WeegeeHooks':
|
||||
for h in self.hooks:
|
||||
for cmd in h.pre:
|
||||
self._run(cmd)
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||
if not exc_type:
|
||||
for h in self.hooks:
|
||||
for cmd in h.post:
|
||||
self._run(cmd)
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeHost(WeegeeBase):
|
||||
BASE = WEEGEE_HOST
|
||||
conn: WireguardHost = None
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: 'WeegeeContext', name: str, type: WireguardHostType = WireguardHostType.Unsupported, host: O[str] = None, user: O[str] = None, elevate_user: O[str] = None, automanage: bool = False, autosync: bool = False) -> 'WeegeeHost':
|
||||
return super().create(ctx, name,
|
||||
automanage=automanage,
|
||||
autosync=autosync,
|
||||
type=type.value,
|
||||
host=host,
|
||||
user=user,
|
||||
elevate_user=elevate_user,
|
||||
)
|
||||
|
||||
@property
|
||||
def type(self) -> WireguardHostType:
|
||||
return WireguardHostType(self.item['type'])
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
super().__post_init__()
|
||||
self.conn = WireguardHost(WeegeeHookedWireguardConnection(self, which_connection(self.type)(self.host, self.user, self.elevate_user)))
|
||||
|
||||
@classmethod
|
||||
def list_hooks(cls) -> list[str]:
|
||||
return ['interface_add', 'interface_del', 'address_add', 'address_del', 'route_add', 'route_del', 'configure']
|
||||
|
||||
def get_hooks(self, name: str, **kwargs) -> WeegeeHookRunner:
|
||||
return WeegeeHookRunner([WeegeeHook(self.context, x) for x in getattr(self, f'{name}_hooks')], self.conn.conn, kwargs)
|
||||
|
||||
def get_peers(self, name: str) -> O[list[WireguardPeer]]:
|
||||
interface = self.conn.get_interface(name)
|
||||
if not interface:
|
||||
return None
|
||||
return interface.list_peers()
|
||||
|
||||
def sync_interface(self, name: str, addresses: list[IPInterface], routes: list[IPNetwork], config: str) -> None:
|
||||
interface = self.conn.get_interface(name)
|
||||
if not interface:
|
||||
if not self.automanage:
|
||||
return
|
||||
interface = self.conn.create_interface(name)
|
||||
if self.automanage:
|
||||
interface.sync(addresses, routes)
|
||||
interface.sync_config(config)
|
||||
|
||||
def delete_interface(self, name: str) -> None:
|
||||
interface = self.conn.get_interface(name)
|
||||
if interface:
|
||||
interface.clear_config()
|
||||
if self.automanage:
|
||||
interface.delete()
|
||||
|
||||
@dataclass
|
||||
class WeegeeHookedWireguardConnection(WireguardConnection):
|
||||
host: WireguardHost
|
||||
inner: WireguardConnection
|
||||
|
||||
def _run(self, *args, **kwargs) -> str:
|
||||
return self.inner._run(*args, **kwargs)
|
||||
|
||||
def has_interface(self, name: str) -> bool:
|
||||
return self.inner.has_interface(name)
|
||||
|
||||
def create_interface(self, name: str) -> None:
|
||||
with self.host.get_hooks('interface_add', i=name):
|
||||
return self.inner.create_interface(name)
|
||||
|
||||
def destroy_interface(self, name: str) -> None:
|
||||
with self.host.get_hooks('interface_del', i=name):
|
||||
return self.inner.destroy_interface(name)
|
||||
|
||||
def set_mtu(self, name: str, mtu: int) -> None:
|
||||
return self.inner.set_mtu(name, mtu)
|
||||
|
||||
def set_up(self, name: str) -> None:
|
||||
return self.inner.set_up(name)
|
||||
|
||||
def set_down(self, name: str) -> None:
|
||||
return self.inner.set_down(name)
|
||||
|
||||
def get_addresses(self, name: str) -> list[IPInterface]:
|
||||
return self.inner.get_addresses(name)
|
||||
|
||||
def add_address(self, name: str, address: IPInterface) -> None:
|
||||
with self.host.get_hooks('address_add', i=name, a=str(address)):
|
||||
return self.inner.add_address(name, address)
|
||||
|
||||
def delete_address(self, name: str, address: IPInterface) -> None:
|
||||
with self.host.get_hooks('address_del', i=name, a=str(address)):
|
||||
return self.inner.delete_address(name, address)
|
||||
|
||||
def delete_all_addresses(self, name: str) -> None:
|
||||
for address in self.get_addresses(name):
|
||||
self.delete_address(name, address)
|
||||
|
||||
def get_routes(self, name: str) -> list[IPNetwork]:
|
||||
return self.inner.get_routes(name)
|
||||
|
||||
def add_route(self, name: str, route: IPNetwork) -> None:
|
||||
with self.host.get_hooks('route_add', i=name, r=str(route)):
|
||||
return self.inner.add_route(name, route)
|
||||
|
||||
def delete_route(self, name: str, route: IPNetwork) -> None:
|
||||
with self.host.get_hooks('route_del', i=name, r=str(route)):
|
||||
return self.inner.delete_route(name, route)
|
||||
|
||||
def delete_all_routes(self, name: str) -> None:
|
||||
for route in self.get_routes(name):
|
||||
self.delete_route(name, route)
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeInterface(WeegeeBase):
|
||||
BASE = WEEGEE_INTERFACE
|
||||
|
||||
@classmethod
|
||||
def filter_for_hosts(cls, ctx: 'WeegeeContext', hosts: set[WeegeeHost]) -> set['WeegeeInterface']:
|
||||
return set(i for i in ctx.get_interfaces() if set(i.hosts) & hosts)
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: 'WeegeeContext', name: str, interface_name: str, private_key: O[str] = None, public_key: O[str] = None, port: O[int] = None, hosts: O[list[WeegeeHost]] = None, addresses: list[IPInterface] = []) -> 'WeegeeInterface':
|
||||
for host in [ctx.get_local_host()] + hosts:
|
||||
try:
|
||||
private_key = private_key or host.conn.gen_private_key()
|
||||
public_key = public_key or host.conn.get_public_key(private_key)
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warn(f'could not generate keypair on host {host.name!r}: {e!r}')
|
||||
else:
|
||||
logger.critical('could not generate public/private keypair automatically: please pass explicitly!')
|
||||
raise ValueError()
|
||||
|
||||
return super().create(ctx, name,
|
||||
hosts=[h.item for h in hosts],
|
||||
interface_name=interface_name,
|
||||
public_key=public_key, private_key=private_key,
|
||||
addresses=addresses, port=port,
|
||||
)
|
||||
|
||||
def save(self) -> None:
|
||||
for host in self.hosts:
|
||||
host.save()
|
||||
super().save()
|
||||
|
||||
def delete(self) -> None:
|
||||
peers = WeegeePeer.filter_for_interfaces(self.context, {self})
|
||||
for p in peers:
|
||||
p.delete()
|
||||
for h in self.hosts:
|
||||
if h.autosync:
|
||||
h.delete_interface(self.interface_name)
|
||||
super().delete()
|
||||
|
||||
@property
|
||||
def hosts(self) -> list[WeegeeHost]:
|
||||
return [WeegeeHost(self.context, x) for x in self.item['hosts']]
|
||||
|
||||
def gen_config(self) -> str:
|
||||
template = Template.load(self.context.instance, WEEGEE_INTERFACE_CONF_WG.get_name())
|
||||
config = WEEGEE_INTERFACE_CONF_WG.make_config(self.context.instance,
|
||||
interface=self.item,
|
||||
)
|
||||
return template.render(config)
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeePeer(WeegeeBase):
|
||||
BASE = WEEGEE_PEER
|
||||
|
||||
@classmethod
|
||||
def filter_for_interfaces(cls, ctx: 'WeegeeContext', interfaces: set[WeegeeInterface]) -> set['WeegeePeer']:
|
||||
return set(p for p in ctx.get_peers() if p.interface in interfaces)
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: 'WeegeeContext', name: str, interface: WeegeeInterface, routes: list[IPNetwork], host: O[str] = None, port: O[int] = None) -> 'WeegeePeer':
|
||||
return super().create(ctx, name,
|
||||
interface=interface.item,
|
||||
routes=routes,
|
||||
host=host, port=port,
|
||||
)
|
||||
|
||||
def save(self) -> None:
|
||||
self.interface.save()
|
||||
super().save()
|
||||
self.sync(auto=True)
|
||||
|
||||
def sync(self, auto=False, log=None) -> None:
|
||||
if not log:
|
||||
log = set()
|
||||
if self.item_name in log:
|
||||
return
|
||||
log.add(self.item_name)
|
||||
logger.info(f'syncing peer: {self.name}')
|
||||
sync_interface(self.interface, auto=auto, log=log)
|
||||
|
||||
def delete(self) -> None:
|
||||
for c in WeegeeConnection.filter_for_peers(self.context, {self}):
|
||||
c.peers.remove(self)
|
||||
if len(c.peers) <= 1:
|
||||
c.delete()
|
||||
else:
|
||||
c.save()
|
||||
super().delete()
|
||||
|
||||
@property
|
||||
def interface(self) -> WeegeeInterface:
|
||||
return WeegeeInterface(self.context, self.item['interface'])
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeConnection(WeegeeBase):
|
||||
BASE = WEEGEE_CONNECTION
|
||||
|
||||
@classmethod
|
||||
def filter_for_peers(cls, ctx: 'WeegeeContext', peers: set[WeegeePeer]) -> set['WeegeeConnection']:
|
||||
return set(c for c in ctx.get_connections() if set(c.peers) & peers)
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: 'WeegeeContext', name: str, peers: list[WeegeePeer], preshared_key: O[str] = None) -> 'WeegeeConnecton':
|
||||
if not preshared_key:
|
||||
hosts = {ctx.get_local_host()}
|
||||
for p in peers:
|
||||
hosts.update(p.interface.hosts)
|
||||
for host in hosts:
|
||||
try:
|
||||
preshared_key = host.conn.gen_preshared_key()
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warn(f'could not generate preshared key on host {host.name!r}: {e!r}')
|
||||
else:
|
||||
logger.critical('could not generate preshared key automatically: please pass explicitly!')
|
||||
raise ValueError()
|
||||
return super().create(ctx, name,
|
||||
peers=[p.item for p in peers],
|
||||
preshared_key=preshared_key,
|
||||
)
|
||||
|
||||
def save(self) -> None:
|
||||
for peer in self.peers:
|
||||
peer.save()
|
||||
super().save()
|
||||
|
||||
@property
|
||||
def peers(self) -> list[WeegeePeer]:
|
||||
return [WeegeePeer(self.context, x) for x in self.item['peers']]
|
||||
|
||||
def gen_config(self, target: WeegeePeer) -> str:
|
||||
peers = [p for p in self.peers if p != target]
|
||||
template = Template.load(self.context.instance, WEEGEE_PEER_CONF_WG.get_name())
|
||||
config = WEEGEE_PEER_CONF_WG.make_config(self.context.instance,
|
||||
connection=self.item,
|
||||
peers={p.name: p.item for p in peers},
|
||||
)
|
||||
return template.render(config)
|
||||
|
||||
|
||||
def do_config_interface(interface: WeegeeInterface, peers: set[WeegeePeer], connections: set[WeegeeConnection]) -> str:
|
||||
peer_configs = []
|
||||
for c in connections:
|
||||
for p in set(c.peers) & peers:
|
||||
peer_configs.append(c.gen_config(p))
|
||||
interface_config = interface.gen_config()
|
||||
|
||||
template = Template.load(interface.context.instance, WEEGEE_CONF_WG.get_name())
|
||||
config = WEEGEE_CONF_WG.make_config(interface.context.instance,
|
||||
interface_config=interface_config,
|
||||
peer_configs=peer_configs,
|
||||
)
|
||||
return template.render(config)
|
||||
|
||||
def do_discover_interface(interface: WeegeeInterface, peers: set[WeegeePeer], connections: set[WeegeeConnection]) -> tuple[list[IPNetwork], set[WeegeePeer]]:
|
||||
routes = []
|
||||
other_peers = set()
|
||||
for c in connections:
|
||||
for p in set(c.peers) - peers:
|
||||
routes.extend(p.routes)
|
||||
routes.extend(a.network for a in p.interface.addresses)
|
||||
other_peers.add(p)
|
||||
return routes, other_peers
|
||||
|
||||
def find_host_interfaces(host: WeegeeHost) -> set[WeegeeInterface]:
|
||||
return WeegeeInterface.filter_for_hosts(host.context, {host})
|
||||
|
||||
def find_interface_connections(interface: WeegeeInterface) -> tuple[set[WeegeePeer], set[WeegeeConnection]]:
|
||||
peers = WeegeePeer.filter_for_interfaces(interface.context, {interface})
|
||||
connections = WeegeeConnection.filter_for_peers(interface.context, peers)
|
||||
return peers, connections
|
||||
|
||||
def find_interface_other_peers(interface: WeegeeInterface) -> set[WeegeePeer]:
|
||||
own_peers, connections = find_interface_connections(interface)
|
||||
_, other_peers = do_discover_interface(interface, own_peers, connections)
|
||||
return other_peers
|
||||
|
||||
def do_sync_interface(interface: WeegeeInterface, peers: set[WeegeePeer], connections: set[WeegeeConnection], auto: bool = False) -> set[WeegeePeer]:
|
||||
config = do_config_interface(interface, peers, connections)
|
||||
routes, other_peers = do_discover_interface(interface, peers, connections)
|
||||
for host in interface.hosts:
|
||||
if auto and not host.autosync:
|
||||
continue
|
||||
host.sync_interface(interface.interface_name, interface.addresses, routes, config)
|
||||
return other_peers
|
||||
|
||||
def sync_interface(interface: WeegeeInterface, auto=False, log=None) -> None:
|
||||
if log is None:
|
||||
log = set()
|
||||
if interface.item_name in log:
|
||||
return
|
||||
log.add(interface.item_name)
|
||||
logger.info(f'syncing interface: {interface.name}')
|
||||
|
||||
all_peers, all_connections = find_interface_connections(interface)
|
||||
other_peers = do_sync_interface(interface, all_peers, all_connections, auto=auto)
|
||||
|
||||
for p in other_peers:
|
||||
sync_interface(p.interface, auto=auto, log=log)
|
||||
|
||||
def sync_all_interfaces(context: 'WeegeeContext', auto=False, log=None) -> None:
|
||||
if log is None:
|
||||
log = set()
|
||||
for interface in context.get_interfaces():
|
||||
sync_interface(interface, auto=auto, log=log)
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeServer(WeegeePeer):
|
||||
def get_client_name(self, name: str) -> str:
|
||||
return f'{self.name}/{name}'
|
||||
|
||||
def get_clients(self) -> list[WeegeePeer]:
|
||||
return WeegeeClient.filter(self.context, self.get_client_name('*'))
|
||||
|
||||
def get_client(self, name: str) -> O[WeegeePeer]:
|
||||
return WeegeeClient.load(self.context, self.get_client_name(name))
|
||||
|
||||
def delete(self) -> None:
|
||||
super().delete()
|
||||
sync_all_interfaces(self.context, auto=True)
|
||||
|
||||
def gen_config(self) -> str:
|
||||
peers, connections = find_interface_connections(self.interface)
|
||||
return do_config_interface(self.interface, peers, connections)
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeClient(WeegeePeer):
|
||||
@classmethod
|
||||
def create(cls, ctx: 'WeegeeContext', name: str, server: WeegeeServer, preshared_key: O[str] = None, **kwargs) -> 'WeegeeClient':
|
||||
name = server.get_client_name(name)
|
||||
client = super().create(ctx, name, **kwargs)
|
||||
connection = WeegeeConnection.create(ctx, name, peers=[server, client], preshared_key=preshared_key)
|
||||
connection.save()
|
||||
return client
|
||||
|
||||
@property
|
||||
def connection(self) -> WeegeeConnection:
|
||||
return WeegeeConnection.load(self.context, self.name)
|
||||
|
||||
@property
|
||||
def server(self) -> WeegeeServer:
|
||||
return next(p for p in self.connection.peers if p != self.peer)
|
||||
|
||||
def delete(self) -> None:
|
||||
self.connection.delete()
|
||||
super().delete()
|
||||
sync_all_interfaces(self.context, auto=True)
|
||||
|
||||
def gen_config(self) -> str:
|
||||
peers, connections = find_interface_connections(self.interface)
|
||||
return do_config_interface(self.interface, peers, connections)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WeegeeConfig:
|
||||
context: 'WeegeeContext'
|
||||
item: Item
|
||||
|
||||
@classmethod
|
||||
def get_name(cls) -> str:
|
||||
return WEEGEE_CONFIG.get_name()
|
||||
|
||||
@classmethod
|
||||
def load(cls, context: 'WeegeeContext', name: str) -> 'WeegeeConfig':
|
||||
return cls(context, Meta.load(context.instance, name))
|
||||
|
||||
def save(self) -> None:
|
||||
self.item.save()
|
||||
|
||||
@property
|
||||
def default_server_hosts(self) -> list[WeegeeHost]:
|
||||
return [WeegeeHost(self.context, item) for item in self.item['default_server_hosts']]
|
||||
|
||||
@default_server_hosts.setter
|
||||
def default_server_hosts(self, value: list[WeegeeHost]) -> None:
|
||||
self.item['default_server_hosts'] = [x.item for x in value]
|
||||
|
||||
@property
|
||||
def default_client_hosts(self) -> list[WeegeeHost]:
|
||||
return [WeegeeHost(self.context, item) for item in self.item['default_client_hosts']]
|
||||
|
||||
@default_client_hosts.setter
|
||||
def default_client_hosts(self, value: list[WeegeeHost]) -> None:
|
||||
self.item['default_client_hosts'] = [x.item for x in value]
|
||||
|
||||
|
||||
@dataclass
|
||||
class WeegeeContext:
|
||||
LOCAL_HOST_NAME = 'local'
|
||||
instance: Instance
|
||||
|
||||
def setup(self) -> None:
|
||||
logger.info('setup: metas')
|
||||
for wmeta in (WEEGEE_HOOK, WEEGEE_HOST, WEEGEE_INTERFACE, WEEGEE_PEER, WEEGEE_CONNECTION, WEEGEE_CONFIG):
|
||||
logger.debug(' ' + wmeta.name)
|
||||
Meta.parse(self.instance, wmeta.get_name(), wmeta.spec).save()
|
||||
logger.info('setup: templates')
|
||||
for wtemp in (WEEGEE_INTERFACE_CONF_WG, WEEGEE_PEER_CONF_WG, WEEGEE_CONF_WG):
|
||||
logger.debug(' ' + wtemp.name)
|
||||
Template(wtemp.get_name(), wtemp.template, self.instance).save()
|
||||
|
||||
logger.info('setup: items')
|
||||
if not WeegeeHost.exists(self, self.LOCAL_HOST_NAME):
|
||||
logger.debug(f' {WeegeeHost.get_name(self.LOCAL_HOST_NAME)}')
|
||||
localhost = WeegeeHost.create(self, self.LOCAL_HOST_NAME)
|
||||
localhost.save()
|
||||
else:
|
||||
localhost = self.get_local_host()
|
||||
|
||||
logger.info('setup: config')
|
||||
config = self.get_config()
|
||||
config.default_server_hosts = [localhost]
|
||||
config.save()
|
||||
|
||||
def get_local_host(self) -> WeegeeHost:
|
||||
return WeegeeHost.load(self, self.LOCAL_HOST_NAME)
|
||||
|
||||
|
||||
def get_config(self) -> WeegeeConfig:
|
||||
return WeegeeConfig.load(self, WeegeeConfig.get_name())
|
||||
|
||||
def get_servers(self) -> list[WeegeeServer]:
|
||||
return WeegeeServer.filter(self, '*')
|
||||
|
||||
def get_server(self, name: str) -> O[WeegeeServer]:
|
||||
return WeegeeServer.load(self, name)
|
||||
|
||||
def get_connections(self) -> list[WeegeeConnection]:
|
||||
return WeegeeConnection.filter(self, '*')
|
||||
|
||||
def get_connection(self, name: str) -> O[WeegeeConnection]:
|
||||
return WeegeeConnection.load(self, name)
|
||||
|
||||
def get_peers(self) -> list[WeegeePeer]:
|
||||
return WeegeePeer.filter(self, '*')
|
||||
|
||||
def get_peer(self, name: str) -> O[WeegeePeer]:
|
||||
return WeegeePeer.load(self, name)
|
||||
|
||||
def get_interfaces(self) -> list[WeegeeInterface]:
|
||||
return WeegeeInterface.filter(self, '*')
|
||||
|
||||
def get_interface(self, name: str) -> O[WeegeeInterface]:
|
||||
return WeegeeInterface.load(self, name)
|
||||
|
||||
def get_hosts(self) -> list[WeegeeHost]:
|
||||
return WeegeeHost.filter(self, '*')
|
||||
|
||||
def get_host(self, name: str) -> O[WeegeeHost]:
|
||||
return WeegeeHost.load(self, name)
|
||||
def setup(context: WeegeeContext) -> None:
|
||||
logger.info('setup: metas')
|
||||
for meta in (WeegeeHook, WeegeeHost, WeegeeInterface, WeegeePeer, WeegeeConnection):
|
||||
logger.debug(' ' + meta.BASE.name)
|
||||
Meta.parse(context.instance, meta.BASE.get_name(), meta.BASE.spec).save()
|
||||
|
||||
logger.info('setup: templates')
|
||||
for temp in (WEEGEE_INTERFACE_CONF_WG, WEEGEE_PEER_CONF_WG, WEEGEE_CONF_WG):
|
||||
logger.debug(' ' + temp.name)
|
||||
Template(temp.get_name(), temp.template, context.instance).save()
|
||||
|
||||
logger.info('setup: items')
|
||||
if not WeegeeHost.exists(context, WeegeeHost.LOCAL_HOST_NAME):
|
||||
logger.debug(f' {WeegeeHost.get_name(WeegeeHost.LOCAL_HOST_NAME)}')
|
||||
WeegeeHost.create(context, WeegeeHost.LOCAL_HOST_NAME).save()
|
||||
|
||||
|
||||
__all__ = [x.__name__ for x in (
|
||||
WeegeeConfig, WeegeeContext,
|
||||
WeegeeHook, WeegeeHook, WeegeeInterface, WeegeePeer, WeegeeConnection,
|
||||
WeegeeServer, WeegeeClient,
|
||||
setup,
|
||||
do_config_interface, do_discover_interface,
|
||||
find_host_interfaces, find_interface_connections, find_interface_other_peers,
|
||||
do_sync_interface, sync_interface, sync_all_interfaces,
|
||||
)]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
import sys
|
||||
import os.path
|
||||
import argparse
|
||||
import logging
|
||||
import ipaddress
|
||||
|
@ -8,14 +9,14 @@ from typing import Optional as O
|
|||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
from .dazy import Instance
|
||||
from .wireguard import WireguardHostType
|
||||
from . import (
|
||||
WeegeeContext,
|
||||
WeegeeContext, WeegeeConfig,
|
||||
WeegeeHook, WeegeeHost, WeegeeInterface, WeegeePeer, WeegeeConnection,
|
||||
WeegeeServer, WeegeeClient,
|
||||
find_interface_other_peers,
|
||||
sync_all_interfaces,
|
||||
setup,
|
||||
)
|
||||
|
||||
|
||||
|
@ -49,14 +50,14 @@ def timestampify(dt: datetime) -> str:
|
|||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.set_defaults(func=None)
|
||||
parser.add_argument('-d', '--base-dir', default='.')
|
||||
parser.add_argument('-c', '--config', help='path to configuration file')
|
||||
parser.add_argument('--yes-i-want-to-destroy-this', action='store_true', default=False)
|
||||
|
||||
commands = parser.add_subparsers(title='commands')
|
||||
|
||||
def do_status(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
success = True
|
||||
for interface in ctx.get_interfaces():
|
||||
for interface in WeegeeInterface.find_all(ctx):
|
||||
if interface.hosts:
|
||||
print(f'{interface.name}:')
|
||||
for host in interface.hosts:
|
||||
|
@ -97,42 +98,85 @@ def main():
|
|||
system_commands = system.add_subparsers(title='system commands')
|
||||
|
||||
def do_setup(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
ctx.setup()
|
||||
setup(ctx)
|
||||
|
||||
setup = system_commands.add_parser('setup')
|
||||
setup.set_defaults(func=do_setup)
|
||||
sys_setup = system_commands.add_parser('setup')
|
||||
sys_setup.set_defaults(func=do_setup)
|
||||
|
||||
def do_migrate(arser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
def do_configure(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
config = ctx.get_config()
|
||||
if args.config_dir:
|
||||
config.meta_dir = args.config_dir
|
||||
config.data_dir = args.data_dir or args.config_dir
|
||||
if args.log_level:
|
||||
config.log_level = args.log_level
|
||||
if args.reset_default_client_hosts:
|
||||
config.default_client_hosts = []
|
||||
for host in args.del_default_client_host:
|
||||
host_item = WeegeeHost.load(context, host).name
|
||||
if host_item in config.default_client_hosts:
|
||||
config.default_client_hosts.remove(host_item)
|
||||
for host in args.add_default_client_host:
|
||||
host_item = WeegeeHost.load(context, host).name
|
||||
if host_item not in config.default_client_hosts:
|
||||
config.default_client_hosts.append(host_item)
|
||||
if args.reset_default_server_hosts:
|
||||
config.default_server_hosts = []
|
||||
for host in args.del_default_server_host:
|
||||
host_item = WeegeeHost.load(context, host).name
|
||||
if host_item in config.default_server_hosts:
|
||||
config.default_server_hosts.remove(host_item)
|
||||
for host in args.add_default_server_host:
|
||||
host_item = WeegeeHost.load(context, host).name
|
||||
if host_item not in config.default_server_hosts:
|
||||
config.default_server_hosts.append(host_item)
|
||||
config.save(args.file)
|
||||
|
||||
sys_configure = system_commands.add_parser('configure')
|
||||
sys_configure.add_argument('-s', '--system-file', dest='file', action='store_const', const=WeegeeConfig.GLOBAL_PATH, help=f'store to system configuration file ({WeegeeConfig.GLOBAL_PATH})')
|
||||
sys_configure.add_argument('-u', '--user-file', dest='file', action='store_const', const=WeegeeConfig.USER_PATH, help=f'store to user configuration file ({WeegeeConfig.USER_PATH})')
|
||||
sys_configure.add_argument('-l', '--local-file', dest='file', action='store_const', const=WeegeeConfig.LOCAL_PATH, help=f'store to local configuration file ({WeegeeConfig.LOCAL_PATH})')
|
||||
sys_configure.add_argument('-f', '--file', help='store to given configuration file')
|
||||
sys_configure.add_argument('-d', '--config-dir', metavar='PATH', help='config base directory (optional)')
|
||||
sys_configure.add_argument('-D', '--data-dir', metavar='PATH', help='data base directory (optional, defaults to config base directory)')
|
||||
sys_configure.add_argument('-L', '--log-level', metavar='LEVEL', help='log level', choices=('debug', 'info', 'warning', 'error', 'critical'))
|
||||
for x in ('client', 'server'):
|
||||
sys_configure.add_argument(f'--reset-default-{x}-hosts', action='store_true')
|
||||
sys_configure.add_argument(f'--add-default-{x}-host', metavar='HOST', action='append', default=[], help=f'add default host for new {x}s')
|
||||
sys_configure.add_argument(f'--del-default-{x}-host', metavar='HOST', action='append', default=[], help=f'remove default host for new {x}s')
|
||||
sys_configure.set_defaults(func=do_configure)
|
||||
|
||||
def do_migrate(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
pass
|
||||
|
||||
migrate = system_commands.add_parser('migrate')
|
||||
migrate.set_defaults(func=do_migrate)
|
||||
sys_migrate = system_commands.add_parser('migrate')
|
||||
sys_migrate.set_defaults(func=do_migrate)
|
||||
|
||||
def do_clean(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
success = True
|
||||
while success:
|
||||
success = False
|
||||
|
||||
for peer in ctx.get_peers():
|
||||
if not WeegeeConnection.filter_for_peers(ctx, {peer}):
|
||||
for peer in WeegeePeer.find_all(ctx):
|
||||
if not WeegeeConnection.find_for_peers(ctx, {peer}):
|
||||
print(f'destroying peer: {peer.name}')
|
||||
peer.delete()
|
||||
success = True
|
||||
|
||||
for connection in ctx.get_connections():
|
||||
for connection in WeegeeConnection.find_all(ctx):
|
||||
if len(connection.peers) <= 1:
|
||||
print(f'destroying connection: {connection.name}')
|
||||
connection.delete()
|
||||
success = True
|
||||
|
||||
for interface in ctx.get_interfaces():
|
||||
if not WeegeePeer.filter_for_interfaces(ctx, {interface}):
|
||||
for interface in WeegeeInterface.find_all(ctx):
|
||||
if not WeegeePeer.find_for_interfaces(ctx, {interface}):
|
||||
print(f'destroying interface: {interface.name}')
|
||||
interface.delete()
|
||||
success = True
|
||||
|
||||
clean = system_commands.add_parser('clean')
|
||||
clean.set_defaults(func=do_clean)
|
||||
sys_clean = system_commands.add_parser('clean')
|
||||
sys_clean.set_defaults(func=do_clean)
|
||||
|
||||
|
||||
# Host commands
|
||||
|
@ -148,17 +192,6 @@ def main():
|
|||
autosync=args.auto_sync, automanage=args.auto_manage,
|
||||
)
|
||||
host.save()
|
||||
if any([args.set_default_server, args.set_default_client, args.add_default_server, args.add_default_client]):
|
||||
config = ctx.get_config()
|
||||
if args.set_default_server:
|
||||
config.default_server_hosts = []
|
||||
if args.set_default_client:
|
||||
config.default_client_hosts = []
|
||||
if args.set_default_server or args.add_default_server:
|
||||
config.default_server_hosts = config.default_server_hosts + [host]
|
||||
if args.set_default_client or args.add_default_client:
|
||||
config.default_client_hosts = config.default_client_hosts + [host]
|
||||
config.save()
|
||||
|
||||
add_host = host_commands.add_parser('create')
|
||||
add_host.add_argument('-t', '--type', type=WireguardHostType, help='host type for auto-manage operations')
|
||||
|
@ -167,15 +200,11 @@ def main():
|
|||
add_host.add_argument('-U', '--elevate_user', metavar='USER', help='username to elevate privileges to')
|
||||
add_host.add_argument('-a', '--auto-sync', action='store_true', default=False, help='whether to auto-synchronize config')
|
||||
add_host.add_argument('-A', '--auto-manage', action='store_true', default=False, help='whether to auto-synchronize interfaces')
|
||||
add_host.add_argument('--set-default-server', action='store_true', default=False, help='set as default server host')
|
||||
add_host.add_argument('--set-default-client', action='store_true', default=False, help='set as default client host')
|
||||
add_host.add_argument('--add-default-server', action='store_true', default=False, help='add to default server hosts')
|
||||
add_host.add_argument('--add-default-client', action='store_true', default=False, help='add to default client hosts')
|
||||
add_host.add_argument('name', help='host name')
|
||||
add_host.set_defaults(func=do_add_host)
|
||||
|
||||
def do_set_host(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
host = ctx.get_host(args.name)
|
||||
host = WeegeeHost.load(ctx, args.name)
|
||||
if args.type is not None:
|
||||
host.type = args.type
|
||||
if args.host is not None:
|
||||
|
@ -185,9 +214,9 @@ def main():
|
|||
if args.elevate_user is not None:
|
||||
host.elevate_user = args.elevate_user
|
||||
if args.auto_sync is not None:
|
||||
host.autosync = int(args.auto_sync)
|
||||
host.autosync = args.auto_sync
|
||||
if args.auto_manage is not None:
|
||||
host.automanage = int(args.auto_manage)
|
||||
host.automanage = args.auto_manage
|
||||
|
||||
for hook_name in host.list_hooks():
|
||||
got_pre = getattr(args, f'hook_pre_{hook_name}')
|
||||
|
@ -214,18 +243,6 @@ def main():
|
|||
|
||||
host.save()
|
||||
|
||||
if any([args.add_default_server, args.add_default_client, args.del_default_server, args.del_default_client]):
|
||||
config = ctx.get_config()
|
||||
if args.set_default_server or args.add_default_server:
|
||||
config.default_server_hosts.append(host)
|
||||
if args.set_default_client or args.add_default_client:
|
||||
config.default_client_hosts.append(host)
|
||||
if args.del_default_server:
|
||||
config.default_server_hosts.remove(host)
|
||||
if args.del_default_client:
|
||||
config.default_client_hosts.remove(host)
|
||||
config.save()
|
||||
|
||||
set_host = host_commands.add_parser('set')
|
||||
set_host.add_argument('-t', '--type', type=WireguardHostType, help='host type for auto-manage operations')
|
||||
set_host.add_argument('-H', '--host', help='remote host')
|
||||
|
@ -235,10 +252,6 @@ def main():
|
|||
set_host.add_argument('--no-auto-sync', dest='auto_sync', action='store_false', help='do not auto-synchronize config')
|
||||
set_host.add_argument('-A', '--auto-manage', action='store_true', default=None, help='auto-synchronize interfaces')
|
||||
set_host.add_argument('--no-auto-manage', dest='auto_manage', action='store_false', help='do not auto-synchronize interface')
|
||||
set_host.add_argument('--add-default-server', action='store_true', default=False, help='add to default server hosts')
|
||||
set_host.add_argument('--add-default-client', action='store_true', default=False, help='add to default client hosts')
|
||||
set_host.add_argument('--del-default-server', action='store_true', default=False, help='remove from default server hosts')
|
||||
set_host.add_argument('--del-default-client', action='store_true', default=False, help='remove from default client hosts')
|
||||
for hook in WeegeeHost.list_hooks():
|
||||
hook_arg = hook.replace('_', '-')
|
||||
hook_desc = hook.replace('_', ' ')
|
||||
|
@ -250,7 +263,7 @@ def main():
|
|||
def do_del_host(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
if not args.yes_i_want_to_destroy_this:
|
||||
parser.error('please pass --yes-i-want-to-destroy-this if you really want to destroy this host')
|
||||
host = ctx.get_host(args.name)
|
||||
host = WeegeeHost.load(ctx, args.name)
|
||||
host.delete()
|
||||
|
||||
del_host = host_commands.add_parser('destroy')
|
||||
|
@ -284,7 +297,7 @@ def main():
|
|||
def do_del_interface(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
if not args.yes_i_want_to_destroy_this:
|
||||
parser.error('please pass --yes-i-want-to-destroy-this if you really want to destroy this interface')
|
||||
interface = ctx.get_interface(args.name)
|
||||
interface = WeegeeInterface.load(ctx, args.name)
|
||||
interface.delete()
|
||||
|
||||
del_interface = interface_commands.add_parser('destroy')
|
||||
|
@ -292,12 +305,12 @@ def main():
|
|||
del_interface.set_defaults(func=do_del_interface)
|
||||
|
||||
def do_connect_interface(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
left_interface = ctx.get_interface(args.left)
|
||||
left_interface = WeegeeInterface.load(ctx, args.left)
|
||||
left_peer = WeegeePeer.create(ctx, f'{args.left}-{args.right}', left_interface,
|
||||
routes=args.left_route, host=args.left_host, port=args.left_port,
|
||||
)
|
||||
left_peer.save()
|
||||
right_interface = ctx.get_interface(args.right)
|
||||
right_interface = WeegeeInterface.load(ctx, args.right)
|
||||
right_peer = WeegeePeer.create(ctx, f'{args.right}-{args.left}', right_interface,
|
||||
routes=args.right_route, host=args.right_host, port=args.right_port,
|
||||
)
|
||||
|
@ -318,7 +331,7 @@ def main():
|
|||
connect_interface.set_defaults(func=do_connect_interface)
|
||||
|
||||
def do_disconnect_interface(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
connection = ctx.get_connection(f'{args.left}-{args.right}')
|
||||
connection = WeegeeConnection.load(ctx, f'{args.left}-{args.right}')
|
||||
connection.delete()
|
||||
|
||||
disconnect_interface = interface_commands.add_parser('disconnect')
|
||||
|
@ -336,7 +349,7 @@ def main():
|
|||
interface_name = args.interface or f'wg-{args.name}'
|
||||
interface = WeegeeInterface.create(ctx, args.name, interface_name,
|
||||
private_key=args.private_key, public_key=args.public_key,
|
||||
addresses=args.address, port=args.port, hosts=ctx.get_config().default_server_hosts,
|
||||
addresses=args.address, port=args.port, hosts=[WeegeeHost.load(ctx, name) for name in ctx.get_config().default_server_hosts],
|
||||
)
|
||||
interface.save()
|
||||
server = WeegeeServer.create(ctx, args.name, interface, routes=args.routes, host=args.host)
|
||||
|
@ -356,7 +369,7 @@ def main():
|
|||
def do_del_server(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
if not args.yes_i_want_to_destroy_this:
|
||||
parser.error('please pass --yes-i-want-to-destroy-this if you really want to destroy this server')
|
||||
server = ctx.get_server(args.name)
|
||||
server = WeegeeServer.load(ctx, args.name)
|
||||
server.delete()
|
||||
|
||||
del_server = server_commands.add_parser('destroy')
|
||||
|
@ -364,7 +377,7 @@ def main():
|
|||
del_server.set_defaults(func=do_del_server)
|
||||
|
||||
def do_conf_server(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
server = ctx.get_server(args.name)
|
||||
server = WeegeeServer.load(ctx, args.name)
|
||||
print(server.gen_config())
|
||||
|
||||
conf_server = server_commands.add_parser('config')
|
||||
|
@ -378,12 +391,12 @@ def main():
|
|||
client_commands = client.add_subparsers(title='client commands')
|
||||
|
||||
def do_add_client(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
server = ctx.get_server(args.server)
|
||||
server = WeegeeServer.load(ctx, args.server)
|
||||
full_name = server.get_client_name(args.name)
|
||||
interface_name = args.interface or f'wg0'
|
||||
interface = WeegeeInterface.create(ctx, full_name, interface_name,
|
||||
private_key=args.private_key, public_key=args.public_key,
|
||||
addresses=args.address, hosts=ctx.get_config().default_client_hosts,
|
||||
addresses=args.address, hosts=[WeegeeHost.load(ctx, name) for name in ctx.get_config().default_client_hosts],
|
||||
)
|
||||
interface.save()
|
||||
client = WeegeeClient.create(ctx, args.name, server, preshared_key=args.preshared_key, interface=interface, routes=[])
|
||||
|
@ -402,7 +415,7 @@ def main():
|
|||
def do_del_client(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
if not args.yes_i_want_to_destroy_this:
|
||||
parser.error('please pass --yes-i-want-to-destroy-this if you really want to destroy this client')
|
||||
server = ctx.get_server(args.server)
|
||||
server = WeegeeServer.load(ctx, args.server)
|
||||
client = server.get_client(args.name)
|
||||
client.delete()
|
||||
|
||||
|
@ -412,7 +425,7 @@ def main():
|
|||
del_client.set_defaults(func=do_del_client)
|
||||
|
||||
def do_conf_client(parser: argparse.ArgumentParser, args: argparse.Namespace, ctx: WeegeeContext) -> O[int]:
|
||||
server = ctx.get_server(args.server)
|
||||
server = WeegeeServer.load(ctx, args.server)
|
||||
client = server.get_client(args.name)
|
||||
print(client.gen_config())
|
||||
|
||||
|
@ -428,8 +441,11 @@ def main():
|
|||
if not args.func:
|
||||
parser.error('a subcommand must be provided')
|
||||
|
||||
instance = Instance(args.base_dir)
|
||||
sys.exit(args.func(parser, args, WeegeeContext(instance)))
|
||||
config = WeegeeConfig.find()
|
||||
if args.config:
|
||||
config.load(args.config)
|
||||
context = WeegeeContext(config)
|
||||
sys.exit(args.func(parser, args, context))
|
||||
|
||||
|
||||
main()
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
from __future__ import annotations
|
||||
from logging import getLogger
|
||||
|
||||
import os.path
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional as O
|
||||
|
||||
from .dazy import Instance, Config, Item, Meta
|
||||
from .desc import (
|
||||
WEEGEE_CONFIG,
|
||||
)
|
||||
|
||||
logger = getLogger()
|
||||
|
||||
|
||||
@dataclass
|
||||
class WeegeeConfig:
|
||||
BASE = WEEGEE_CONFIG
|
||||
item: Item
|
||||
merges: list[tuple[str, Config]]
|
||||
context: O['WeegeeContext'] = None
|
||||
|
||||
GLOBAL_PATH = '/etc/weegee.conf'
|
||||
USER_PATH = os.path.expanduser('~/.weegee.conf')
|
||||
LOCAL_PATH = os.path.abspath('./weegee.conf')
|
||||
|
||||
@classmethod
|
||||
def get_meta(cls) -> Meta:
|
||||
return Meta.parse(None, cls.BASE.get_name(), cls.BASE.spec)
|
||||
|
||||
@classmethod
|
||||
def create(cls) -> 'WeegeeConfig':
|
||||
meta = cls.get_meta()
|
||||
item = Item.make(None, '').resolve(meta)
|
||||
return cls(item, [(None, item, meta)])
|
||||
|
||||
@classmethod
|
||||
def find(cls) -> 'WeegeeConfig':
|
||||
self = cls.create()
|
||||
for path in (cls.GLOBAL_PATH, cls.USER_PATH, cls.LOCAL_PATH):
|
||||
if os.path.isfile(path):
|
||||
self.load(path)
|
||||
else:
|
||||
self.merges.append((path, self.item, None))
|
||||
return self
|
||||
|
||||
def load(self, path: str) -> None:
|
||||
item = Item.loadfile(path, path)
|
||||
self.item = item.resolve(self.item)
|
||||
self.merges.append((path, self.item, item))
|
||||
|
||||
def save(self, path: O[str] = None) -> None:
|
||||
if not path:
|
||||
if len(self.merges) <= 1:
|
||||
raise ValueError('must supply filename to save to')
|
||||
path = self.merges[-1][0]
|
||||
unmerges = [self.merges[-2][1]]
|
||||
else:
|
||||
for i, (mpath, _, _) in enumerate(self.merges):
|
||||
if mpath == path:
|
||||
unmerges = [self.merges[i-1][1]] + [partial for _, _, partial in self.merges[i + 1:]]
|
||||
break
|
||||
else:
|
||||
unmerges = self.merges[-1][1]
|
||||
|
||||
item = self.item
|
||||
for config in reversed(unmerges):
|
||||
if config:
|
||||
item = item.unresolve(config)
|
||||
item.savefile(path)
|
||||
|
||||
|
||||
def __dir__(self) -> list[str]:
|
||||
return super().__dir__() + list(self.item.values)
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return self.item[name]
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
if 'item' in self.__dict__ and name in self.item:
|
||||
self.item[name] = value
|
||||
else:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
|
||||
class WeegeeContext:
|
||||
config: WeegeeConfig
|
||||
instance: Instance
|
||||
|
||||
def __init__(self, config: O[WeegeeConfig]) -> None:
|
||||
self.config = config or WeegeeConfig.create()
|
||||
self.config.context = self
|
||||
self.instance = Instance(self.config.meta_path, self.config.data_path or self.config.meta_path)
|
||||
logger.setLevel(self.config.log_level.upper())
|
||||
|
||||
def get_config(self) -> WeegeeConfig:
|
||||
return self.config
|
|
@ -0,0 +1,462 @@
|
|||
from __future__ import annotations
|
||||
from logging import getLogger
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional as O, Any
|
||||
|
||||
from .dazy import Meta, Item, Template
|
||||
from .wireguard import (
|
||||
IPAddress, IPNetwork, IPInterface,
|
||||
WireguardHostType, WireguardHost, WireguardPeer, WireguardConnection, which_connection,
|
||||
)
|
||||
from .config import WeegeeContext
|
||||
from .desc import (
|
||||
WEEGEE_HOOK, WEEGEE_HOST, WEEGEE_INTERFACE, WEEGEE_PEER, WEEGEE_CONNECTION,
|
||||
WEEGEE_INTERFACE_CONF_WG, WEEGEE_PEER_CONF_WG, WEEGEE_CONF_WG,
|
||||
)
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeBase:
|
||||
BASE = None
|
||||
context: WeegeeContext
|
||||
item: Item
|
||||
meta: Meta = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if not self.meta:
|
||||
self.meta = self.get_meta(self.context)
|
||||
self.item = self.item.resolve(self.meta)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, name: str) -> str:
|
||||
return f'{cls.BASE.item_prefix}/{name}'
|
||||
|
||||
@classmethod
|
||||
def get_meta(cls, context: WeegeeContext) -> Meta:
|
||||
return Meta.load(context.instance, cls.BASE.get_name())
|
||||
|
||||
@classmethod
|
||||
def exists(cls, context: WeegeeContext, name: str) -> bool:
|
||||
return Item.exists(context.instance, cls.get_name(name))
|
||||
|
||||
@classmethod
|
||||
def create(cls, context: WeegeeContext, name: str, **kwargs) -> 'WeegeeBase':
|
||||
meta = cls.get_meta(context)
|
||||
item = Item.make(context.instance, cls.get_name(name), **kwargs)
|
||||
if not item.resolve(meta).is_value_complete(meta):
|
||||
raise TypeError('internal error')
|
||||
return cls(context, item, meta)
|
||||
|
||||
@classmethod
|
||||
def find_all(cls, context: WeegeeContext) -> list['WeegeeBase']:
|
||||
return cls.find(context, '*')
|
||||
|
||||
@classmethod
|
||||
def find(cls, context: WeegeeContext, pattern: str) -> list['WeegeeBase']:
|
||||
return [cls(context, Item.load(context.instance, name)) for name in Item.filter(context.instance, cls.get_name(pattern))]
|
||||
|
||||
@classmethod
|
||||
def load(cls, context: WeegeeContext, name: str, path: O[str] = None) -> 'WeegeeBase':
|
||||
return cls(context, Item.load(context.instance, cls.get_name(name)) if not path else Item.loadfile(name, path))
|
||||
|
||||
def save(self, path: O[str] = None) -> None:
|
||||
unresolved = self.item.unresolve(self.meta)
|
||||
if path:
|
||||
unresolved.savefile(path)
|
||||
else:
|
||||
unresolved.save()
|
||||
|
||||
def delete(self) -> None:
|
||||
self.item.delete()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.item_name[len(self.get_name('')):]
|
||||
|
||||
@property
|
||||
def item_name(self) -> str:
|
||||
return self.item.name
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
if name not in self.item:
|
||||
raise AttributeError(name)
|
||||
return self.item[name]
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
if 'item' in self.__dict__ and self.item.is_type_resolved(name):
|
||||
self.item[name] = value
|
||||
else:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __dir__(self) -> list[str]:
|
||||
return super().__dir__() + list(self.item.values)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
return isinstance(other, self.__class__) and self.item.name == other.item.name
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.__class__.__name__, self.item_name))
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeHook(WeegeeBase):
|
||||
BASE = WEEGEE_HOOK
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: WeegeeContext, name: str, pre: list[str], post: list[str]) -> 'WeegeeHook':
|
||||
return super().create(ctx, name,
|
||||
pre=pre, post=post,
|
||||
)
|
||||
|
||||
@dataclass
|
||||
class WeegeeHookRunner:
|
||||
hooks: list[WeegeeHook]
|
||||
conn: WireguardConnection
|
||||
kwargs: dict[str, Any]
|
||||
|
||||
def _run(self, cmd: str) -> None:
|
||||
for k, v in self.kwargs.items():
|
||||
cmd = cmd.replace('%' + k, str(v))
|
||||
return self.conn._run(cmd, shell=True)
|
||||
|
||||
def __enter__(self) -> 'WeegeeHookRunner':
|
||||
for h in self.hooks:
|
||||
for cmd in h.pre:
|
||||
self._run(cmd)
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||
if not exc_type:
|
||||
for h in self.hooks:
|
||||
for cmd in h.post:
|
||||
self._run(cmd)
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeHost(WeegeeBase):
|
||||
BASE = WEEGEE_HOST
|
||||
LOCAL_HOST_NAME = 'local'
|
||||
conn: WireguardHost = None
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: WeegeeContext, name: str, type: WireguardHostType = WireguardHostType.Unsupported, host: O[str] = None, user: O[str] = None, elevate_user: O[str] = None, automanage: bool = False, autosync: bool = False) -> 'WeegeeHost':
|
||||
return super().create(ctx, name,
|
||||
automanage=automanage,
|
||||
autosync=autosync,
|
||||
type=type.value,
|
||||
host=host,
|
||||
user=user,
|
||||
elevate_user=elevate_user,
|
||||
)
|
||||
|
||||
@property
|
||||
def type(self) -> WireguardHostType:
|
||||
return WireguardHostType(self.item['type'])
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
super().__post_init__()
|
||||
self.conn = WireguardHost(WeegeeHookedWireguardConnection(self, which_connection(self.type)(self.host, self.user, self.elevate_user)))
|
||||
|
||||
@classmethod
|
||||
def get_local_host(cls, ctx: WeegeeContext) -> 'WeegeeHost':
|
||||
return cls.load(ctx, cls.LOCAL_HOST_NAME)
|
||||
|
||||
@classmethod
|
||||
def list_hooks(cls) -> list[str]:
|
||||
return ['interface_add', 'interface_del', 'address_add', 'address_del', 'route_add', 'route_del', 'configure']
|
||||
|
||||
def get_hooks(self, name: str, **kwargs) -> WeegeeHookRunner:
|
||||
return WeegeeHookRunner([WeegeeHook(self.context, x) for x in getattr(self, f'{name}_hooks')], self.conn.conn, kwargs)
|
||||
|
||||
def get_peers(self, name: str) -> O[list[WireguardPeer]]:
|
||||
interface = self.conn.get_interface(name)
|
||||
if not interface:
|
||||
return None
|
||||
return interface.list_peers()
|
||||
|
||||
def sync_interface(self, name: str, addresses: list[IPInterface], routes: list[IPNetwork], config: str) -> None:
|
||||
interface = self.conn.get_interface(name)
|
||||
if not interface:
|
||||
if not self.automanage:
|
||||
return
|
||||
interface = self.conn.create_interface(name)
|
||||
if self.automanage:
|
||||
interface.sync(addresses, routes)
|
||||
interface.sync_config(config)
|
||||
|
||||
def delete_interface(self, name: str) -> None:
|
||||
interface = self.conn.get_interface(name)
|
||||
if interface:
|
||||
interface.clear_config()
|
||||
if self.automanage:
|
||||
interface.delete()
|
||||
|
||||
@dataclass
|
||||
class WeegeeHookedWireguardConnection(WireguardConnection):
|
||||
host: WireguardHost
|
||||
inner: WireguardConnection
|
||||
|
||||
def _run(self, *args, **kwargs) -> str:
|
||||
return self.inner._run(*args, **kwargs)
|
||||
|
||||
def has_interface(self, name: str) -> bool:
|
||||
return self.inner.has_interface(name)
|
||||
|
||||
def create_interface(self, name: str) -> None:
|
||||
with self.host.get_hooks('interface_add', i=name):
|
||||
return self.inner.create_interface(name)
|
||||
|
||||
def destroy_interface(self, name: str) -> None:
|
||||
with self.host.get_hooks('interface_del', i=name):
|
||||
return self.inner.destroy_interface(name)
|
||||
|
||||
def set_mtu(self, name: str, mtu: int) -> None:
|
||||
return self.inner.set_mtu(name, mtu)
|
||||
|
||||
def set_up(self, name: str) -> None:
|
||||
return self.inner.set_up(name)
|
||||
|
||||
def set_down(self, name: str) -> None:
|
||||
return self.inner.set_down(name)
|
||||
|
||||
def get_addresses(self, name: str) -> list[IPInterface]:
|
||||
return self.inner.get_addresses(name)
|
||||
|
||||
def add_address(self, name: str, address: IPInterface) -> None:
|
||||
with self.host.get_hooks('address_add', i=name, a=str(address)):
|
||||
return self.inner.add_address(name, address)
|
||||
|
||||
def delete_address(self, name: str, address: IPInterface) -> None:
|
||||
with self.host.get_hooks('address_del', i=name, a=str(address)):
|
||||
return self.inner.delete_address(name, address)
|
||||
|
||||
def delete_all_addresses(self, name: str) -> None:
|
||||
for address in self.get_addresses(name):
|
||||
self.delete_address(name, address)
|
||||
|
||||
def get_routes(self, name: str) -> list[IPNetwork]:
|
||||
return self.inner.get_routes(name)
|
||||
|
||||
def add_route(self, name: str, route: IPNetwork) -> None:
|
||||
with self.host.get_hooks('route_add', i=name, r=str(route)):
|
||||
return self.inner.add_route(name, route)
|
||||
|
||||
def delete_route(self, name: str, route: IPNetwork) -> None:
|
||||
with self.host.get_hooks('route_del', i=name, r=str(route)):
|
||||
return self.inner.delete_route(name, route)
|
||||
|
||||
def delete_all_routes(self, name: str) -> None:
|
||||
for route in self.get_routes(name):
|
||||
self.delete_route(name, route)
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeInterface(WeegeeBase):
|
||||
BASE = WEEGEE_INTERFACE
|
||||
|
||||
@classmethod
|
||||
def find_for_hosts(cls, ctx: WeegeeContext, hosts: set[WeegeeHost]) -> set['WeegeeInterface']:
|
||||
return set(i for i in cls.find_all(ctx) if set(i.hosts) & hosts)
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: WeegeeContext, name: str, interface_name: str, private_key: O[str] = None, public_key: O[str] = None, port: O[int] = None, hosts: O[list[WeegeeHost]] = None, addresses: list[IPInterface] = []) -> 'WeegeeInterface':
|
||||
for host in [WeegeeHost.get_local_host(ctx)] + hosts:
|
||||
try:
|
||||
private_key = private_key or host.conn.gen_private_key()
|
||||
public_key = public_key or host.conn.get_public_key(private_key)
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warn(f'could not generate keypair on host {host.name!r}: {e!r}')
|
||||
else:
|
||||
logger.critical('could not generate public/private keypair automatically: please pass explicitly!')
|
||||
raise ValueError()
|
||||
|
||||
return super().create(ctx, name,
|
||||
hosts=[h.item for h in hosts],
|
||||
interface_name=interface_name,
|
||||
public_key=public_key, private_key=private_key,
|
||||
addresses=addresses, port=port,
|
||||
)
|
||||
|
||||
def save(self) -> None:
|
||||
for host in self.hosts:
|
||||
host.save()
|
||||
super().save()
|
||||
|
||||
def delete(self) -> None:
|
||||
peers = WeegeePeer.find_for_interfaces(self.context, {self})
|
||||
for p in peers:
|
||||
p.delete()
|
||||
for h in self.hosts:
|
||||
if h.autosync:
|
||||
h.delete_interface(self.interface_name)
|
||||
super().delete()
|
||||
|
||||
@property
|
||||
def hosts(self) -> list[WeegeeHost]:
|
||||
return [WeegeeHost(self.context, x) for x in self.item['hosts']]
|
||||
|
||||
def gen_config(self) -> str:
|
||||
template = Template.load(self.context.instance, WEEGEE_INTERFACE_CONF_WG.get_name())
|
||||
config = WEEGEE_INTERFACE_CONF_WG.make_config(self.context.instance,
|
||||
interface=self.item,
|
||||
)
|
||||
return template.render(config)
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeePeer(WeegeeBase):
|
||||
BASE = WEEGEE_PEER
|
||||
|
||||
@classmethod
|
||||
def find_for_interfaces(cls, ctx: WeegeeContext, interfaces: set[WeegeeInterface]) -> set['WeegeePeer']:
|
||||
return set(p for p in cls.find_all(ctx) if p.interface in interfaces)
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: WeegeeContext, name: str, interface: WeegeeInterface, routes: list[IPNetwork], host: O[str] = None, port: O[int] = None) -> 'WeegeePeer':
|
||||
return super().create(ctx, name,
|
||||
interface=interface.item,
|
||||
routes=routes,
|
||||
host=host, port=port,
|
||||
)
|
||||
|
||||
def save(self) -> None:
|
||||
self.interface.save()
|
||||
super().save()
|
||||
self.sync(auto=True)
|
||||
|
||||
def sync(self, auto=False, log=None) -> None:
|
||||
if not log:
|
||||
log = set()
|
||||
if self.item_name in log:
|
||||
return
|
||||
log.add(self.item_name)
|
||||
logger.info(f'syncing peer: {self.name}')
|
||||
sync_interface(self.interface, auto=auto, log=log)
|
||||
|
||||
def delete(self) -> None:
|
||||
for c in WeegeeConnection.find_for_peers(self.context, {self}):
|
||||
c.peers.remove(self)
|
||||
if len(c.peers) <= 1:
|
||||
c.delete()
|
||||
else:
|
||||
c.save()
|
||||
super().delete()
|
||||
|
||||
@property
|
||||
def interface(self) -> WeegeeInterface:
|
||||
return WeegeeInterface(self.context, self.item['interface'])
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeConnection(WeegeeBase):
|
||||
BASE = WEEGEE_CONNECTION
|
||||
|
||||
@classmethod
|
||||
def find_for_peers(cls, ctx: WeegeeContext, peers: set[WeegeePeer]) -> set['WeegeeConnection']:
|
||||
return set(c for c in cls.find_all(ctx) if set(c.peers) & peers)
|
||||
|
||||
@classmethod
|
||||
def create(cls, ctx: WeegeeContext, name: str, peers: list[WeegeePeer], preshared_key: O[str] = None) -> 'WeegeeConnecton':
|
||||
if not preshared_key:
|
||||
hosts = {WeegeeHost.get_local_host(ctx)}
|
||||
for p in peers:
|
||||
hosts.update(p.interface.hosts)
|
||||
for host in hosts:
|
||||
try:
|
||||
preshared_key = host.conn.gen_preshared_key()
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warn(f'could not generate preshared key on host {host.name!r}: {e!r}')
|
||||
else:
|
||||
logger.critical('could not generate preshared key automatically: please pass explicitly!')
|
||||
raise ValueError()
|
||||
return super().create(ctx, name,
|
||||
peers=[p.item for p in peers],
|
||||
preshared_key=preshared_key,
|
||||
)
|
||||
|
||||
def save(self) -> None:
|
||||
for peer in self.peers:
|
||||
peer.save()
|
||||
super().save()
|
||||
|
||||
@property
|
||||
def peers(self) -> list[WeegeePeer]:
|
||||
return [WeegeePeer(self.context, x) for x in self.item['peers']]
|
||||
|
||||
def gen_config(self, target: WeegeePeer) -> str:
|
||||
peers = [p for p in self.peers if p != target]
|
||||
template = Template.load(self.context.instance, WEEGEE_PEER_CONF_WG.get_name())
|
||||
config = WEEGEE_PEER_CONF_WG.make_config(self.context.instance,
|
||||
connection=self.item,
|
||||
peers={p.name: p.item for p in peers},
|
||||
)
|
||||
return template.render(config)
|
||||
|
||||
|
||||
def do_config_interface(interface: WeegeeInterface, peers: set[WeegeePeer], connections: set[WeegeeConnection]) -> str:
|
||||
peer_configs = []
|
||||
for c in connections:
|
||||
for p in set(c.peers) & peers:
|
||||
peer_configs.append(c.gen_config(p))
|
||||
interface_config = interface.gen_config()
|
||||
|
||||
template = Template.load(interface.context.instance, WEEGEE_CONF_WG.get_name())
|
||||
config = WEEGEE_CONF_WG.make_config(interface.context.instance,
|
||||
interface_config=interface_config,
|
||||
peer_configs=peer_configs,
|
||||
)
|
||||
return template.render(config)
|
||||
|
||||
def do_discover_interface(interface: WeegeeInterface, peers: set[WeegeePeer], connections: set[WeegeeConnection]) -> tuple[list[IPNetwork], set[WeegeePeer]]:
|
||||
routes = []
|
||||
other_peers = set()
|
||||
for c in connections:
|
||||
for p in set(c.peers) - peers:
|
||||
routes.extend(p.routes)
|
||||
routes.extend(a.network for a in p.interface.addresses)
|
||||
other_peers.add(p)
|
||||
return routes, other_peers
|
||||
|
||||
def find_host_interfaces(host: WeegeeHost) -> set[WeegeeInterface]:
|
||||
return WeegeeInterface.find_for_hosts(host.context, {host})
|
||||
|
||||
def find_interface_connections(interface: WeegeeInterface) -> tuple[set[WeegeePeer], set[WeegeeConnection]]:
|
||||
peers = WeegeePeer.find_for_interfaces(interface.context, {interface})
|
||||
connections = WeegeeConnection.find_for_peers(interface.context, peers)
|
||||
return peers, connections
|
||||
|
||||
def find_interface_other_peers(interface: WeegeeInterface) -> set[WeegeePeer]:
|
||||
own_peers, connections = find_interface_connections(interface)
|
||||
_, other_peers = do_discover_interface(interface, own_peers, connections)
|
||||
return other_peers
|
||||
|
||||
def do_sync_interface(interface: WeegeeInterface, peers: set[WeegeePeer], connections: set[WeegeeConnection], auto: bool = False) -> set[WeegeePeer]:
|
||||
config = do_config_interface(interface, peers, connections)
|
||||
routes, other_peers = do_discover_interface(interface, peers, connections)
|
||||
for host in interface.hosts:
|
||||
if auto and not host.autosync:
|
||||
continue
|
||||
host.sync_interface(interface.interface_name, interface.addresses, routes, config)
|
||||
return other_peers
|
||||
|
||||
def sync_interface(interface: WeegeeInterface, auto=False, log=None) -> None:
|
||||
if log is None:
|
||||
log = set()
|
||||
if interface.item_name in log:
|
||||
return
|
||||
log.add(interface.item_name)
|
||||
logger.info(f'syncing interface: {interface.name}')
|
||||
|
||||
all_peers, all_connections = find_interface_connections(interface)
|
||||
other_peers = do_sync_interface(interface, all_peers, all_connections, auto=auto)
|
||||
|
||||
for p in other_peers:
|
||||
sync_interface(p.interface, auto=auto, log=log)
|
||||
|
||||
def sync_all_interfaces(context: WeegeeContext, auto=False, log=None) -> None:
|
||||
if log is None:
|
||||
log = set()
|
||||
for interface in WeegeeInterface.find_all(context):
|
||||
sync_interface(interface, auto=auto, log=log)
|
|
@ -85,8 +85,11 @@ WEEGEE_CONFIG = WeegeeMeta(
|
|||
name='wg/config',
|
||||
version=1,
|
||||
spec=[
|
||||
f'default_server_hosts: [@{WEEGEE_HOST.get_name()}] = []',
|
||||
f'default_client_hosts: [@{WEEGEE_HOST.get_name()}] = []',
|
||||
f'default_server_hosts: [str] = []',
|
||||
f'default_client_hosts: [str] = []',
|
||||
'log_level: str = "info"',
|
||||
'meta_path: str = "."',
|
||||
'data_path: ?str = ',
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
from __future__ import annotations
|
||||
from logging import getLogger
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional as O, Any
|
||||
|
||||
from .config import WeegeeContext
|
||||
from .core import (
|
||||
WeegeePeer, WeegeeConnection,
|
||||
sync_all_interfaces, find_interface_connections, do_config_interface,
|
||||
)
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeServer(WeegeePeer):
|
||||
def get_client_name(self, name: str) -> str:
|
||||
return f'{self.name}/{name}'
|
||||
|
||||
def get_clients(self) -> list[WeegeePeer]:
|
||||
return WeegeeClient.find(self.context, self.get_client_name('*'))
|
||||
|
||||
def get_client(self, name: str) -> O[WeegeePeer]:
|
||||
return WeegeeClient.load(self.context, self.get_client_name(name))
|
||||
|
||||
def delete(self) -> None:
|
||||
super().delete()
|
||||
sync_all_interfaces(self.context, auto=True)
|
||||
|
||||
def gen_config(self) -> str:
|
||||
peers, connections = find_interface_connections(self.interface)
|
||||
return do_config_interface(self.interface, peers, connections)
|
||||
|
||||
@dataclass(eq=False)
|
||||
class WeegeeClient(WeegeePeer):
|
||||
@classmethod
|
||||
def create(cls, ctx: WeegeeContext, name: str, server: WeegeeServer, preshared_key: O[str] = None, **kwargs) -> 'WeegeeClient':
|
||||
name = server.get_client_name(name)
|
||||
client = super().create(ctx, name, **kwargs)
|
||||
connection = WeegeeConnection.create(ctx, name, peers=[server, client], preshared_key=preshared_key)
|
||||
connection.save()
|
||||
return client
|
||||
|
||||
@property
|
||||
def connection(self) -> WeegeeConnection:
|
||||
return WeegeeConnection.load(self.context, self.name)
|
||||
|
||||
@property
|
||||
def server(self) -> WeegeeServer:
|
||||
return next(p for p in self.connection.peers if p != self.peer)
|
||||
|
||||
def delete(self) -> None:
|
||||
self.connection.delete()
|
||||
super().delete()
|
||||
sync_all_interfaces(self.context, auto=True)
|
||||
|
||||
def gen_config(self) -> str:
|
||||
peers, connections = find_interface_connections(self.interface)
|
||||
return do_config_interface(self.interface, peers, connections)
|
Loading…
Reference in New Issue