class SaveReportServerAction(AbstractServerAction): name = 'save-report' before_send = Signal('SaveReportServerAction.before_send') after_send = Signal('SaveReportServerAction.after_send') before_receive = Signal('SaveReportServerAction.before_receive') after_receive = Signal('SaveReportServerAction.after_receive') after_confirm = Signal('SaveReportServerAction.after_confirm') def execute(self, connection): with create_session() as session: client = session.query(Client).filter( Client.id == connection.client_id).first() report = Report(client=client) before_report_create.emit(session=session, report=report) session.add(report) session.flush() after_report_create.emit(session=session, report=report) values = self.payload['report'] for name in reports_registry: value = values.get(name, None) before_report_value_create.emit(session=session, report_value=value) session.add(ReportValue(report=report, name=name, value=value)) session.flush() after_report_value_create.emit(session=session, report_value=value)
class SetCommand(AbstractCommand): name = 'set' help = 'Set config settings' before_run = Signal('SetCommand.before_run') after_run = Signal('SetCommand.after_run') @staticmethod def arguments(parser): group = parser.add_argument_group('settings') for name in sorted(settings_registry): setting = settings_registry[name] flag = '--{}'.format(name) metavar = name.rsplit('.', 1)[-1].upper() params = setting.add_argument_params() group.add_argument(flag, metavar=metavar, help=setting.help, **params) def run(self, args): affected = [] for name in settings_registry: value = getattr(args, name) if value is not None: affected.append(name) config.set(name, value) config.save() for name in affected: value = config.get(name) print('{}: {}'.format(name, value))
class InstallCommand(AbstractCommand): name = 'install' help = 'Install Skywall frontend npm dependencies' before_run = Signal('InstallCommand.before_run') after_run = Signal('InstallCommand.after_run') def run(self, args): install_frontend()
class BuildCommand(AbstractCommand): name = 'build' help = 'Build Skywall frontend' before_run = Signal('BuildCommand.before_run') after_run = Signal('BuildCommand.after_run') def run(self, args): build_frontend()
class ClientCommand(AbstractCommand): name = 'client' help = 'Run skywall client' before_run = Signal('ClientCommand.before_run') after_run = Signal('ClientCommand.after_run') def run(self, args): config.validate(CLIENT_MODE) run_client()
class PingClientAction(AbstractClientAction): name = 'example-ping' before_send = Signal('PingClientAction.before_send') after_send = Signal('PingClientAction.after_send') before_receive = Signal('PingClientAction.before_receive') after_receive = Signal('PingClientAction.after_receive') after_confirm = Signal('PingClientAction.after_confirm') def execute(self, client): print('PingClientAction', self.payload)
class PongServerAction(AbstractServerAction): name = 'example-pong' before_send = Signal('PongServerAction.before_send') after_send = Signal('PongServerAction.after_send') before_receive = Signal('PongServerAction.before_receive') after_receive = Signal('PongServerAction.after_receive') after_confirm = Signal('PongServerAction.after_confirm') def execute(self, connection): print('PongServerAction', self.payload)
class ServerCommand(AbstractCommand): name = 'server' help = 'Run skywall server' before_run = Signal('ServerCommand.before_run') after_run = Signal('ServerCommand.after_run') def run(self, args): config.validate(SERVER_MODE) connect_database() run_server()
class SetIdClientAction(AbstractClientAction): name = 'set-id' before_send = Signal('SetIdClientAction.before_send') after_send = Signal('SetIdClientAction.after_send') before_receive = Signal('SetIdClientAction.before_receive') after_receive = Signal('SetIdClientAction.after_receive') after_confirm = Signal('SetIdClientAction.after_confirm') def execute(self, client): config.set('client.id', self.payload['client_id']) config.set('client.token', self.payload['client_token']) config.save()
class SetLabelClientAction(AbstractClientAction): name = 'set-label' before_send = Signal('SetLabelClientAction.before_send') after_send = Signal('SetLabelClientAction.after_send') before_receive = Signal('SetLabelClientAction.before_receive') after_receive = Signal('SetLabelClientAction.after_receive') after_confirm = Signal('SetLabelClientAction.after_confirm') def execute(self, client): label = self.payload['label'] config.set('client.label', label) config.save()
class ExampleCommand(AbstractCommand): name = 'example-echo' help = 'Example Skywall module command' before_run = Signal('ExampleCommand.before_run') after_run = Signal('ExampleCommand.after_run') @staticmethod def arguments(parser): parser.add_argument('names', metavar='names', nargs='+', help='Names to print') def run(self, args): print('This is example Skywall module command') example_signal.emit(value=args)
class SaveLabelServerAction(AbstractServerAction): name = 'save-label' before_send = Signal('SaveLabelServerAction.before_send') after_send = Signal('SaveLabelServerAction.after_send') before_receive = Signal('SaveLabelServerAction.before_receive') after_receive = Signal('SaveLabelServerAction.after_receive') after_confirm = Signal('SaveLabelServerAction.after_confirm') def execute(self, connection): with create_session() as session: client = session.query(Client).filter( Client.id == connection.client_id).first() client.label = self.payload['label'] or '' before_client_update.emit(session=session, client=client) session.flush() after_client_update.emit(session=session, client=client)
class GetCommand(AbstractCommand): name = 'get' help = 'Get config settings' before_run = Signal('GetCommand.before_run') after_run = Signal('GetCommand.after_run') @staticmethod def arguments(parser): parser.add_argument('names', metavar='name', nargs='*', help='Name or prefix of names of settings to show') def run(self, args): for name in sorted(settings_registry): if _setting_matches_args(name, args.names): value = config.get(name) print('{}: {}'.format(name, value))
from contextlib import contextmanager from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base from skywall.core.config import config from skywall.core.signals import Signal Model = declarative_base() _session = None before_database_connect = Signal('before_database_connect') after_database_connect = Signal('after_database_connect') def connect_database(): # pylint: disable=global-statement global _session before_database_connect.emit() database = config.get('server.database') engine = create_engine(database, echo=True) _session = sessionmaker(bind=engine) Model.metadata.create_all(engine) after_database_connect.emit() @contextmanager def create_session(): session = _session() try: yield session session.commit()
from skywall.core.config import config from skywall.core.signals import Signal from skywall.core.api import api_registry from skywall.core.database import create_session from skywall.core.actions import parse_server_action from skywall.core.constants import CLIENT_ID_HEADER, CLIENT_TOKEN_HEADER, API_ROUTE, BUILD_ROUTE from skywall.core.frontend import get_frontend, run_webpack from skywall.core.utils import randomstring from skywall.models.clients import Client, before_client_create, after_client_create from skywall.models.connections import (Connection, before_connection_create, after_connection_create, before_connection_update, after_connection_update) from skywall.actions.clientid import SetIdClientAction before_server_start = Signal('before_server_start') after_server_start = Signal('after_server_start') before_server_stop = Signal('before_server_stop') after_server_stop = Signal('after_server_stop') before_server_connection_open = Signal('before_server_connection_open') after_server_connection_open = Signal('after_server_connection_open') before_server_connection_close = Signal('before_server_connection_close') after_server_connection_close = Signal('after_server_connection_close') after_server_connection_ended = Signal('after_server_connection_ended') before_client_action_send = Signal('before_client_action_send') after_client_action_send = Signal('after_client_action_send') after_client_action_confirm = Signal('after_client_action_confirm') before_server_action_receive = Signal('before_server_action_receive')
import os import re from ruamel import yaml from skywall.core.settings import settings_registry from skywall.core.signals import Signal before_config_load = Signal('before_config_load') after_config_load = Signal('after_config_load') before_config_save = Signal('before_config_save') after_config_save = Signal('after_config_save') class Config: data = None def load(self): before_config_load.emit(config=self) try: with open('config.yaml', 'r') as f: self.data = yaml.round_trip_load(f) except FileNotFoundError: self.data = None after_config_load.emit(config=self) def save(self): before_config_save.emit(config=self) with open('config.yaml', 'w') as f: yaml.round_trip_dump(self.data, f) after_config_save.emit(config=self) def validate(self, mode):
import re from aiohttp.web import json_response, HTTPBadRequest from sqlalchemy.exc import IntegrityError from skywall.core.signals import Signal from skywall.core.api import register_api, parse_json_body, assert_request_param_is_string from skywall.core.database import create_session from skywall.models.groups import Group, before_group_create, after_group_create before_add_group = Signal('before_add_group') after_add_group = Signal('after_add_group') @register_api('POST', '/groups', before_add_group, after_add_group) async def add_group(request): """ --- tags: - Skywall Core summary: Add group description: Creates a new group produces: - application/json parameters: - name: body in: body description: Group properties to be saved required: true schema: type: object title: PostGroupBody required:
from functools import wraps from collections import namedtuple from aiohttp.web import HTTPBadRequest, HTTPNotFound from skywall.core.signals import Signal Api = namedtuple('Api', ['method', 'path', 'handler']) api_registry = [] before_api_call = Signal('before_api_call') after_api_call = Signal('after_api_call') def register_api(method, path, before_call, after_call): def decorator(handler): @wraps(handler) async def signaled_handler(request): before_api_call.emit(api=api, request=request) before_call.emit(api=api, request=request) try: response = await handler(request) after_call.emit(api=api, request=request, response=response, exception=None) after_api_call.emit(api=api, request=request, response=response, exception=None) return response except Exception as e: after_call.emit(api=api, request=request, response=None, exception=e) after_api_call.emit(api=api, request=request, response=None, exception=e) raise api = Api(method, path, signaled_handler) api_registry.append(api) return signaled_handler
from sqlalchemy import Column, Integer, String, TIMESTAMP, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.sql.functions import current_timestamp from skywall.core.database import Model from skywall.core.signals import Signal before_client_create = Signal('before_client_create') after_client_create = Signal('after_client_create') before_client_update = Signal('before_client_update') after_client_update = Signal('after_client_update') class Client(Model): __tablename__ = 'client' id = Column(Integer, primary_key=True) created = Column(TIMESTAMP(timezone=True), nullable=False, server_default=current_timestamp()) token = Column(String, nullable=False) label = Column(String, nullable=False, server_default='') group_id = Column(Integer, ForeignKey('group.id'), nullable=True) group = relationship('Group', back_populates='clients') reports = relationship('Report', back_populates='client') connections = relationship('Connection', back_populates='client') def __repr__(self): return '<Client id={0.id} label={0.label}>'.format(self)
from sqlalchemy import Column, Integer, TIMESTAMP, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.sql.functions import current_timestamp from skywall.core.database import Model from skywall.core.signals import Signal before_connection_create = Signal('before_connection_create') after_connection_create = Signal('after_connection_create') before_connection_update = Signal('before_connection_update') after_connection_update = Signal('after_connection_update') class Connection(Model): __tablename__ = 'connection' id = Column(Integer, primary_key=True) created = Column(TIMESTAMP(timezone=True), nullable=False, server_default=current_timestamp()) client_id = Column(Integer, ForeignKey('client.id'), nullable=False) last_activity = Column(TIMESTAMP(timezone=True), nullable=False, server_default=current_timestamp()) closed = Column(TIMESTAMP(timezone=True), nullable=True) client = relationship('Client', back_populates='connections') def __repr__(self): return '<Report id={0.id} client_id={0.client_id}>'.format(self)
from aiohttp.web import json_response, HTTPServiceUnavailable from skywall.core.signals import Signal from skywall.core.api import ( register_api, parse_json_body, parse_obj_path_param, assert_request_param_is_string, assert_request_param_is_entity, ) from skywall.core.database import create_session from skywall.core.server import get_server from skywall.models.groups import Group from skywall.models.clients import Client, before_client_update, after_client_update from skywall.actions.labels import SetLabelClientAction before_update_client = Signal('before_update_client') after_update_client = Signal('after_update_client') async def _send_label_to_client(client_id, label): connection = get_server().get_connection(client_id) if not connection: reason = 'Changing label failed. Client {} is not connected right now.'.format( client_id) raise HTTPServiceUnavailable(reason=reason) try: await connection.check_send_action(SetLabelClientAction(label=label)) except asyncio.TimeoutError: reason = 'Changing label failed. Client {} is not responding right now.'.format( client_id) raise HTTPServiceUnavailable(reason=reason)
from sqlalchemy import Column, Integer, String from sqlalchemy.orm import relationship from skywall.core.database import Model from skywall.core.signals import Signal from skywall.models.clients import Client before_group_create = Signal('before_group_create') after_group_create = Signal('after_group_create') before_group_update = Signal('before_group_update') after_group_update = Signal('after_group_update') before_group_delete = Signal('before_group_delete') after_group_delete = Signal('after_group_delete') class Group(Model): __tablename__ = 'group' id = Column(Integer, primary_key=True) name = Column(String, nullable=False, unique=True) description = Column(String, nullable=False, server_default='') clients = relationship('Client', back_populates='group') def __repr__(self): return '<Group id={0.id} name={0.name}>'.format(self) def get_group_clients(session, group): """ We can't use directly `group.clients` for the DEFAULT group because it is represented by `None`. Unless we are sure the group is not DEFAULT, we must use `get_group_clients(session, group)` instead.
from skywall.core.signals import Signal example_signal = Signal('example_signal') @example_signal.connect def example_signal_listener(value): print('Received example signal: {}'.format(value))
import asyncio from aiohttp import ClientSession, WSCloseCode, WSMsgType, ClientConnectionError, WSServerHandshakeError from skywall.core.constants import ACTION_CONFIRM_TIMEOUT, CLIENT_RECONECT_INTERVAL from skywall.core.config import config from skywall.core.signals import Signal from skywall.core.actions import parse_client_action from skywall.core.reports import collect_report from skywall.core.constants import CLIENT_ID_HEADER, CLIENT_TOKEN_HEADER from skywall.actions.reports import SaveReportServerAction from skywall.actions.labels import SaveLabelServerAction before_client_start = Signal('before_client_start') after_client_start = Signal('after_client_start') before_client_stop = Signal('before_client_stop') after_client_stop = Signal('after_client_stop') before_client_action_receive = Signal('before_client_action_receive') after_client_action_receive = Signal('after_client_action_receive') before_server_action_send = Signal('before_server_action_send') after_server_action_send = Signal('after_server_action_send') after_server_action_confirm = Signal('after_server_action_confirm') class WebsocketClient: def __init__(self, loop): self.url = config.get('server.publicUrl') self.client_id = config.get('client.id') self.client_token = config.get('client.token')
from aiohttp.web import json_response from skywall.core.signals import Signal from skywall.core.api import register_api, parse_obj_path_param from skywall.core.database import create_session from skywall.models.groups import Group, before_group_delete, after_group_delete before_delete_group = Signal('before_delete_group') after_delete_group = Signal('after_delete_group') @register_api('DELETE', '/groups/{groupId}', before_delete_group, after_delete_group) async def delete_group(request): """ --- tags: - Skywall Core summary: Delete group description: Deletes an existing group produces: - application/json parameters: - name: groupId in: path description: ID of group to delete required: true type: integer responses: 200: description: Group deleted schema:
import os import argparse from skywall.core.config import config from skywall.core.modules import import_enabled_modules from skywall.core.commands import commands_registry from skywall.core.signals import Signal before_command_run = Signal('before_command_run') after_command_run = Signal('after_command_run') def assert_virtualenv(): if not os.environ.get('VIRTUAL_ENV'): raise NotImplementedError( 'Running Skywall outside a virtualenv is not supported') def change_to_workdir(): workdir = os.path.dirname(os.environ.get('VIRTUAL_ENV')) os.chdir(workdir) def parse_args(): desc = 'Client-Server based manager for connecting systems together and running tasks.' parser = argparse.ArgumentParser(description=desc) subparsers = parser.add_subparsers(dest='command', metavar='command') subparsers.required = True for command_name in sorted(commands_registry): command = commands_registry[command_name] subparser = subparsers.add_parser(command.name, help=command.help) command.arguments(subparser)
from sqlalchemy import Column, Integer, String, TIMESTAMP, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.sql.functions import current_timestamp from sqlalchemy.dialects.postgresql import JSONB from skywall.core.database import Model from skywall.core.signals import Signal before_report_create = Signal('before_report_create') after_report_create = Signal('after_report_create') before_report_value_create = Signal('before_report_value_create') after_report_value_create = Signal('after_report_value_create') class Report(Model): __tablename__ = 'report' id = Column(Integer, primary_key=True) created = Column(TIMESTAMP(timezone=True), nullable=False, server_default=current_timestamp()) client_id = Column(Integer, ForeignKey('client.id'), nullable=False) client = relationship('Client', back_populates='reports') values = relationship('ReportValue', back_populates='report') def __repr__(self): return '<Report id={0.id} client_id={0.client_id}>'.format(self) class ReportValue(Model):
from aiohttp.web import json_response from skywall.core.database import create_session from skywall.core.signals import Signal from skywall.core.api import register_api from skywall_example.models.example import Example, before_example_create, after_example_create before_get_example = Signal('before_get_example') after_get_example = Signal('before_get_example') @register_api('GET', '/example', before_get_example, after_get_example) async def get_example(request): """ --- tags: - Example module summary: Example API description: Example Skywall module API endpoint produces: - application/json responses: 200: description: Example response schema: type: object title: GetExample required: - message properties: message: type: string
from aiohttp.web import json_response from sqlalchemy import desc from skywall.core.api import register_api from skywall.core.signals import Signal from skywall.core.database import create_session from skywall.core.reports import reports_registry from skywall.core.server import get_server from skywall.models.clients import Client from skywall.models.groups import Group from skywall.models.connections import Connection from skywall.models.reports import Report, ReportValue before_get_clients = Signal('before_get_clients') after_get_clients = Signal('after_get_clients') def _client_response(client): return { 'id': client.id, 'created': client.created.timestamp(), 'label': client.label, 'groupId': client.group_id, 'connected': get_server().get_connection(client.id) is not None, } def _clients_response(clients): return [_client_response(client) for client in clients] def _group_response(group): return {
import re from aiohttp.web import json_response, HTTPBadRequest from sqlalchemy.exc import IntegrityError from skywall.core.signals import Signal from skywall.core.api import register_api, parse_json_body, parse_obj_path_param, assert_request_param_is_string from skywall.core.database import create_session from skywall.models.groups import Group, before_group_update, after_group_update before_update_group = Signal('before_update_group') after_update_group = Signal('after_update_group') @register_api('PUT', '/groups/{groupId}', before_update_group, after_update_group) async def update_group(request): """ --- tags: - Skywall Core summary: Update group description: Updates an existing group produces: - application/json parameters: - name: groupId in: path description: ID of group to update required: true type: integer - name: body in: body