diff --git a/messages/v1/messages/config.configuration.server.get.yml b/messages/v1/messages/config.configuration.server.get.yml index 02c4b67..8305ed1 100644 --- a/messages/v1/messages/config.configuration.server.get.yml +++ b/messages/v1/messages/config.configuration.server.get.yml @@ -18,9 +18,10 @@ parameters: ref: Server.ServerId description: | Identifiant du serveur. - deploy: + deployed: type: Boolean description: Configuration de type déployée. + default: true response: type: ConfigConfiguration diff --git a/messages/v1/messages/config.configuration.server.updated.yml b/messages/v1/messages/config.configuration.server.updated.yml index 66f3952..65f38f3 100644 --- a/messages/v1/messages/config.configuration.server.updated.yml +++ b/messages/v1/messages/config.configuration.server.updated.yml @@ -17,6 +17,6 @@ parameters: type: Number description: | Identifiant du serveur. - deploy: + deployed: type: Boolean description: Configuration de type déployée. diff --git a/messages/v1/messages/servermodel.created.yml b/messages/v1/messages/servermodel.created.yml index e180b78..90d55c4 100644 --- a/messages/v1/messages/servermodel.created.yml +++ b/messages/v1/messages/servermodel.created.yml @@ -10,6 +10,5 @@ public: false domain: servermodel-domain parameters: - servermodels: - type: '[]Servermodel' - description: Informations sur les modèles de serveur créés. + type: Servermodel + description: Informations sur les modèles de serveur créés. diff --git a/messages/v1/messages/servermodel.updated.yml b/messages/v1/messages/servermodel.updated.yml index 46af1fe..531c18e 100644 --- a/messages/v1/messages/servermodel.updated.yml +++ b/messages/v1/messages/servermodel.updated.yml @@ -10,6 +10,5 @@ public: false domain: servermodel-domain parameters: - servermodels: - type: '[]Servermodel' - description: Informations sur les modèles de serveur modifiés. + type: 'Servermodel' + description: Informations sur les modèles de serveur modifiés. diff --git a/messages/v1/messages/session.server.get.yml b/messages/v1/messages/session.server.get.yml index 3484914..33dc568 100644 --- a/messages/v1/messages/session.server.get.yml +++ b/messages/v1/messages/session.server.get.yml @@ -2,7 +2,7 @@ uri: session.server.get description: | - Configure le server. + Récupérer la configuration du server. pattern: rpc @@ -16,6 +16,11 @@ parameters: ref: Config.SessionId shortarg: s description: Identifiant de la configuration. + name: + type: String + shortarg: n + description: Nom de la variable. + default: null response: type: Session diff --git a/messages/v1/messages/session.server.validate.yml b/messages/v1/messages/session.server.validate.yml index 81134c6..383312c 100644 --- a/messages/v1/messages/session.server.validate.yml +++ b/messages/v1/messages/session.server.validate.yml @@ -18,6 +18,6 @@ parameters: description: Identifiant de la session. response: - type: SessionConfigurationStatus + type: Session description: Statut de la configuration. diff --git a/messages/v1/messages/session.servermodel.get.yml b/messages/v1/messages/session.servermodel.get.yml index c22cd9b..aac98a8 100644 --- a/messages/v1/messages/session.servermodel.get.yml +++ b/messages/v1/messages/session.servermodel.get.yml @@ -16,6 +16,11 @@ parameters: ref: Config.SessionId shortarg: s description: Identifiant de la configuration. + name: + type: String + shortarg: n + description: Nom de la variable. + default: null response: type: Session diff --git a/messages/v1/messages/session.servermodel.validate.yml b/messages/v1/messages/session.servermodel.validate.yml index 7d3e25d..1fe0078 100644 --- a/messages/v1/messages/session.servermodel.validate.yml +++ b/messages/v1/messages/session.servermodel.validate.yml @@ -18,6 +18,6 @@ parameters: description: Identifiant de la session. response: - type: SessionConfigurationStatus + type: Session description: Statut de la configuration. diff --git a/messages/v1/types/config.configuration.yml b/messages/v1/types/config.configuration.yml index 05aaee5..fbd26e7 100644 --- a/messages/v1/types/config.configuration.yml +++ b/messages/v1/types/config.configuration.yml @@ -3,8 +3,15 @@ title: ConfigConfiguration type: object description: Description de la configuration. properties: + server_id: + type: number + description: Identifiant du serveur. + ref: Server.ServerId + deployed: + type: boolean + description: La configuration est déployée. configuration: - type: File + type: object description: Détail de la configuration au format JSON. required: - configuration diff --git a/messages/v1/types/servermodel.yml b/messages/v1/types/servermodel.yml index 1d2ff34..c963f44 100644 --- a/messages/v1/types/servermodel.yml +++ b/messages/v1/types/servermodel.yml @@ -37,16 +37,16 @@ properties: type: object description: Liste des services applicatifs déclarés pour ce modèle de serveur. schema: - type: File + type: string description: Contenu du schema. probes: - type: File + type: string description: Informations sur les sondes. creolefuncs: - type: File + type: string description: Fonctions Creole. conffiles: - type: File + type: string description: Fichiers creole au format tar encodé base64 required: - servermodelid diff --git a/messages/v1/types/session.configuration.status.yml b/messages/v1/types/session.configuration.status.yml deleted file mode 100644 index d84c3e2..0000000 --- a/messages/v1/types/session.configuration.status.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: SessionConfigurationStatus -type: object -description: Statut de la configuration. -properties: - session_id: - type: string - description: ID de la session. - ref: Config.SessionId - status: - type: string - description: Statut de la configuration (peut être ok, error, incomplete) - message: - type: string - description: Message d'erreur si la configuration a le statut error. - mandatories: - type: array - items: - type: string - description: Liste des variables obligatoires non renseignées si la configuration a le statut incomplete. -required: - - session_id - - status - diff --git a/messages/v1/types/session.status.yml b/messages/v1/types/session.status.yml index cdad2ad..bb3497d 100644 --- a/messages/v1/types/session.status.yml +++ b/messages/v1/types/session.status.yml @@ -12,9 +12,6 @@ properties: index: type: number description: Index de la variable a modifier. - status: - type: string - description: Status de la modification. message: type: string description: Message d'erreur. diff --git a/messages/v1/types/session.yml b/messages/v1/types/session.yml index 0641515..62f4d56 100644 --- a/messages/v1/types/session.yml +++ b/messages/v1/types/session.yml @@ -27,7 +27,7 @@ properties: type: boolean description: La configuration est en mode debug. content: - type: file + type: object description: Contenu de la configuration. required: - session_id diff --git a/src/risotto/__init__.py b/src/risotto/__init__.py index 6b111a1..0c67d85 100644 --- a/src/risotto/__init__.py +++ b/src/risotto/__init__.py @@ -1,6 +1,4 @@ from .http import get_app -# just to register every route -from . import services as _services __ALL__ = ('get_app',) diff --git a/src/risotto/config.py b/src/risotto/config.py index e6b8b8e..0ecd5b0 100644 --- a/src/risotto/config.py +++ b/src/risotto/config.py @@ -1,7 +1,7 @@ HTTP_PORT = 8080 MESSAGE_ROOT_PATH = 'messages' ROOT_CACHE_DIR = 'cache' -DEBUG = True +DEBUG = False DATABASE_DIR = 'database' INTERNAL_USER = 'internal' CONFIGURATION_DIR = 'configurations' diff --git a/src/risotto/dispatcher.py b/src/risotto/dispatcher.py index 112a343..3bc5737 100644 --- a/src/risotto/dispatcher.py +++ b/src/risotto/dispatcher.py @@ -149,7 +149,7 @@ class PublishDispatcher: return # config is ok, so publish the message - for function_obj in self.messages[version][message]['functions']: + for function_obj in self.messages[version][message].get('functions', []): function = function_obj['function'] module_name = function.__module__.split('.')[-2] function_name = function.__name__ @@ -163,21 +163,21 @@ class PublishDispatcher: if function_obj['risotto_context']: kw['risotto_context'] = risotto_context # send event - await function(self.injected_self[function_obj['module']], **kw) + returns = await function(self.injected_self[function_obj['module']], **kw) except Exception as err: if DEBUG: print_exc() log.error_msg(risotto_context, kwargs, err, info_msg) + continue else: log.info_msg(risotto_context, kwargs, info_msg) - - # notification - if obj.get('notification'): - notif_version, notif_message = obj['notification'].split('.', 1) - await self.publish(notif_version, - notif_message, - risotto_context, - **returns) + # notification + if function_obj.get('notification'): + notif_version, notif_message = function_obj['notification'].split('.', 1) + await self.publish(notif_version, + notif_message, + risotto_context, + **returns) class Dispatcher(register.RegisterDispatcher, CallDispatcher, PublishDispatcher): diff --git a/src/risotto/http.py b/src/risotto/http.py index 74dbda1..55db445 100644 --- a/src/risotto/http.py +++ b/src/risotto/http.py @@ -1,6 +1,9 @@ from aiohttp.web import Application, Response, get, post, HTTPBadRequest, HTTPInternalServerError, HTTPNotFound -from tiramisu import Config from json import dumps +from traceback import print_exc +from tiramisu import Config + + from .dispatcher import dispatcher from .utils import _ from .context import Context @@ -8,7 +11,7 @@ from .error import CallError, NotAllowedError, RegistrationError from .message import get_messages from .logger import log from .config import DEBUG, HTTP_PORT -from traceback import print_exc +from .services import load_services def create_context(request): @@ -96,6 +99,7 @@ async def get_app(loop): """ build all routes """ global extra_routes + load_services() app = Application(loop=loop) routes = [] for version, messages in dispatcher.messages.items(): diff --git a/src/risotto/logger.py b/src/risotto/logger.py index 2dd1f8d..b8f1655 100644 --- a/src/risotto/logger.py +++ b/src/risotto/logger.py @@ -1,6 +1,7 @@ from typing import Dict from .context import Context from .utils import _ +from .config import DEBUG class Logger: @@ -30,6 +31,7 @@ class Logger: """ send message when an error append """ paths_msg = self._get_message_paths(risotto_context) + # if DEBUG: print(_(f'{risotto_context.username}: ERROR: {error} ({paths_msg} with arguments "{arguments}": {msg})')) def info_msg(self, @@ -48,7 +50,8 @@ class Logger: if msg: tmsg += f' {msg}' - print(tmsg) + if DEBUG: + print(tmsg) log = Logger() diff --git a/src/risotto/register.py b/src/risotto/register.py index 8c28172..1248fa7 100644 --- a/src/risotto/register.py +++ b/src/risotto/register.py @@ -209,10 +209,10 @@ class RegisterDispatcher: self.messages[version][message]['functions'] = [] dico = {'module': module_name, - 'functions': function, + 'function': function, 'arguments': function_args, 'risotto_context': inject_risotto_context} - if notification: + if notification and notification is not undefined: dico['notification'] = notification self.messages[version][message]['functions'].append(dico) diff --git a/src/risotto/services/__init__.py b/src/risotto/services/__init__.py index 2c0f6cc..a9be627 100644 --- a/src/risotto/services/__init__.py +++ b/src/risotto/services/__init__.py @@ -4,15 +4,16 @@ from importlib import import_module from ..dispatcher import dispatcher -def list_import(): +def load_services(modules=None, + validate: bool=True): abs_here = dirname(abspath(__file__)) here = basename(abs_here) module = basename(dirname(abs_here)) - for filename in listdir(abs_here): + if not modules: + modules = listdir(abs_here) + for filename in modules: absfilename = join(abs_here, filename) if isdir(absfilename) and isfile(join(absfilename, '__init__.py')): dispatcher.set_module(filename, import_module(f'.{here}.{filename}', module)) - dispatcher.validate() - - -list_import() + if validate: + dispatcher.validate() diff --git a/src/risotto/services/config/config.py b/src/risotto/services/config/config.py index cea5a67..673e9d4 100644 --- a/src/risotto/services/config/config.py +++ b/src/risotto/services/config/config.py @@ -1,9 +1,9 @@ from lxml.etree import parse from io import BytesIO +from os import unlink from os.path import isdir, isfile, join from traceback import print_exc -from json import dumps -from typing import Dict +from typing import Dict, List from tiramisu import Storage, delete_session, MetaConfig, MixConfig from rougail import load as rougail_load @@ -22,7 +22,7 @@ class Risotto(Controller): server = {} def __init__(self) -> None: - for dirname in [ROOT_CACHE_DIR, DATABASE_DIR, ROUGAIL_DTD_PATH]: + for dirname in [ROOT_CACHE_DIR, DATABASE_DIR]: if not isdir(dirname): raise RegistrationError(_(f'unable to find the cache dir "{dirname}"')) self.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR) @@ -58,7 +58,8 @@ class Risotto(Controller): for servermodel in servermodels: if 'servermodelparentsid' in servermodel: for servermodelparentid in servermodel['servermodelparentsid']: - self.servermodel_legacy(servermodel['servermodelname'], + self.servermodel_legacy(risotto_context, + servermodel['servermodelname'], servermodel['servermodelid'], servermodelparentid) @@ -156,6 +157,7 @@ class Risotto(Controller): return metaconfig def servermodel_legacy(self, + risotto_context: Context, servermodel_name: str, servermodel_id: int, servermodel_parent_id: int) -> None: @@ -284,99 +286,106 @@ class Risotto(Controller): async def server_deleted(self, server_id: int) -> None: # delete config to it's parents - for config in self.server[server_id].values(): + for server_type in ['server', 'server_to_deploy']: + config = self.server[server_id]['server'] for parent in config.config.parents(): parent.config.pop(config.config.name()) - delete_session(config.config.name()) + delete_session(storage=self.save_storage, + session_id=config.config.name()) # delete metaconfig del self.server[server_id] @register('v1.servermodel.created') async def servermodel_created(self, - servermodels) -> None: + risotto_context: Context, + servermodelid: int, + servermodelname: str, + servermodelparentsid: List[int]) -> None: """ when servermodels are created, load it and do link """ - for servermodel in servermodels: - await self.load_servermodel(servermodel['servermodelid'], servermodel['servermodelname']) - for servermodel in servermodels: - if 'servermodelparentsid' in servermodel: - for servermodelparentid in servermodel['servermodelparentsid']: - self.servermodel_legacy(servermodel['servermodelname'], servermodel['servermodelid'], servermodelparentid) + await self.load_and_link_servermodel(risotto_context, + servermodelid, + servermodelname, + servermodelparentsid) + + + async def load_and_link_servermodel(self, + risotto_context: Context, + servermodelid: int, + servermodelname: str, + servermodelparentsid: List[int]) -> None: + await self.load_servermodel(risotto_context, + servermodelid, + servermodelname) + if servermodelparentsid is not None: + for servermodelparentid in servermodelparentsid: + self.servermodel_legacy(risotto_context, + servermodelname, + servermodelid, + servermodelparentid) + + def servermodel_delete(self, + servermodelid: int) -> List[MetaConfig]: + metaconfig = self.servermodel.pop(servermodelid) + mixconfig = next(metaconfig.config.list()) + children = [] + for child in mixconfig.config.list(): + children.append(child) + mixconfig.config.pop(child.config.name()) + metaconfig.config.pop(mixconfig.config.name()) + delete_session(storage=self.save_storage, + session_id=mixconfig.config.name()) + del mixconfig + for parent in metaconfig.config.parents(): + parent.config.pop(metaconfig.config.name()) + delete_session(storage=self.save_storage, + session_id=metaconfig.config.name()) + return children @register('v1.servermodel.updated') async def servermodel_updated(self, risotto_context: Context, - servermodels) -> None: - for servermodel in servermodels: - servermodelid = servermodel['servermodelid'] - servermodelname = servermodel['servermodelname'] - servermodelparentsid = servermodel.get('servermodelparentsid') - log.info_msg(risotto_context, - None, - f'Reload servermodel {servermodelname} ({servermodelid})') - # unlink cache to force download new aggregated file - cache_file = join(ROOT_CACHE_DIR, str(servermodelid)+".xml") - if isfile(cache_file): - unlink(cache_file) + servermodelid: int, + servermodelname: str, + servermodelparentsid: List[int]) -> None: + log.info_msg(risotto_context, + None, + f'Reload servermodel {servermodelname} ({servermodelid})') + # unlink cache to force download new aggregated file + cache_file = join(ROOT_CACHE_DIR, str(servermodelid)+".xml") + if isfile(cache_file): + unlink(cache_file) - # get current servermodel - old_servermodel = self.servermodel[servermodelid] + # store all informations + if servermodelid in self.servermodel: + old_values = self.servermodel[servermodelid].value.exportation() + old_permissives = self.servermodel[servermodelid].permissive.exportation() + old_properties = self.servermodel[servermodelid].property.exportation() + children = self.servermodel_delete(servermodelid) + else: + old_values = None - # create new one - await self.load_servermodel(servermodelid, servermodelname) + # create new one + await self.load_and_link_servermodel(risotto_context, + servermodelid, + servermodelname, + servermodelparentsid) - # migrate all informations - self.servermodel[servermodelid].value.importation(old_servermodel.value.exportation()) - self.servermodel[servermodelid].permissive.importation(old_servermodel.permissive.exportation()) - self.servermodel[servermodelid].property.importation(old_servermodel.property.exportation()) - - # remove link to legacy - if servermodelparentsid: - for servermodelparentid in servermodelparentsid: - mix = self.servermodel[servermodelparentid].config.get('m_v_' + str(servermodelparentid)) - try: - mix.config.pop(old_servermodel.config.name()) - except: - # if mix config is reloaded too - pass - # add new link - self.servermodel_legacy(servermodelname, servermodelid, servermodelparentid) - - # reload servers or servermodels in servermodel - for subconfig in old_servermodel.config.list(): - if not isinstance(subconfig, MixConfig): - # a server - name = subconfig.config.name() - if name.startswith('str_'): - continue - server_id = subconfig.information.get('server_id') - server_name = subconfig.information.get('server_name') - try: - old_servermodel.config.pop(name) - old_servermodel.config.pop(f'std_{server_id}') - except: - pass - del self.server[server_id] - self.load_server(risotto_context, - server_id, - server_name, - servermodelid) - else: - # a servermodel - for subsubconfig in subconfig.config.list(): - name = subsubconfig.config.name() - try: - subconfig.config.pop(name) - except: - pass - self.servermodel_legacy(subsubconfig.information.get('servermodel_name'), - subsubconfig.information.get('servermodel_id'), - servermodelid) + # migrates informations + if old_values is not None: + self.servermodel[servermodelid].value.importation(old_values) + self.servermodel[servermodelid].permissive.importation(old_permissives) + self.servermodel[servermodelid].property.importation(old_properties) + for child in children: + self.servermodel_legacy(risotto_context, + child.information.get('servermodel_name'), + child.information.get('servermodel_id'), + servermodelid) @register('v1.config.configuration.server.get', None) async def get_configuration(self, server_id: int, - deploy: bool) -> bytes: + deployed: bool) -> bytes: if server_id not in self.server: msg = _(f'cannot find server with id {server_id}') log.error_msg(risotto_context, @@ -384,16 +393,16 @@ class Risotto(Controller): msg) raise CallError(msg) - if deploy: + if deployed: server = self.server[server_id]['server'] else: server = self.server[server_id]['server_to_deploy'] server.property.read_only() try: - dico = server.value.dict(fullpath=True) + configuration = server.value.dict(fullpath=True) except: - if deploy: + if deployed: msg = _(f'No configuration available for server {server_id}') else: msg = _(f'No undeployed configuration available for server {server_id}') @@ -401,8 +410,9 @@ class Risotto(Controller): None, msg) raise CallError(msg) - return dumps(dico).encode() - + return {'server_id': server_id, + 'deployed': deployed, + 'configuration': configuration} @register('v1.config.configuration.server.deploy', 'v1.config.configuration.server.updated') async def deploy_configuration(self, @@ -428,4 +438,4 @@ class Risotto(Controller): config.property.importation(config_std.property.exportation()) return {'server_id': server_id, - 'deploy': True} + 'deployed': True} diff --git a/src/risotto/services/session/session.py b/src/risotto/services/session/session.py index 8383d95..7b24f4d 100644 --- a/src/risotto/services/session/session.py +++ b/src/risotto/services/session/session.py @@ -1,7 +1,6 @@ from os import urandom # , unlink from binascii import hexlify from traceback import print_exc -from json import dumps from typing import Dict, List, Optional, Any from tiramisu import Storage @@ -28,6 +27,7 @@ class Risotto(Controller): return storage_servermodel def get_session(self, + risotto_context: Context, session_id: str, type: str) -> Dict: """ Get session information from storage @@ -36,7 +36,8 @@ class Risotto(Controller): storage = storage_server else: storage = storage_servermodel - return storage.get_session(session_id) + return storage.get_session(session_id, + risotto_context.username) def get_session_informations(self, risotto_context: Context, @@ -44,9 +45,9 @@ class Risotto(Controller): type: str) -> Dict: """ format session with a session ID name """ - session = self.get_session(session_id, - type, - risotto_context.username) + session = self.get_session(risotto_context, + session_id, + type) return self.format_session(session_id, session) @@ -104,16 +105,16 @@ class Risotto(Controller): self.modify_storage) # return session's information - return self.get_session_informations(session_id, + return self.get_session_informations(risotto_context, + session_id, type) @register(['v1.session.server.list', 'v1.session.servermodel.list'], None) async def list_session_server(self, risotto_context: Context) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] - storage = self.get_storage(type, - risotto_context.username) - return [self.format_session(session_id, session) or session_id, session in storage.get_sessions().items()] + storage = self.get_storage(type) + return [self.format_session(session_id, session) for session_id, session in storage.get_sessions().items()] @register(['v1.session.server.filter', 'v1.session.servermodel.filter'], None) @@ -127,7 +128,7 @@ class Risotto(Controller): storage = self.get_storage(type) # to validate the session right storage.get_session(session_id, - username) + risotto_context.username) if namespace is not None: storage.set_namespace(session_id, namespace) @@ -139,7 +140,8 @@ class Risotto(Controller): if debug is not None: storage.set_config_debug(session_id, debug) - return self.get_session_informations(session_id, + return self.get_session_informations(risotto_context, + session_id, type) @register(['v1.session.server.configure', 'v1.session.servermodel.configure'], None) @@ -152,25 +154,25 @@ class Risotto(Controller): value: Any, value_multi: Optional[List]) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] - session = self.get_session(session_id, - type, - risotto_context.username) + session = self.get_session(risotto_context, + session_id, + type) # if multi and not follower the value is in fact in value_multi - option = session['config'].option(name).option + option = session['option'].option(name).option if option.ismulti() and not option.isfollower(): value = value_multi - try: - update = {'name': name, - 'action': action, - 'value': value} - if index is not None: - update['index'] = index - updates = {'updates': [update]} - session['option'].updates(updates) - except Exception as err: - if DEBUG: - print_exc() - raise CallError(str(err)) + namespace = session['namespace'] + update = {'name': f'{namespace}.{name}', + 'action': action, + 'value': value} + if index is not None: + update['index'] = index + updates = {'updates': [update]} + ret = session['option'].updates(updates) + if update['name'] in ret: + for val in ret[update['name']][index]: + if isinstance(val, ValueError): + raise CallError(val) ret = {'session_id': session_id, 'name': name} if index is not None: @@ -182,9 +184,9 @@ class Risotto(Controller): risotto_context: Context, session_id: str) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] - session = self.get_session(session_id, - type, - risotto_context.username) + session = self.get_session(risotto_context, + session_id, + type) try: session['config'].forcepermissive.option(session['namespace']).value.dict() except Exception as err: @@ -205,13 +207,17 @@ class Risotto(Controller): @register(['v1.session.server.get', 'v1.session.servermodel.get'], None) async def get_session_server(self, risotto_context: Context, - session_id: str) -> Dict: + session_id: str, + name: Optional[str]) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] - session = self.get_session(session_id, - type, - risotto_context.username) + session = self.get_session(risotto_context, + session_id, + type) info = self.format_session(session_id, session) - info['content'] = dumps(session['option'].value.dict(fullpath=True)) + if name is not None: + info['content'] = {name: session['option'].option(name).value.get()} + else: + info['content'] = session['option'].value.dict() return info @register(['v1.session.server.stop', 'v1.session.servermodel.stop'], None) diff --git a/src/risotto/services/session/storage.py b/src/risotto/services/session/storage.py index a320209..bdb6b0b 100644 --- a/src/risotto/services/session/storage.py +++ b/src/risotto/services/session/storage.py @@ -66,7 +66,7 @@ class Storage(object): mode: str): """ Define which edition mode to select """ - config = self.session[id]['config'] + config = self.sessions[id]['config'] for mode_level in modes.values(): if modes[mode] < mode_level: config.property.add(mode_level.name) @@ -77,7 +77,7 @@ class Storage(object): def set_config_debug(self, id_, is_debug): """ Enable/Disable debug mode """ - config = self.session[id_]['config'] + config = self.sessions[id_]['config'] if is_debug: config.property.pop('hidden') else: @@ -94,13 +94,14 @@ class Storage(object): return self.sessions; def get_session(self, - id: int, + session_id: int, username: str) -> Dict: - if id not in self.sessions: + if session_id not in self.sessions: raise Exception(f'the session {id} not exists') - if username != storage.get_session(session_id)['username']: + session = self.sessions[session_id] + if username != session['username']: raise NotAllowedError() - return self.sessions[id] + return session def del_session(self, id: int): diff --git a/tests/test_config.py b/tests/test_config.py index 6f818b1..f788010 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -13,7 +13,6 @@ def setup_module(module): validate=False) config_module = dispatcher.get_service('config') config_module.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR, name='test') - config_module.save_persistent = False dispatcher.set_module('server', import_module(f'.server', 'fake_services')) dispatcher.set_module('servermodel', import_module(f'.servermodel', 'fake_services')) diff --git a/tests/test_session.py b/tests/test_session.py new file mode 100644 index 0000000..64c403a --- /dev/null +++ b/tests/test_session.py @@ -0,0 +1,501 @@ +from importlib import import_module +import pytest +from tiramisu import Storage +from risotto.context import Context +from risotto.services import load_services +from risotto.dispatcher import dispatcher +from risotto.config import DATABASE_DIR +from risotto.services.session.storage import storage_server, storage_servermodel + + +def get_fake_context(module_name): + risotto_context = Context() + risotto_context.username = 'test' + risotto_context.paths.append(f'{module_name}.on_join') + risotto_context.type = None + return risotto_context + + +def setup_module(module): + load_services(['config', 'session'], + validate=False) + config_module = dispatcher.get_service('config') + config_module.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR, name='test') + dispatcher.set_module('server', import_module(f'.server', 'fake_services')) + dispatcher.set_module('servermodel', import_module(f'.servermodel', 'fake_services')) + + +def teardown_function(function): + config_module = dispatcher.get_service('session') + storage_server.sessions = {} + storage_servermodel.sessions = {} + + +@pytest.mark.asyncio +async def test_server_start(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + + +@pytest.mark.asyncio +async def test_server_list(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + assert not await dispatcher.call('v1', + 'session.server.list', + fake_context) + await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + assert await dispatcher.call('v1', + 'session.server.list', + fake_context) + + +@pytest.mark.asyncio +async def test_server_filter_namespace(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + session_id = session['session_id'] + namespace = 'containers' + await dispatcher.call('v1', + 'session.server.filter', + fake_context, + session_id=session_id, + namespace=namespace) + list_result = await dispatcher.call('v1', + 'session.server.list', + fake_context) + assert list_result[0]['namespace'] == namespace + + +@pytest.mark.asyncio +async def test_server_filter_mode(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + session_id = session['session_id'] + assert session['mode'] == 'normal' + mode = 'expert' + await dispatcher.call('v1', + 'session.server.filter', + fake_context, + session_id=session_id, + mode=mode) + list_result = await dispatcher.call('v1', + 'session.server.list', + fake_context) + assert list_result[0]['mode'] == mode + + +@pytest.mark.asyncio +async def test_server_filter_debug(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + session_id = session['session_id'] + assert session['debug'] == False + debug = True + await dispatcher.call('v1', + 'session.server.filter', + fake_context, + session_id=session_id, + debug=debug) + list_result = await dispatcher.call('v1', + 'session.server.list', + fake_context) + assert list_result[0]['debug'] == debug +#FIXME + + +@pytest.mark.asyncio +async def test_server_filter_get(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + session_id = session['session_id'] + values = await dispatcher.call('v1', + 'session.server.get', + fake_context, + session_id=session_id) + assert values == {'content': {"general.mode_conteneur_actif": "non", + "general.master.master": [], + "general.master.slave1": [], + "general.master.slave2": []}, + 'debug': False, + 'id': 3, + 'mode': 'normal', + 'namespace': 'creole', + 'session_id': session_id, + 'timestamp': values['timestamp'], + 'username': 'test'} + + +@pytest.mark.asyncio +async def test_server_filter_get_one_value(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + session_id = session['session_id'] + values = await dispatcher.call('v1', + 'session.server.get', + fake_context, + session_id=session_id, + name="general.mode_conteneur_actif") + assert values == {'content': {"general.mode_conteneur_actif": "non"}, + 'debug': False, + 'id': 3, + 'mode': 'normal', + 'namespace': 'creole', + 'session_id': session_id, + 'timestamp': values['timestamp'], + 'username': 'test'} + + +@pytest.mark.asyncio +async def test_server_filter_configure(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + session_id = session['session_id'] + await dispatcher.call('v1', + 'session.server.configure', + fake_context, + session_id=session_id, + action='modify', + name='general.mode_conteneur_actif', + value='oui') + list_result = await dispatcher.call('v1', + 'session.server.list', + fake_context) + values = await dispatcher.call('v1', + 'session.server.get', + fake_context, + session_id=session_id, + name="general.mode_conteneur_actif") + assert values == {'content': {"general.mode_conteneur_actif": "oui"}, + 'debug': False, + 'id': 3, + 'mode': 'normal', + 'namespace': 'creole', + 'session_id': session_id, + 'timestamp': values['timestamp'], + 'username': 'test'} + + +@pytest.mark.asyncio +async def test_server_filter_validate(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + session_id = session['session_id'] + await dispatcher.call('v1', + 'session.server.validate', + fake_context, + session_id=session_id) + + +@pytest.mark.asyncio +async def test_server_stop(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.server: + await config_module.on_join(fake_context) + assert not await dispatcher.call('v1', + 'session.server.list', + fake_context) + start = await dispatcher.call('v1', + 'session.server.start', + fake_context, + id=3) + session_id = start['session_id'] + assert await dispatcher.call('v1', + 'session.server.list', + fake_context) + await dispatcher.call('v1', + 'session.server.stop', + fake_context, + session_id=session_id) + assert not await dispatcher.call('v1', + 'session.server.list', + fake_context) + + +# servermodel +@pytest.mark.asyncio +async def test_servermodel_start(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + + +@pytest.mark.asyncio +async def test_servermodel_list(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + assert not await dispatcher.call('v1', + 'session.servermodel.list', + fake_context) + await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + assert await dispatcher.call('v1', + 'session.servermodel.list', + fake_context) + + +@pytest.mark.asyncio +async def test_servermodel_filter_namespace(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + session_id = session['session_id'] + namespace = 'containers' + await dispatcher.call('v1', + 'session.servermodel.filter', + fake_context, + session_id=session_id, + namespace=namespace) + list_result = await dispatcher.call('v1', + 'session.servermodel.list', + fake_context) + assert list_result[0]['namespace'] == namespace + + +@pytest.mark.asyncio +async def test_servermodel_filter_mode(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + session_id = session['session_id'] + assert session['mode'] == 'normal' + mode = 'expert' + await dispatcher.call('v1', + 'session.servermodel.filter', + fake_context, + session_id=session_id, + mode=mode) + list_result = await dispatcher.call('v1', + 'session.servermodel.list', + fake_context) + assert list_result[0]['mode'] == mode + + +@pytest.mark.asyncio +async def test_servermodel_filter_debug(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + session_id = session['session_id'] + assert session['debug'] == False + debug = True + await dispatcher.call('v1', + 'session.servermodel.filter', + fake_context, + session_id=session_id, + debug=debug) + list_result = await dispatcher.call('v1', + 'session.servermodel.list', + fake_context) + assert list_result[0]['debug'] == debug + + +@pytest.mark.asyncio +async def test_servermodel_filter_get(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + session_id = session['session_id'] + values = await dispatcher.call('v1', + 'session.servermodel.get', + fake_context, + session_id=session_id) + assert values == {'content': {"general.mode_conteneur_actif": "non", + "general.master.master": [], + "general.master.slave1": [], + "general.master.slave2": []}, + 'debug': False, + 'id': 1, + 'mode': 'normal', + 'namespace': 'creole', + 'session_id': session_id, + 'timestamp': values['timestamp'], + 'username': 'test'} + + +@pytest.mark.asyncio +async def test_servermodel_filter_get_one_value(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + session_id = session['session_id'] + values = await dispatcher.call('v1', + 'session.servermodel.get', + fake_context, + session_id=session_id, + name="general.mode_conteneur_actif") + assert values == {'content': {"general.mode_conteneur_actif": "non"}, + 'debug': False, + 'id': 1, + 'mode': 'normal', + 'namespace': 'creole', + 'session_id': session_id, + 'timestamp': values['timestamp'], + 'username': 'test'} + + +@pytest.mark.asyncio +async def test_servermodel_filter_configure(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + session_id = session['session_id'] + await dispatcher.call('v1', + 'session.servermodel.configure', + fake_context, + session_id=session_id, + action='modify', + name='general.mode_conteneur_actif', + value='oui') + list_result = await dispatcher.call('v1', + 'session.servermodel.list', + fake_context) + values = await dispatcher.call('v1', + 'session.servermodel.get', + fake_context, + session_id=session_id, + name="general.mode_conteneur_actif") + assert values == {'content': {"general.mode_conteneur_actif": "oui"}, + 'debug': False, + 'id': 1, + 'mode': 'normal', + 'namespace': 'creole', + 'session_id': session_id, + 'timestamp': values['timestamp'], + 'username': 'test'} + + +@pytest.mark.asyncio +async def test_servermodel_filter_validate(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + session = await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + session_id = session['session_id'] + await dispatcher.call('v1', + 'session.servermodel.validate', + fake_context, + session_id=session_id) + + +@pytest.mark.asyncio +async def test_servermodel_stop(): + fake_context = get_fake_context('session') + config_module = dispatcher.get_service('config') + if not config_module.servermodel: + await config_module.on_join(fake_context) + assert not await dispatcher.call('v1', + 'session.servermodel.list', + fake_context) + start = await dispatcher.call('v1', + 'session.servermodel.start', + fake_context, + id=1) + session_id = start['session_id'] + assert await dispatcher.call('v1', + 'session.servermodel.list', + fake_context) + await dispatcher.call('v1', + 'session.servermodel.stop', + fake_context, + session_id=session_id) + assert not await dispatcher.call('v1', + 'session.servermodel.list', + fake_context)