config.session => session

This commit is contained in:
Emmanuel Garette 2019-12-03 08:34:11 +01:00
parent 939f93253e
commit 03937baf51
23 changed files with 472 additions and 501 deletions

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.server.configure uri: session.server.configure
description: | description: |
Configure le server. Configure le server.
sampleuse: |
zephir-client config.session.server.configure -s 2
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -44,5 +41,5 @@ parameters:
default: [] default: []
response: response:
type: ConfigStatus type: SessionStatus
description: Description de la session. description: Description de la session.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.server.filter uri: session.server.filter
description: | description: |
Filter la configuration a éditer. Filter la configuration a éditer.
sampleuse: |
zephir-client config.session.server.filter -s 2
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -36,5 +33,5 @@ parameters:
default: null default: null
response: response:
type: ConfigSession type: Session
description: Description de la session. description: Description de la session.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.server.get uri: session.server.get
description: | description: |
Configure le server. Configure le server.
sampleuse: |
zephir-client config.session.server.get -s 2
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -21,5 +18,5 @@ parameters:
description: Identifiant de la configuration. description: Identifiant de la configuration.
response: response:
type: ConfigSession type: Session
description: Description de la session. description: Description de la session.

View File

@ -1,19 +1,16 @@
--- ---
uri: config.session.server.list uri: session.server.list
description: | description: |
Liste les sessions de configuration des serveurs. Liste les sessions de configuration des serveurs.
sampleuse: |
zephir-client config.session.server.list
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
response: response:
type: '[]ConfigSession' type: '[]Session'
description: | description: |
Liste des sessions. Liste des sessions.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.server.start uri: session.server.start
description: | description: |
Démarre une session de configuration pour un serveur. Démarre une session de configuration pour un serveur.
sampleuse: |
zephir-client config.session.server.start -c 2
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
id: id:
@ -22,5 +19,5 @@ parameters:
Identifiant de la configuration. Identifiant de la configuration.
response: response:
type: ConfigSession type: Session
description: Description de la session. description: Description de la session.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.server.stop uri: session.server.stop
description: | description: |
Termine une session de configuration d'un serveur. Termine une session de configuration d'un serveur.
sampleuse: |
zephir-client config.session.server.stop -s xxxxx
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -26,5 +23,5 @@ parameters:
default: false default: false
response: response:
type: ConfigSession type: Session
description: Description de la session. description: Description de la session.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.server.validate uri: session.server.validate
description: | description: |
Valider la configuration d'un serveur. Valider la configuration d'un serveur.
sampleuse: |
zephir-client config.session.server.validate -s xxxxx
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -21,6 +18,6 @@ parameters:
description: Identifiant de la session. description: Identifiant de la session.
response: response:
type: ConfigConfigurationStatus type: SessionConfigurationStatus
description: Statut de la configuration. description: Statut de la configuration.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.servermodel.configure uri: session.servermodel.configure
description: | description: |
Configure le servermodel. Configure le servermodel.
sampleuse: |
zephir-client config.session.servermodel.configure -s 2
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -44,5 +41,5 @@ parameters:
default: [] default: []
response: response:
type: ConfigStatus type: SessionStatus
description: Description de la session. description: Description de la session.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.servermodel.filter uri: session.servermodel.filter
description: | description: |
Filter la configuration a éditer. Filter la configuration a éditer.
sampleuse: |
zephir-client config.session.servermodel.filter -s 2
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -36,5 +33,5 @@ parameters:
default: null default: null
response: response:
type: ConfigSession type: Session
description: Description de la session. description: Description de la session.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.servermodel.get uri: session.servermodel.get
description: | description: |
Configure le servermodel. Configure le servermodel.
sampleuse: |
zephir-client config.session.servermodel.get -s 2
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -21,5 +18,5 @@ parameters:
description: Identifiant de la configuration. description: Identifiant de la configuration.
response: response:
type: ConfigSession type: Session
description: Description de la session. description: Description de la session.

View File

@ -1,18 +1,15 @@
uri: config.session.servermodel.list uri: session.servermodel.list
description: | description: |
Liste les sessions de configuration des modèles de serveur. Liste les sessions de configuration des modèles de serveur.
sampleuse: |
zephir-client config.session.servermodel.list
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
response: response:
type: '[]ConfigSession' type: '[]Session'
description: | description: |
Liste des sessions. Liste des sessions.

View File

@ -1,16 +1,13 @@
uri: config.session.servermodel.start uri: session.servermodel.start
description: | description: |
Démarre une session de configuration pour un modèle de serveur. Démarre une session de configuration pour un modèle de serveur.
sampleuse: |
zephir-client config.session.servermodel.start -c 2
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
id: id:
@ -21,5 +18,5 @@ parameters:
Identifiant de la configuration. Identifiant de la configuration.
response: response:
type: ConfigSession type: Session
description: Description de la session. description: Description de la session.

View File

@ -1,16 +1,13 @@
uri: config.session.servermodel.stop uri: session.servermodel.stop
description: | description: |
Termine une session de configuration d'un modèle de serveur. Termine une session de configuration d'un modèle de serveur.
sampleuse: |
zephir-client config.session.servermodel.stop -s xxxxx
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -25,5 +22,5 @@ parameters:
default: false default: false
response: response:
type: ConfigSession type: Session
description: Description de la session. description: Description de la session.

View File

@ -1,17 +1,14 @@
--- ---
uri: config.session.servermodel.validate uri: session.servermodel.validate
description: | description: |
Valider la configuration d'un modèle serveur. Valider la configuration d'un modèle serveur.
sampleuse: |
zephir-client config.session.servermodel.validate -s xxxxx
pattern: rpc pattern: rpc
public: true public: true
domain: config-domain domain: session-domain
parameters: parameters:
session_id: session_id:
@ -21,6 +18,6 @@ parameters:
description: Identifiant de la session. description: Identifiant de la session.
response: response:
type: ConfigConfigurationStatus type: SessionConfigurationStatus
description: Statut de la configuration. description: Statut de la configuration.

View File

@ -1,5 +1,5 @@
--- ---
title: ConfigConfigurationStatus title: SessionConfigurationStatus
type: object type: object
description: Statut de la configuration. description: Statut de la configuration.
properties: properties:

View File

@ -1,5 +1,5 @@
--- ---
title: ConfigStatus title: SessionStatus
type: object type: object
description: Status de la modification de la configuration. description: Status de la modification de la configuration.
properties: properties:

View File

@ -1,5 +1,5 @@
--- ---
title: ConfigSession title: Session
type: object type: object
description: Description de la session. description: Description de la session.
properties: properties:

View File

@ -1,24 +1,20 @@
from lxml.etree import parse from lxml.etree import parse
from io import BytesIO from io import BytesIO
from os import urandom # , unlink
from os.path import isdir, isfile, join from os.path import isdir, isfile, join
from binascii import hexlify
from traceback import print_exc from traceback import print_exc
from json import dumps from json import dumps
from typing import Dict, List, Optional, Any from typing import Dict
from tiramisu import Storage, list_sessions, delete_session, Config, MetaConfig, MixConfig from tiramisu import Storage, delete_session, MetaConfig, MixConfig
from rougail import load as rougail_load from rougail import load as rougail_load
from ...controller import Controller from ...controller import Controller
from ...register import register from ...register import register
from ...http import register as register_http
from ...config import ROOT_CACHE_DIR, DATABASE_DIR, DEBUG, ROUGAIL_DTD_PATH from ...config import ROOT_CACHE_DIR, DATABASE_DIR, DEBUG, ROUGAIL_DTD_PATH
from ...context import Context from ...context import Context
from ...utils import _ from ...utils import _
from ...error import CallError, NotAllowedError, RegistrationError from ...error import CallError, NotAllowedError, RegistrationError
from ...logger import log from ...logger import log
from .storage import storage_server, storage_servermodel
if not isdir(ROOT_CACHE_DIR): if not isdir(ROOT_CACHE_DIR):
@ -31,23 +27,8 @@ class Risotto(Controller):
def __init__(self) -> None: def __init__(self) -> None:
self.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR) self.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR)
self.modify_storage = Storage(engine='dictionary')
super().__init__() super().__init__()
def valid_user(self,
session_id: str,
risotto_context: Context,
type: str) -> None:
""" check if current user is the session owner
"""
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
username = risotto_context.username
if username != storage.get_session(session_id)['username']:
raise NotAllowedError()
async def on_join(self, async def on_join(self,
risotto_context: Context) -> None: risotto_context: Context) -> None:
""" pre-load servermodel and server """ pre-load servermodel and server
@ -449,258 +430,3 @@ class Risotto(Controller):
return {'server_id': server_id, return {'server_id': server_id,
'deploy': True} 'deploy': True}
def get_session(self,
session_id: str,
type: str) -> Dict:
""" Get session information from storage
"""
if type == 'server':
return storage_server.get_session(session_id)
return storage_servermodel.get_session(session_id)
def get_session_informations(self,
session_id: str,
type: str) -> Dict:
""" format session with a session ID name
"""
session = self.get_session(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']}
def list_sessions(self,
type: str) -> List:
ret = []
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
for session in storage.list_sessions():
ret.append(self.format_session(session['session_id'], session))
return ret
def load_dict(self,
session: Dict) -> Dict:
if not session['option']:
session['option'] = session['config'].option(session['namespace'])
return session['option'].dict(remotable='all')
@register(['v1.config.session.server.start', 'v1.config.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]
server_list = getattr(self, type)
if id not in server_list:
raise Exception(_(f'cannot find {type} with id {id}'))
# check if a session already exists, in this case returns it
session_list = self.list_sessions(type)
for sess in session_list:
if sess['id'] == id and sess['username'] == risotto_context.username:
session_id = sess['session_id']
session = self.get_session(session_id, type)
return self.format_session(session_id, session)
# create a new session
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
while True:
session_id = 'z' + hexlify(urandom(23)).decode()
if not storage.has_session(session_id):
break
else:
print('session {} already exists'.format(session_id))
username = risotto_context.username
storage.add_session(session_id,
server_list[id],
id,
username,
self.modify_storage)
return self.get_session_informations(session_id,
type)
@register(['v1.config.session.server.list', 'v1.config.session.servermodel.list'], None)
async def list_session_server(self,
risotto_context: Context):
type = risotto_context.message.rsplit('.', 2)[-2]
return self.list_sessions(type)
@register(['v1.config.session.server.filter', 'v1.config.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]
session = self.get_session(session_id,
type)
if namespace is not None:
session['option'] = None
session['namespace'] = namespace
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
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(session_id,
type)
@register(['v1.config.session.server.configure', 'v1.config.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(session_id,
type)
ret = {'session_id': session_id,
'name': name}
if index is not None:
ret['index'] = index
option = session['config'].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
if not session['option']:
session['option'] = session['config'].option(session['namespace'])
self.load_dict(session)
updates = {'updates': [update]}
session['option'].updates(updates)
ret['status'] = 'ok'
except Exception as err:
if DEBUG:
print_exc()
ret['message'] = str(err)
ret['status'] = 'error'
return ret
@register(['v1.config.session.server.validate', 'v1.config.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(session_id, type)
ret = {}
try:
session['config'].forcepermissive.option(session['namespace']).value.dict()
except Exception as err:
ret['status'] = 'error'
ret['message'] = str(err)
else:
if type == 'server':
mandatories = list(session['config'].forcepermissive.value.mandatory())
if mandatories:
ret['status'] = 'incomplete'
ret['mandatories'] = mandatories
else:
ret['status'] = 'ok'
else:
ret['status'] = 'ok'
return ret
@register(['v1.config.session.server.get', 'v1.config.session.servermodel.get'], None)
async def get_session_(self,
risotto_context: Context,
session_id: str) -> Dict:
type = risotto_context.message.rsplit('.', 2)[-2]
info = self.get_session_informations(session_id,
type)
info['content'] = session_id
session = self.get_session(session_id,
type)
if not session['option']:
session['option'] = session['config'].option(session['namespace'])
info['content'] = dumps(session['option'].value.dict(fullpath=True))
return info
@register(['v1.config.session.server.stop', 'v1.config.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]
self.valid_user(session_id,
risotto_context,
type)
session = self.get_session(session_id,
type)
id_ = session['id']
if type == 'server':
storage = storage_server
if save:
storage.save_values(session_id)
if self.server[id_].option('creole.general.available_probes').value.get() == "oui":
self.publish('v1.config.configuration.server.updated', server_id=id_, deploy=False)
else:
storage = storage_servermodel
if save:
storage.save_values(session_id)
for probe in self.servermodel[id_].config.list():
# FIXME should use config.information.get('server_id')
name = probe.config.name()
if name.startswith('p_'):
server_id = int(name.rsplit('_', 1)[-1])
if self.server[server_id].option('creole.general.available_probes').value.get() == "oui":
self.publish('v1.config.configuration.server.updated', server_id=server_id)
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:
self.valid_user(session_id,
risotto_context,
'server')
session = storage_server.get_session(session_id)
return self.load_dict(session)
@register_http('v1', '/config/servermodel/{session_id}')
async def get_servermodel_api(self,
request,
risotto_context: Context,
session_id: str) -> Dict:
self.valid_user(session_id,
risotto_context,
'servermodel')
session = storage_servermodel.get_session(session_id)
return self.load_dict(session)

View File

@ -1,138 +0,0 @@
import time
from rougail import modes
class StorageError(Exception):
pass
class Storage(object):
__slots__ = ('sessions',)
def __init__(self):
self.sessions = {}
def has_session(self, id_):
return id_ in self.sessions
def config_exists(self, id_):
return self.sessions[id_]['config_exists']
def add_session(self, session_id, orig_config, server_id, username, storage):
for session in self.sessions.values():
if session['id'] == server_id:
raise Storage(_(f'{username} already edits this configuration'))
prefix_id = "{}_".format(session_id)
config_server, orig_config = self.transform_orig_config(orig_config, server_id)
config_id = "{}{}".format(prefix_id, config_server)
meta = orig_config.config.deepcopy(session_id=config_id, storage=storage, metaconfig_prefix=prefix_id)
config = meta
while True:
try:
children = list(config.config.list())
except:
break
if children:
config = children[0]
else:
break
config.property.read_write()
self.set_owner(config, username)
orig_values = config.value.exportation()
config.information.set('orig_values', orig_values)
config_exists = False
for owner in orig_values[3]:
if isinstance(owner, list):
if set(owner) != {'forced'}:
config_exists = True
break
elif owner != 'forced':
config_exists = True
break
self.sessions[session_id] = {'config': config,
# do not delete meta, so keep it!
'meta': meta,
'orig_config': orig_config,
'id': server_id,
'timestamp': int(time.time()),
'username': username,
'option': None,
'namespace': 'creole',
'config_exists': config_exists}
self.set_config_mode(session_id, 'normal')
self.set_config_debug(session_id, False)
def list_sessions(self):
for session_id, session in self.sessions.items():
yield {'session_id': session_id,
'id': session['id'],
'timestamp': session['timestamp'],
'username': session['username'],
'namespace': session['namespace'],
'mode': session['mode'],
'debug': session['debug']}
def del_session(self, id_):
del self.sessions[id_]
def get_session(self, id_):
if id_ not in self.sessions:
raise Exception('please start a session before')
return self.sessions[id_]
def save_values(self, id_):
config = self.sessions[id_]['config']
server_id = self.sessions[id_]['id']
orig_config = self.sessions[id_]['orig_config']
values = config.value.exportation()
orig_config.value.importation(values)
orig_config.permissive.importation(config.permissive.exportation())
# current values become old values in diff_config
config.information.set('orig_values', values)
def get_username(self, id_):
return self.get_session(id_)['username']
def set_config_mode(self, id_, mode):
""" Define which edition mode to select
"""
config = self.get_session(id_)['config']
for mode_level in modes.values():
if modes[mode] < mode_level:
config.property.add(mode_level.name)
else:
config.property.pop(mode_level.name)
self.sessions[id_]['mode'] = mode
def set_config_debug(self, id_, is_debug):
""" Enable/Disable debug mode
"""
config = self.get_session(id_)['config']
if is_debug:
config.property.pop('hidden')
else:
config.property.add('hidden')
self.sessions[id_]['debug'] = is_debug
class StorageServer(Storage):
def transform_orig_config(self, orig_config, server_id):
config_server = "std_{}".format(server_id)
orig_config = orig_config.config(config_server)
return config_server, orig_config
def set_owner(self, config, username):
config.owner.set(username)
class StorageServermodel(Storage):
def transform_orig_config(self, orig_config, server_id):
config_server = "v_{}".format(server_id)
return config_server, orig_config
def set_owner(self, config, username):
config.owner.set('servermodel_' + username)
storage_server = StorageServer()
storage_servermodel = StorageServermodel()

View File

@ -0,0 +1 @@
from .session import Risotto

View File

@ -0,0 +1,275 @@
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
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 valid_user(self,
session_id: str,
risotto_context: Context,
type: str) -> None:
""" check if current user is the session owner
"""
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
username = risotto_context.username
if username != storage.get_session(session_id)['username']:
raise NotAllowedError()
@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}'))
server = config_module.server[id]
config = server['server']
storage = storage_server
else:
if id not in config_module.servermodel:
raise Exception(_(f'cannot find {type} with id {id}'))
config = config_module.servermodel[id]
storage = storage_servermodel
# check if a session already exists, in this case returns it
session_list = self.list_sessions(type)
for sess in session_list:
if sess['id'] == id and sess['username'] == risotto_context.username:
session_id = sess['session_id']
session = self.get_session(session_id, type)
return self.format_session(session_id, session)
# create a new session
while True:
session_id = 'z' + hexlify(urandom(23)).decode()
if not storage.has_session(session_id):
break
else:
print('session {} already exists'.format(session_id))
username = risotto_context.username
storage.add_session(session_id,
config,
id,
username,
self.modify_storage)
return self.get_session_informations(session_id,
type)
@register(['v1.session.server.list', 'v1.session.servermodel.list'], None)
async def list_session_server(self,
risotto_context: Context):
type = risotto_context.message.rsplit('.', 2)[-2]
return self.list_sessions(type)
@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]
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
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(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(session_id,
type)
ret = {'session_id': session_id,
'name': name}
if index is not None:
ret['index'] = index
option = session['config'].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)
ret['status'] = 'ok'
except Exception as err:
if DEBUG:
print_exc()
ret['message'] = str(err)
ret['status'] = 'error'
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(session_id, type)
ret = {}
try:
session['config'].forcepermissive.option(session['namespace']).value.dict()
except Exception as err:
ret['status'] = 'error'
ret['message'] = str(err)
else:
if type == 'server':
mandatories = list(session['config'].forcepermissive.value.mandatory())
if mandatories:
ret['status'] = 'incomplete'
ret['mandatories'] = mandatories
else:
ret['status'] = 'ok'
else:
ret['status'] = 'ok'
return ret
@register(['v1.session.server.get', 'v1.session.servermodel.get'], None)
async def get_session_(self,
risotto_context: Context,
session_id: str) -> Dict:
type = risotto_context.message.rsplit('.', 2)[-2]
info = self.get_session_informations(session_id,
type)
info['content'] = session_id
session = self.get_session(session_id,
type)
info['content'] = dumps(session['option'].value.dict(fullpath=True))
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]
self.valid_user(session_id,
risotto_context,
type)
session = self.get_session(session_id,
type)
id_ = session['id']
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
if save:
storage.save_values(session_id)
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:
self.valid_user(session_id,
risotto_context,
'server')
session = storage_server.get_session(session_id)
return self.load_dict(session)
@register_http('v1', '/config/servermodel/{session_id}')
async def get_servermodel_api(self,
request,
risotto_context: Context,
session_id: str) -> Dict:
self.valid_user(session_id,
risotto_context,
'servermodel')
session = storage_servermodel.get_session(session_id)
return self.load_dict(session)
def get_session(self,
session_id: str,
type: str) -> Dict:
""" Get session information from storage
"""
if type == 'server':
return storage_server.get_session(session_id)
return storage_servermodel.get_session(session_id)
def get_session_informations(self,
session_id: str,
type: str) -> Dict:
""" format session with a session ID name
"""
session = self.get_session(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']}
def list_sessions(self,
type: str) -> List:
ret = []
if type == 'server':
storage = storage_server
else:
storage = storage_servermodel
for session_id, session in storage.get_sessions().items():
ret.append(self.format_session(session_id, session))
return ret
def load_dict(self,
session: Dict) -> Dict:
return session['option'].dict(remotable='all')

View File

@ -0,0 +1,148 @@
import time
from tiramisu import Config
from rougail import modes
class StorageError(Exception):
pass
class Storage(object):
__slots__ = ('sessions',)
def __init__(self):
self.sessions = {}
def has_session(self,
id: int):
return id in self.sessions
def add_session(self,
session_id: int,
orig_config: Config,
server_id: int,
username: str,
config_storage):
for session in self.sessions.values():
if session['id'] == server_id:
raise Storage(_(f'{username} already edits this configuration'))
prefix_id = f'{session_id}_'
config_name = self.get_config_name(server_id)
config_id = f'{prefix_id}{config_name}'
print(config_id)
# copy Config and all it's parents
meta = orig_config.config.deepcopy(session_id=config_id,
storage=config_storage,
metaconfig_prefix=prefix_id)
# retrieve the copied config (not metaconfig)
config = meta
while True:
try:
children = list(config.config.list())
except:
break
if children:
config = children[0]
else:
break
config.property.read_write()
# set the default owner
self.set_owner(config,
username)
# store it
self.sessions[session_id] = {'config': config,
# do not delete meta, so keep it!
'meta': meta,
'id': server_id,
'timestamp': int(time.time()),
'username': username}
self.set_config_mode(session_id,
'normal')
self.set_config_debug(session_id,
False)
self.set_namespace(session_id,
'creole')
def set_namespace(self,
session_id: int,
namespace: str):
self.sessions[session_id]['option'] = self.sessions[session_id]['config'].option(namespace)
self.sessions[session_id]['namespace'] = namespace
def get_sessions(self):
return self.sessions;
def del_session(self,
id: int):
del self.sessions[id]
def get_session(self,
id: int):
if id not in self.sessions:
raise Exception(f'the session {id} not exists')
return self.sessions[id]
def save_values(self, id_):
# FIXME
config = self.sessions[id_]['config']
server_id = self.sessions[id_]['id']
orig_config = self.sessions[id_]['orig_config']
values = config.value.exportation()
orig_config.value.importation(values)
orig_config.permissive.importation(config.permissive.exportation())
def get_username(self,
id: int):
return self.get_session(id)['username']
def set_config_mode(self,
id: int,
mode: str):
""" Define which edition mode to select
"""
config = self.get_session(id)['config']
for mode_level in modes.values():
if modes[mode] < mode_level:
config.property.add(mode_level.name)
else:
config.property.pop(mode_level.name)
self.sessions[id]['mode'] = mode
def set_config_debug(self, id_, is_debug):
""" Enable/Disable debug mode
"""
config = self.get_session(id_)['config']
if is_debug:
config.property.pop('hidden')
else:
config.property.add('hidden')
self.sessions[id_]['debug'] = is_debug
class StorageServer(Storage):
def get_config_name(self,
server_id: int):
return f'std_{server_id}'
def set_owner(self,
config: Config,
username: str):
config.owner.set(username)
class StorageServermodel(Storage):
def get_config_name(self,
server_id: int):
return f'v_{server_id}'
def set_owner(self,
config: Config,
username: str):
config.owner.set('servermodel_' + username)
storage_server = StorageServer()
storage_servermodel = StorageServermodel()

View File

@ -8,6 +8,7 @@ from ...controller import Controller
from ...register import register from ...register import register
from ...dispatcher import dispatcher from ...dispatcher import dispatcher
class Risotto(Controller): class Risotto(Controller):
def __init__(self): def __init__(self):
self.storage = Storage(engine='dictionary') self.storage = Storage(engine='dictionary')