316 lines
14 KiB
Python
316 lines
14 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 ...context import Context
|
|
from ...utils import _
|
|
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,
|
|
test):
|
|
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')
|
|
async def start_session_server(self,
|
|
risotto_context: Context,
|
|
server_name: str) -> Dict:
|
|
""" start a new config session for a server
|
|
"""
|
|
config_module = dispatcher.get_service('config')
|
|
server = await self.call('v1.server.describe',
|
|
risotto_context,
|
|
server_name=server_name)
|
|
if not server or server['server_id'] not in config_module.server:
|
|
raise Exception(_(f'cannot find server with name {server_name}'))
|
|
id = server['server_id']
|
|
config = config_module.server[id]['server_to_deploy']
|
|
|
|
storage = self.get_storage('server')
|
|
|
|
# check if a session already exists
|
|
sessions = storage.get_sessions()
|
|
for sess_id, session in sessions.items():
|
|
if session['id'] == id:
|
|
if session['username'] == risotto_context.username:
|
|
# same user so returns it
|
|
return self.format_session(sess_id,
|
|
session)
|
|
else:
|
|
raise Exception(_(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
|
|
await 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,
|
|
'server')
|
|
|
|
@register('v1.session.servermodel.start')
|
|
async def start_session_servermodel(self,
|
|
risotto_context: Context,
|
|
servermodel_name: str,
|
|
source_name: str,
|
|
release_distribution: str) -> Dict:
|
|
""" start a new config session for a server or a servermodel
|
|
"""
|
|
config_module = dispatcher.get_service('config')
|
|
servermodel = await self.call('v1.servermodel.describe',
|
|
risotto_context,
|
|
servermodel_name=servermodel_name,
|
|
source_name=source_name,
|
|
release_distribution=release_distribution)
|
|
if not servermodel or servermodel['servermodel_id'] not in config_module.servermodel:
|
|
raise Exception(_(f'cannot find servermodel with name {servermodel_name}'))
|
|
id = servermodel['servermodel_id']
|
|
config = config_module.servermodel[id]
|
|
|
|
storage = self.get_storage('servermodel')
|
|
|
|
# check if a session already exists
|
|
sessions = storage.get_sessions()
|
|
for sess_id, session in sessions.items():
|
|
if session['id'] == id:
|
|
if session['username'] == risotto_context.username:
|
|
# same user so returns it
|
|
return self.format_session(sess_id,
|
|
session)
|
|
else:
|
|
raise Exception(_(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
|
|
await 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,
|
|
'servermodel')
|
|
|
|
@register(['v1.session.server.list', 'v1.session.servermodel.list'])
|
|
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'])
|
|
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 Exception(f'unknown mode {mode}')
|
|
await storage.set_config_mode(session_id,
|
|
mode)
|
|
if debug is not None:
|
|
await 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'])
|
|
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
|
|
# FIXME option = session['option'].option(name).option
|
|
option = session['config'].option(name).option
|
|
if await option.ismulti() and not await option.isfollower():
|
|
value = value_multi
|
|
#FIXME namespace = session['namespace']
|
|
#FIXME update = {'name': f'{namespace}.{name}',
|
|
update = {'name': name,
|
|
'action': action,
|
|
'value': value}
|
|
if index is not None:
|
|
update['index'] = index
|
|
updates = {'updates': [update]}
|
|
ret = await session['option'].updates(updates)
|
|
if update['name'] in ret:
|
|
for val in ret[update['name']][index]:
|
|
if isinstance(val, ValueError):
|
|
raise Exception(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'])
|
|
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:
|
|
await session['config'].forcepermissive.option(session['namespace']).value.dict()
|
|
except Exception as err:
|
|
raise Exception(str(err))
|
|
if type == 'server':
|
|
config = session['config']
|
|
await config.property.read_only()
|
|
mandatories = list(await config.value.mandatory())
|
|
await config.property.read_write()
|
|
if mandatories:
|
|
# FIXME mandatories = [mandatory.split('.', 1)[1] for mandatory in mandatories]
|
|
if len(mandatories) == 1:
|
|
mandatories = mandatories[0]
|
|
msg = _(f'the parameter "--{mandatories}" is mandatory')
|
|
else:
|
|
mandatories = '", "--'.join(mandatories)
|
|
msg = _(f'parameters "--{mandatories}" are mandatories')
|
|
raise Exception(msg)
|
|
return self.format_session(session_id,
|
|
session)
|
|
|
|
@register(['v1.session.server.get', 'v1.session.servermodel.get'])
|
|
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:
|
|
content = {name: await session['config'].option(name).value.get()}
|
|
else:
|
|
content = await session['option'].value.dict(fullpath=True,
|
|
leader_to_list=True)
|
|
info['content'] = content
|
|
return info
|
|
|
|
@register(['v1.session.server.stop', 'v1.session.servermodel.stop'])
|
|
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_to_deploy']
|
|
else:
|
|
config = config_module.servermodel[id_]
|
|
if save:
|
|
modif_config = session['config']
|
|
await config.value.importation(await modif_config.value.exportation())
|
|
await config.permissive.importation(await 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 await 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 await session['option'].dict(remotable='all')
|