risotto/src/risotto/services/session/session.py

262 lines
11 KiB
Python

from os import urandom # , unlink
from binascii import hexlify
from traceback import print_exc
from typing import Dict, List, Optional, Any
from tiramisu import Storage
from ...http import register as register_http
from ...config import DEBUG
from ...context import Context
from ...utils import _
from ...error import CallError
from .storage import storage_server, storage_servermodel
from ...controller import Controller
from ...register import register
from ...dispatcher import dispatcher
class Risotto(Controller):
def __init__(self):
self.modify_storage = Storage(engine='dictionary')
def get_storage(self,
type: str):
if type == 'server':
return storage_server
return storage_servermodel
def get_session(self,
risotto_context: Context,
session_id: str,
type: str) -> Dict:
""" Get session information from storage
"""
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
return storage.get_session(session_id,
risotto_context.username)
def get_session_informations(self,
risotto_context: Context,
session_id: str,
type: str) -> Dict:
""" format session with a session ID name
"""
session = self.get_session(risotto_context,
session_id,
type)
return self.format_session(session_id,
session)
def format_session(self,
session_name: str,
session: Dict) -> Dict:
""" format session
"""
return {'session_id': session_name,
'id': session['id'],
'username': session['username'],
'timestamp': session['timestamp'],
'namespace': session['namespace'],
'mode': session['mode'],
'debug': session['debug']}
@register(['v1.session.server.start', 'v1.session.servermodel.start'], None)
async def start_session(self,
risotto_context: Context,
id: int) -> Dict:
""" start a new config session for a server or a servermodel
"""
type = risotto_context.message.rsplit('.', 2)[-2]
config_module = dispatcher.get_service('config')
if type == 'server':
if id not in config_module.server:
raise Exception(_(f'cannot find {type} with id {id}'))
config = config_module.server[id]['server']
else:
if id not in config_module.servermodel:
raise Exception(_(f'cannot find {type} with id {id}'))
config = config_module.servermodel[id]
storage = self.get_storage(type)
# check if a session already exists
sessions = storage.get_sessions()
for session in sessions.values():
if sess['id'] == id:
if sess['username'] == risotto_context.username:
# same user so returns it
return self.format_session(session['session_id'], session)
else:
raise CallError(_(f'{username} already edits this configuration'))
# create a new session
while True:
session_id = 'z' + hexlify(urandom(23)).decode()
if not session_id in sessions:
break
storage.add_session(session_id,
config,
id,
risotto_context.username,
self.modify_storage)
# return session's information
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)
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)
async def filter_session(self,
risotto_context: Context,
session_id: str,
namespace: str,
mode: str,
debug: Optional[bool]):
type = risotto_context.message.rsplit('.', 2)[-2]
storage = self.get_storage(type)
# to validate the session right
storage.get_session(session_id,
risotto_context.username)
if namespace is not None:
storage.set_namespace(session_id,
namespace)
if mode is not None:
if mode not in ('basic', 'normal', 'expert'):
raise CallError(f'unknown mode {mode}')
storage.set_config_mode(session_id,
mode)
if debug is not None:
storage.set_config_debug(session_id,
debug)
return self.get_session_informations(risotto_context,
session_id,
type)
@register(['v1.session.server.configure', 'v1.session.servermodel.configure'], None)
async def configure_session(self,
risotto_context: Context,
session_id: str,
action: str,
name: str,
index: int,
value: Any,
value_multi: Optional[List]) -> Dict:
type = risotto_context.message.rsplit('.', 2)[-2]
session = self.get_session(risotto_context,
session_id,
type)
# if multi and not follower the value is in fact in value_multi
option = session['option'].option(name).option
if option.ismulti() and not option.isfollower():
value = value_multi
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:
ret['index'] = index
return ret
@register(['v1.session.server.validate', 'v1.session.servermodel.validate'], None)
async def validate_session(self,
risotto_context: Context,
session_id: str) -> Dict:
type = risotto_context.message.rsplit('.', 2)[-2]
session = self.get_session(risotto_context,
session_id,
type)
try:
session['config'].forcepermissive.option(session['namespace']).value.dict()
except Exception as err:
raise CallError(str(err))
if type == 'server':
mandatories = list(session['config'].forcepermissive.value.mandatory())
if mandatories:
if len(mandatories) == 1:
mandatories = mandatories[0]
msg = _('the parameter "--{mandatories}" is mandatory')
else:
mandatories = '", "--'.join(mandatories)
msg = _('parameters "{mandatories}" are mandatories')
raise CallError(msg)
return self.format_session(session_id,
session)
@register(['v1.session.server.get', 'v1.session.servermodel.get'], None)
async def get_session_server(self,
risotto_context: Context,
session_id: str,
name: Optional[str]) -> Dict:
type = risotto_context.message.rsplit('.', 2)[-2]
session = self.get_session(risotto_context,
session_id,
type)
info = self.format_session(session_id, session)
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)
async def stop_session(self,
risotto_context: Context,
session_id: str,
save: bool) -> Dict:
type = risotto_context.message.rsplit('.', 2)[-2]
storage = self.get_storage(type)
session = storage.get_session(session_id,
risotto_context.username)
id_ = session['id']
config_module = dispatcher.get_service('config')
if type == 'server':
config = config_module.server[id_]['server']
else:
config = config_module.servermodel[id_]
if save:
modif_config = session['config']
config.value.importation(modif_config.value.exportation())
config.permissive.importation(modif_config.permissive.exportation())
storage.del_session(session_id)
return self.format_session(session_id, session)
@register_http('v1', '/config/server/{session_id}')
async def get_server_api(self,
request,
risotto_context: Context,
session_id: str) -> Dict:
session = storage_server.get_session(session_id,
risotto_context.username)
return session['option'].dict(remotable='all')
@register_http('v1', '/config/servermodel/{session_id}')
async def get_servermodel_api(self,
request,
risotto_context: Context,
session_id: str) -> Dict:
session = storage_servermodel.get_session(session_id,
risotto_context.username)
return session['option'].dict(remotable='all')