forked from Infra/risotto
import config-manager controller
This commit is contained in:
parent
332dc61fd4
commit
847fbfc1e1
|
@ -1,3 +1,5 @@
|
||||||
HTTP_PORT = 8080
|
HTTP_PORT = 8080
|
||||||
MESSAGE_ROOT_PATH = 'messages'
|
MESSAGE_ROOT_PATH = 'messages'
|
||||||
DEBUG = False
|
ROOT_CACHE_DIR = 'cache'
|
||||||
|
DEBUG = True
|
||||||
|
DATABASE_DIR = 'database'
|
||||||
|
|
|
@ -47,7 +47,7 @@ class RegisterDispatcher:
|
||||||
def get_function_args():
|
def get_function_args():
|
||||||
function_args = self.get_function_args(function)
|
function_args = self.get_function_args(function)
|
||||||
# risotto_context is a special argument, remove it
|
# risotto_context is a special argument, remove it
|
||||||
if function_args[0] == 'risotto_context':
|
if function_args and function_args[0] == 'risotto_context':
|
||||||
function_args = function_args[1:]
|
function_args = function_args[1:]
|
||||||
return set(function_args)
|
return set(function_args)
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ class RegisterDispatcher:
|
||||||
if not Config(self.option).option(uri).option.type() == 'message':
|
if not Config(self.option).option(uri).option.type() == 'message':
|
||||||
raise RegistrationError(_(f'{uri} is not a valid message'))
|
raise RegistrationError(_(f'{uri} is not a valid message'))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise RegistrationError(_(f'{uri} is not a valid message'))
|
raise RegistrationError(_(f'the message {uri} not exists'))
|
||||||
|
|
||||||
# create an uris' version if needed
|
# create an uris' version if needed
|
||||||
if version not in self.uris:
|
if version not in self.uris:
|
||||||
|
@ -139,7 +139,7 @@ class RegisterDispatcher:
|
||||||
|
|
||||||
# True if first argument is the risotto_context
|
# True if first argument is the risotto_context
|
||||||
function_args = self.get_function_args(function)
|
function_args = self.get_function_args(function)
|
||||||
if function_args[0] == 'risotto_context':
|
if function_args and function_args[0] == 'risotto_context':
|
||||||
inject_risotto_context = True
|
inject_risotto_context = True
|
||||||
function_args.pop(0)
|
function_args.pop(0)
|
||||||
else:
|
else:
|
||||||
|
@ -162,17 +162,18 @@ class RegisterDispatcher:
|
||||||
self.uris[version][uri] = dico
|
self.uris[version][uri] = dico
|
||||||
else:
|
else:
|
||||||
# if event
|
# if event
|
||||||
if notification and notification is not undefined:
|
|
||||||
raise RegistrationError(_(f'notification not supported yet'))
|
|
||||||
# valid function's arguments
|
# valid function's arguments
|
||||||
self._valid_event_params(version, uri, function, module_name)
|
self._valid_event_params(version, uri, function, module_name)
|
||||||
# register this function
|
# register this function
|
||||||
if uri not in self.uris[version]:
|
if uri not in self.uris[version]:
|
||||||
self.uris[version][uri] = []
|
self.uris[version][uri] = []
|
||||||
self.uris[version][uri].append({'module': module_name,
|
dico = {'module': module_name,
|
||||||
'function': function,
|
'function': function,
|
||||||
'arguments': function_args,
|
'arguments': function_args,
|
||||||
'risotto_context': inject_risotto_context})
|
'risotto_context': inject_risotto_context}
|
||||||
|
if notification and notification is not undefined:
|
||||||
|
dico['notification'] = notification
|
||||||
|
self.uris[version][uri].append(dico)
|
||||||
|
|
||||||
def set_module(self, module_name, module):
|
def set_module(self, module_name, module):
|
||||||
""" register and instanciate a new module
|
""" register and instanciate a new module
|
||||||
|
@ -269,42 +270,45 @@ class Dispatcher(RegisterDispatcher):
|
||||||
uri:str,
|
uri:str,
|
||||||
context: Context,
|
context: Context,
|
||||||
kwargs: Dict):
|
kwargs: Dict):
|
||||||
if not isinstance(returns, dict):
|
if isinstance(returns, dict):
|
||||||
|
returns = [returns]
|
||||||
|
if not isinstance(returns, list):
|
||||||
module_name = function.__module__.split('.')[-2]
|
module_name = function.__module__.split('.')[-2]
|
||||||
function_name = function.__name__
|
function_name = function.__name__
|
||||||
err = _(f'function {module_name}.{function_name} has to return a dict')
|
err = _(f'function {module_name}.{function_name} has to return a dict or a list')
|
||||||
log.error_msg(version, uri, context, kwargs, 'call', err)
|
log.error_msg(version, uri, context, kwargs, 'call', err)
|
||||||
raise CallError(str(err))
|
raise CallError(str(err))
|
||||||
response = self.messages[uri]['response']
|
response = self.messages[uri]['response']
|
||||||
if response is None:
|
if response is None:
|
||||||
raise Exception('hu?')
|
raise Exception('hu?')
|
||||||
else:
|
else:
|
||||||
config = Config(response, display_name=lambda self, dyn_name: self.impl_getname())
|
for ret in returns:
|
||||||
config.property.read_write()
|
config = Config(response, display_name=lambda self, dyn_name: self.impl_getname())
|
||||||
try:
|
config.property.read_write()
|
||||||
for key, value in returns.items():
|
try:
|
||||||
config.option(key).value.set(value)
|
for key, value in ret.items():
|
||||||
except AttributeError:
|
config.option(key).value.set(value)
|
||||||
module_name = function.__module__.split('.')[-2]
|
except AttributeError:
|
||||||
function_name = function.__name__
|
module_name = function.__module__.split('.')[-2]
|
||||||
err = _(f'function {module_name}.{function_name} return the unknown parameter "{key}"')
|
function_name = function.__name__
|
||||||
log.error_msg(version, uri, context, kwargs, 'call', err)
|
err = _(f'function {module_name}.{function_name} return the unknown parameter "{key}"')
|
||||||
raise CallError(str(err))
|
log.error_msg(version, uri, context, kwargs, 'call', err)
|
||||||
except ValueError:
|
raise CallError(str(err))
|
||||||
module_name = function.__module__.split('.')[-2]
|
except ValueError:
|
||||||
function_name = function.__name__
|
module_name = function.__module__.split('.')[-2]
|
||||||
err = _(f'function {module_name}.{function_name} return the parameter "{key}" with an unvalid value "{value}"')
|
function_name = function.__name__
|
||||||
log.error_msg(version, uri, context, kwargs, 'call', err)
|
err = _(f'function {module_name}.{function_name} return the parameter "{key}" with an unvalid value "{value}"')
|
||||||
raise CallError(str(err))
|
log.error_msg(version, uri, context, kwargs, 'call', err)
|
||||||
config.property.read_only()
|
raise CallError(str(err))
|
||||||
try:
|
config.property.read_only()
|
||||||
config.value.dict()
|
try:
|
||||||
except Exception as err:
|
config.value.dict()
|
||||||
module_name = function.__module__.split('.')[-2]
|
except Exception as err:
|
||||||
function_name = function.__name__
|
module_name = function.__module__.split('.')[-2]
|
||||||
err = _(f'function {module_name}.{function_name} return an invalid response {err}')
|
function_name = function.__name__
|
||||||
log.error_msg(version, uri, context, kwargs, 'call', err)
|
err = _(f'function {module_name}.{function_name} return an invalid response {err}')
|
||||||
raise CallError(str(err))
|
log.error_msg(version, uri, context, kwargs, 'call', err)
|
||||||
|
raise CallError(str(err))
|
||||||
|
|
||||||
|
|
||||||
async def call(self, version, uri, risotto_context, public_only=False, **kwargs):
|
async def call(self, version, uri, risotto_context, public_only=False, **kwargs):
|
||||||
|
@ -351,10 +355,15 @@ class Dispatcher(RegisterDispatcher):
|
||||||
# notification
|
# notification
|
||||||
if obj.get('notification'):
|
if obj.get('notification'):
|
||||||
notif_version, notif_message = obj['notification'].split('.', 1)
|
notif_version, notif_message = obj['notification'].split('.', 1)
|
||||||
await self.publish(notif_version,
|
if not isinstance(returns, list):
|
||||||
notif_message,
|
send_returns = [returns]
|
||||||
new_context,
|
else:
|
||||||
**returns)
|
send_returns = returns
|
||||||
|
for ret in send_returns:
|
||||||
|
await self.publish(notif_version,
|
||||||
|
notif_message,
|
||||||
|
new_context,
|
||||||
|
**ret)
|
||||||
return returns
|
return returns
|
||||||
|
|
||||||
async def publish(self, version, uri, risotto_context, public_only=False, **kwargs):
|
async def publish(self, version, uri, risotto_context, public_only=False, **kwargs):
|
||||||
|
@ -404,6 +413,14 @@ class Dispatcher(RegisterDispatcher):
|
||||||
function_name = function.__name__
|
function_name = function.__name__
|
||||||
log.info_msg(version, uri, new_context, kwargs,'publish', info_msg)
|
log.info_msg(version, uri, new_context, kwargs,'publish', info_msg)
|
||||||
|
|
||||||
|
# notification
|
||||||
|
if obj.get('notification'):
|
||||||
|
notif_version, notif_message = obj['notification'].split('.', 1)
|
||||||
|
await self.publish(notif_version,
|
||||||
|
notif_message,
|
||||||
|
new_context,
|
||||||
|
**returns)
|
||||||
|
|
||||||
|
|
||||||
dispatcher = Dispatcher()
|
dispatcher = Dispatcher()
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ async def api(request):
|
||||||
if not tiramisu:
|
if not tiramisu:
|
||||||
config = Config(get_messages(load_shortarg=True,
|
config = Config(get_messages(load_shortarg=True,
|
||||||
only_public=True)[1])
|
only_public=True)[1])
|
||||||
|
config.property.read_write()
|
||||||
tiramisu = config.option.dict(remotable='none')
|
tiramisu = config.option.dict(remotable='none')
|
||||||
return Response(text=dumps(tiramisu))
|
return Response(text=dumps(tiramisu))
|
||||||
|
|
||||||
|
|
|
@ -464,15 +464,19 @@ def _parse_responses(message_def,
|
||||||
raise Exception('multi response with name {} in {}'.format(name, file_path))
|
raise Exception('multi response with name {} in {}'.format(name, file_path))
|
||||||
names.append(name)
|
names.append(name)
|
||||||
|
|
||||||
|
kwargs = {'name': name,
|
||||||
|
'doc': obj.description.strip().rstrip()}
|
||||||
|
type_ = obj.type
|
||||||
|
if type_.startswith('[]'):
|
||||||
|
kwargs['multi'] = True
|
||||||
|
type_ = type_[2:]
|
||||||
option = {'String': StrOption,
|
option = {'String': StrOption,
|
||||||
'Number': IntOption,
|
'Number': IntOption,
|
||||||
'Boolean': BoolOption,
|
'Boolean': BoolOption,
|
||||||
# FIXME
|
# FIXME
|
||||||
'File': StrOption}.get(obj.type)
|
'File': StrOption}.get(type_)
|
||||||
if not option:
|
if not option:
|
||||||
raise Exception(f'unknown param type {obj.type}')
|
raise Exception(f'unknown param type {obj.type}')
|
||||||
kwargs = {'name': name,
|
|
||||||
'doc': obj.description.strip().rstrip()}
|
|
||||||
if hasattr(obj, 'default'):
|
if hasattr(obj, 'default'):
|
||||||
kwargs['default'] = obj.default
|
kwargs['default'] = obj.default
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,32 +1,536 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#import logging
|
||||||
|
#from lxml.etree import parse
|
||||||
|
#from io import StringIO
|
||||||
|
#from autobahn.wamp.exception import ApplicationError
|
||||||
|
#import asyncio
|
||||||
|
from tiramisu import Storage, MixConfig, delete_session
|
||||||
|
#from tiramisu.error import PropertiesOptionError
|
||||||
|
#
|
||||||
|
#from os import urandom, unlink
|
||||||
|
#from os.path import isfile, join
|
||||||
|
#from binascii import hexlify
|
||||||
|
#from json import dumps, loads
|
||||||
|
#from aiohttp.web import HTTPForbidden
|
||||||
|
|
||||||
|
#from creole.loader import PopulateTiramisuObjects
|
||||||
|
#from zephir.controller import ZephirCommonController, run
|
||||||
|
#from zephir.http import register as register_http
|
||||||
|
#from zephir.wamp import register as register_wamp
|
||||||
|
#from zephir.config import DEBUG
|
||||||
|
##from eolegenconfig import webapi
|
||||||
|
#from eolegenconfig.lib import storage
|
||||||
|
#from eolegenconfig import lib
|
||||||
|
#from zephir.i18n import _
|
||||||
from ...controller import Controller
|
from ...controller import Controller
|
||||||
from ...dispatcher import register
|
from ...dispatcher import register
|
||||||
|
from ...config import ROOT_CACHE_DIR, DATABASE_DIR
|
||||||
|
from ...context import Context
|
||||||
|
from .storage import storage_server, storage_servermodel
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Risotto(Controller):
|
class Risotto(Controller):
|
||||||
@register('v1.config.configuration.server.updated')
|
servermodel = {}
|
||||||
async def server_created(self, server_id):
|
# FIXME : should be renamed to probe
|
||||||
print('pouet ' + str(server_id))
|
server = {}
|
||||||
|
|
||||||
@register('v1.config.session.server.start', None)
|
def __init__(self, *args, **kwargs):
|
||||||
async def get_configuration(self, risotto_context, id):
|
# add root and statics
|
||||||
#stop = await self.call('v1.config.session.server.stop', risotto_context, sessionid=str(id))
|
# FIXME
|
||||||
#await self.publish('v1.config.configuration.server.updated', risotto_context, server_id=1, deploy=True)
|
#default_storage.setting(engine='sqlite3', dir_database='/srv/database')
|
||||||
return {'id': id,
|
self.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR)
|
||||||
'sessionid': 'sess',
|
self.modify_storage = Storage(engine='dictionary')
|
||||||
'username': risotto_context.username,
|
super().__init__(*args, **kwargs)
|
||||||
'timestamp': 0,
|
|
||||||
'namespace': 'creole',
|
|
||||||
'mode': 'basic',
|
|
||||||
'debug': False}
|
|
||||||
|
|
||||||
@register('v1.config.session.server.stop', None)
|
def valid_user(self, sessionid, risotto_context):
|
||||||
async def get_configuration2(self, sessionid, save):
|
username = risotto_context.username
|
||||||
return {'stop': sessionid}
|
if username != storage.get_username(sessionid):
|
||||||
|
raise HTTPForbidden()
|
||||||
|
|
||||||
|
async def onJoin(self, *args, **kwargs):
|
||||||
|
await super().onJoin(*args, **kwargs)
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
await self.load_servermodels()
|
||||||
|
await self.load_servers()
|
||||||
|
|
||||||
|
async def load_servermodels(self):
|
||||||
|
print('Load servermodels')
|
||||||
|
try:
|
||||||
|
servermodels = await self.call('v1.servermodel.list')
|
||||||
|
except ApplicationError as err:
|
||||||
|
print(_('cannot load servermodel list: {}').format(str(err)))
|
||||||
|
return
|
||||||
|
for servermodel in servermodels:
|
||||||
|
try:
|
||||||
|
await self.load_servermodel(servermodel['servermodelid'], servermodel['servermodelname'])
|
||||||
|
except ApplicationError as err:
|
||||||
|
if DEBUG:
|
||||||
|
print('Error, cannot load servermodel {}: {}'.format(servermodel['servermodelname'], err))
|
||||||
|
for servermodel in servermodels:
|
||||||
|
if 'servermodelparentsid' in servermodel:
|
||||||
|
for servermodelparentid in servermodel['servermodelparentsid']:
|
||||||
|
self.servermodel_legacy(servermodel['servermodelname'], servermodel['servermodelid'], servermodelparentid)
|
||||||
|
|
||||||
|
|
||||||
|
async def load_servermodel(self, servermodelid, servermodelname):
|
||||||
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
|
cache_file = join(ROOT_CACHE_DIR, str(servermodelid)+".xml")
|
||||||
|
creolefunc_file = join(ROOT_CACHE_DIR, str(servermodelid)+".creolefuncs")
|
||||||
|
print('Load servermodel {} ({})'.format(servermodelname, servermodelid))
|
||||||
|
|
||||||
|
if isfile(cache_file):
|
||||||
|
fileio = open(cache_file)
|
||||||
|
else:
|
||||||
|
servermodel = await self.call('v1.servermodel.describe',
|
||||||
|
servermodelid=servermodelid,
|
||||||
|
inheritance=False,
|
||||||
|
resolvdepends=False,
|
||||||
|
schema=True,
|
||||||
|
creolefuncs=True)
|
||||||
|
fileio = StringIO()
|
||||||
|
fileio.write(servermodel['schema'])
|
||||||
|
fileio.seek(0)
|
||||||
|
|
||||||
|
with open(cache_file, 'w') as cache:
|
||||||
|
cache.write(servermodel['schema'])
|
||||||
|
with open(creolefunc_file, 'w') as cache:
|
||||||
|
cache.write(servermodel['creolefuncs'])
|
||||||
|
del servermodel
|
||||||
|
xmlroot = parse(fileio).getroot()
|
||||||
|
tiramisu_objects = PopulateTiramisuObjects()
|
||||||
|
tiramisu_objects.parse_dtd('/srv/src/creole/data/creole.dtd')
|
||||||
|
tiramisu_objects.make_tiramisu_objects(xmlroot, creolefunc_file)
|
||||||
|
config = tiramisu_objects.build(persistent=True,
|
||||||
|
session_id='v_{}'.format(servermodelid),
|
||||||
|
meta_config=True)
|
||||||
|
|
||||||
|
config.owner.set('v_{}'.format(servermodelname))
|
||||||
|
config.information.set('servermodel_id', servermodelid)
|
||||||
|
config.information.set('servermodel_name', servermodelname)
|
||||||
|
|
||||||
|
self.servermodel[servermodelid] = config
|
||||||
|
|
||||||
|
def servermodel_legacy(self, servermodel_name, servermodel_id, servermodel_parent_id):
|
||||||
|
if servermodel_parent_id is None:
|
||||||
|
return
|
||||||
|
if not self.servermodel.get(servermodel_parent_id):
|
||||||
|
if DEBUG:
|
||||||
|
print(f'Servermodel with id {servermodel_parent_id} not loaded, skipping legacy for servermodel {servermodel_name} ({servermodel_id})')
|
||||||
|
return
|
||||||
|
servermodel_parent = self.servermodel[servermodel_parent_id]
|
||||||
|
servermodel_parent_name = servermodel_parent.information.get('servermodel_name')
|
||||||
|
if DEBUG:
|
||||||
|
print(f'Create legacy of servermodel {servermodel_name} ({servermodel_id}) with parent {servermodel_parent_name} ({servermodel_parent_id})')
|
||||||
|
mix = servermodel_parent.config.get('m_v_' + str(servermodel_parent_id))
|
||||||
|
try:
|
||||||
|
mix.config.add(self.servermodel[servermodel_id])
|
||||||
|
except Exception as err:
|
||||||
|
if DEBUG:
|
||||||
|
print(str(err))
|
||||||
|
|
||||||
|
|
||||||
|
async def load_servers(self):
|
||||||
|
print('Load servers')
|
||||||
|
try:
|
||||||
|
risotto_context = Context()
|
||||||
|
risotto_context.username = 'root'
|
||||||
|
servers = await self.call('v1.server.list', risotto_context)
|
||||||
|
except ApplicationError as err:
|
||||||
|
print(_('cannot load server list: {}').format(str(err)))
|
||||||
|
return
|
||||||
|
for server in servers:
|
||||||
|
try:
|
||||||
|
self.load_server(server['serverid'], server['servername'], server['servermodelid'])
|
||||||
|
await self._load_env(server['serverid'])
|
||||||
|
except Exception as err:
|
||||||
|
print('Unable to load server {} ({}): {}'.format(server['servername'], server['serverid'], err))
|
||||||
|
|
||||||
|
def load_server(self, serverid, servername, servermodelid):
|
||||||
|
if serverid in self.server:
|
||||||
|
return
|
||||||
|
print('Load server {} ({})'.format(servername, serverid))
|
||||||
|
if not servermodelid in self.servermodel:
|
||||||
|
raise ValueError(f'unable to find servermodel with id {servermodelid}')
|
||||||
|
metaconfig = self.servermodel[servermodelid].config.new('p_{}'.format(serverid),
|
||||||
|
persistent=True,
|
||||||
|
type='metaconfig')
|
||||||
|
metaconfig.information.set('server_id', serverid)
|
||||||
|
metaconfig.information.set('server_name', servername)
|
||||||
|
metaconfig.owner.set('probe')
|
||||||
|
config = metaconfig.config.new('s_{}'.format(serverid),
|
||||||
|
persistent=True)
|
||||||
|
config.owner.set(servername)
|
||||||
|
config = metaconfig.config.new('std_{}'.format(serverid),
|
||||||
|
persistent=True)
|
||||||
|
config.owner.set(servername)
|
||||||
|
|
||||||
|
if 'disabled' not in config.property.get():
|
||||||
|
# has to be read_only
|
||||||
|
ro = list(config.property.getdefault('read_only', 'append'))
|
||||||
|
if 'force_store_value' in ro:
|
||||||
|
# force_store_value is not allowed for new server (wait when configuration is deploy)
|
||||||
|
ro.remove('force_store_value')
|
||||||
|
config.property.setdefault(frozenset(ro), 'read_only', 'append')
|
||||||
|
rw = list(config.property.getdefault('read_write', 'append'))
|
||||||
|
rw.remove('force_store_value')
|
||||||
|
config.property.setdefault(frozenset(rw), 'read_write', 'append')
|
||||||
|
config.property.read_only()
|
||||||
|
|
||||||
|
self.server[serverid] = metaconfig
|
||||||
|
|
||||||
|
async def _load_env(self, server_id):
|
||||||
|
metaconfig = self.server[server_id]
|
||||||
|
old_informations = {}
|
||||||
|
for old_information in metaconfig.information.list():
|
||||||
|
old_informations[old_information] = metaconfig.information.get(old_information)
|
||||||
|
metaconfig.config.reset()
|
||||||
|
for old_information, old_value in old_informations.items():
|
||||||
|
metaconfig.information.set(old_information, old_value)
|
||||||
|
risotto_context = Context()
|
||||||
|
risotto_context.username = 'root'
|
||||||
|
server = await self.call('v1.server.describe', risotto_context=risotto_context, serverid=server_id, environment=True)
|
||||||
|
for key, value in server['serverenvironment'].items():
|
||||||
|
metaconfig.unrestraint.option(key).value.set(value)
|
||||||
|
if server['serverenvironment']:
|
||||||
|
metaconfig.unrestraint.option('creole.general.available_probes').value.set("oui")
|
||||||
|
else:
|
||||||
|
metaconfig.unrestraint.option('creole.general.available_probes').value.set("non")
|
||||||
|
|
||||||
|
# @register('v1.server.created', None)
|
||||||
|
# async def server_created(self, serverid, servername, servermodelid):
|
||||||
|
# self.load_server(serverid, servername, servermodelid)
|
||||||
|
#
|
||||||
|
# @register('v1.server.deleted', None)
|
||||||
|
# async def server_deleted(self, serverid):
|
||||||
|
# metaconfig = self.server[serverid]
|
||||||
|
# # remove config inside metaconfig
|
||||||
|
# for config in metaconfig.config.list():
|
||||||
|
# metaconfig.config.pop(config.config.name())
|
||||||
|
# delete_session(config.config.name())
|
||||||
|
# del config
|
||||||
|
# # delete config to parents
|
||||||
|
# for parent in metaconfig.config.parents():
|
||||||
|
# parent.config.pop(metaconfig.config.name())
|
||||||
|
# # delete metaconfig
|
||||||
|
# delete_session(metaconfig.config.name())
|
||||||
|
# del self.server[serverid]
|
||||||
|
# del metaconfig
|
||||||
|
|
||||||
|
# @register('v1.server.environment.updated', "v1.config.configuration.server.updated")
|
||||||
|
# async def env_updated(self, server_id):
|
||||||
|
# await self._load_env(server_id)
|
||||||
|
# self.publish('v1.config.configuration.server.updated', server_id=server_id, deploy=False)
|
||||||
|
# return {'server_id': server_id, 'deploy': True}
|
||||||
|
|
||||||
|
# @register('v1.servermodel.created', None)
|
||||||
|
# async def servermodel_created(self, servermodels):
|
||||||
|
# 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)
|
||||||
|
#
|
||||||
|
# @register('v1.servermodel.updated', None)
|
||||||
|
# async def servermodel_updated(self, servermodels):
|
||||||
|
# for servermodel in servermodels:
|
||||||
|
# servermodelid = servermodel['servermodelid']
|
||||||
|
# servermodelname = servermodel['servermodelname']
|
||||||
|
# servermodelparentsid = servermodel.get('servermodelparentsid')
|
||||||
|
# print('Reload servermodel {} ({})'.format(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]
|
||||||
|
# # create new one
|
||||||
|
# await self.load_servermodel(servermodelid, servermodelname)
|
||||||
|
# # 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)
|
||||||
|
# # load servers in servermodel
|
||||||
|
# for subconfig in old_servermodel.config.list():
|
||||||
|
# if not isinstance(subconfig, MixConfig):
|
||||||
|
# name = subconfig.config.name()
|
||||||
|
# try:
|
||||||
|
# old_servermodel.config.pop(name)
|
||||||
|
# except:
|
||||||
|
# pass
|
||||||
|
# server_id = subconfig.information.get('server_id')
|
||||||
|
# server_name = subconfig.information.get('server_name')
|
||||||
|
# del self.server[server_id]
|
||||||
|
# self.load_server(server_id, server_name, servermodelid)
|
||||||
|
# else:
|
||||||
|
# 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)
|
||||||
|
|
||||||
@register('v1.config.configuration.server.get', None)
|
@register('v1.config.configuration.server.get', None)
|
||||||
async def get_configuration3(self, server_id, deploy):
|
async def get_configuration(self, server_id, deploy):
|
||||||
return {'get': server_id}
|
return {'configuration': (server_id, deploy)}
|
||||||
|
|
||||||
@register('v1.config.configuration.server.deploy')
|
@register('v1.config.configuration.server.deploy', 'v1.config.configuration.server.updated')
|
||||||
async def get_configuration4(self, server_id):
|
async def deploy_configuration(self, server_id):
|
||||||
return {'deploy': server_id}
|
"""Copy values, permissions, permissives from config 'to deploy' to active config
|
||||||
|
"""
|
||||||
|
metaconfig = self.server[server_id]
|
||||||
|
config_std = metaconfig.config("std_{}".format(server_id))
|
||||||
|
|
||||||
|
ro = config_std.property.getdefault('read_only', 'append')
|
||||||
|
if 'force_store_value' not in ro:
|
||||||
|
ro = frozenset(list(ro) + ['force_store_value'])
|
||||||
|
config_std.property.setdefault(ro, 'read_only', 'append')
|
||||||
|
rw = config_std.property.getdefault('read_write', 'append')
|
||||||
|
rw = frozenset(list(rw) + ['force_store_value'])
|
||||||
|
config_std.property.setdefault(rw, 'read_write', 'append')
|
||||||
|
config_std.property.add('force_store_value')
|
||||||
|
|
||||||
|
config = metaconfig.config("s_{}".format(server_id))
|
||||||
|
config.value.importation(config_std.value.exportation())
|
||||||
|
config.permissive.importation(config_std.permissive.exportation())
|
||||||
|
config.property.importation(config_std.property.exportation())
|
||||||
|
return {'server_id': server_id, 'deploy': True}
|
||||||
|
|
||||||
|
# SESSION
|
||||||
|
#__________________________________________________________________
|
||||||
|
def get_session(self, session_id, type):
|
||||||
|
if type == 'server':
|
||||||
|
return storage_server.get_session(session_id)
|
||||||
|
return storage_servermodel.get_session(session_id)
|
||||||
|
|
||||||
|
def get_session_informations(self, session_id, type):
|
||||||
|
session = self.get_session(session_id, type)
|
||||||
|
return self.format_session(session_id, session)
|
||||||
|
|
||||||
|
def format_session(self, session_name, session):
|
||||||
|
return {'sessionid': 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):
|
||||||
|
ret = []
|
||||||
|
if type == 'server':
|
||||||
|
storage = storage_server
|
||||||
|
else:
|
||||||
|
storage = storage_servermodel
|
||||||
|
for session in storage.list_sessions():
|
||||||
|
ret.append(self.format_session(session['sessionid'], session))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def load_dict(self, session):
|
||||||
|
if not session['option']:
|
||||||
|
session['option'] = session['config'].option(session['namespace'])
|
||||||
|
return session['option'].dict(remotable='all')
|
||||||
|
|
||||||
|
# start
|
||||||
|
async def start_session(self, risotto_context, id, type, server_list):
|
||||||
|
if id not in server_list:
|
||||||
|
raise Exception(_(f'cannot find {type} with id {id}'))
|
||||||
|
|
||||||
|
session_id = ''
|
||||||
|
session_list = self.list_sessions(type)
|
||||||
|
for sess in session_list:
|
||||||
|
if sess['id'] == id and sess['username'] == risotto_context.username:
|
||||||
|
session_id = sess['sessionid']
|
||||||
|
session = self.get_session(session_id, type)
|
||||||
|
return self.format_session(session_id, session)
|
||||||
|
else:
|
||||||
|
session_id = ''
|
||||||
|
|
||||||
|
if session_id == '':
|
||||||
|
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], type, id, username, self.modify_storage)
|
||||||
|
return self.get_session_informations(session_id, type)
|
||||||
|
|
||||||
|
@register('v1.config.session.server.start', None)
|
||||||
|
async def start_session_server(self, risotto_context, id):
|
||||||
|
return await self.start_session(risotto_context, id, 'server', self.server)
|
||||||
|
|
||||||
|
@register('v1.config.session.servermodel.start', None)
|
||||||
|
async def start_session_servermodel(self, risotto_context, id):
|
||||||
|
return await self.start_session(risotto_context, id, 'servermodel', self.servermodel)
|
||||||
|
|
||||||
|
# list
|
||||||
|
@register('v1.config.session.server.list', None)
|
||||||
|
async def list_session_server(self):
|
||||||
|
return self.list_sessions('server')
|
||||||
|
|
||||||
|
@register('v1.config.session.servermodel.list', None)
|
||||||
|
async def list_session_servermodel(self):
|
||||||
|
return self.list_sessions('servermodel')
|
||||||
|
|
||||||
|
# filter
|
||||||
|
async def filter_session(self, session_id, type, namespace, mode, debug):
|
||||||
|
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 Exception(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.filter', None)
|
||||||
|
async def filter_session_server(self, session_id, namespace, mode, debug):
|
||||||
|
return await self.filter_session(session_id, 'server', namespace, mode, debug)
|
||||||
|
|
||||||
|
@register('v1.config.session.servermodel.filter', None)
|
||||||
|
async def filter_session_servermodel(self, session_id, namespace, mode, debug):
|
||||||
|
return await self.filter_session(session_id, 'servermodel', namespace, mode, debug)
|
||||||
|
|
||||||
|
# configure
|
||||||
|
async def configure_session(self, session_id, type, action, name, index, value):
|
||||||
|
session = self.get_session(session_id, type)
|
||||||
|
ret = {'session_id': session_id,
|
||||||
|
'name': name}
|
||||||
|
if index is not None:
|
||||||
|
ret['index'] = index
|
||||||
|
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:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
ret['message'] = str(err)
|
||||||
|
ret['status'] = 'error'
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@register('v1.config.session.server.configure', None)
|
||||||
|
async def configure_session_server(self, session_id, action, name, index, value):
|
||||||
|
return await self.configure_session(session_id, 'server', action, name, index, value)
|
||||||
|
|
||||||
|
@register('v1.config.session.servermodel.configure', None)
|
||||||
|
async def configure_session_servermodel(self, session_id, action, name, index, value):
|
||||||
|
return await self.configure_session(session_id, 'servermodel', action, name, index, value)
|
||||||
|
|
||||||
|
# validate
|
||||||
|
async def validate_session(self, session_id, type):
|
||||||
|
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.validate', None)
|
||||||
|
async def validate_session_server(self, session_id):
|
||||||
|
return await self.validate_session(session_id, 'server')
|
||||||
|
|
||||||
|
@register('v1.config.session.servermodel.validate', None)
|
||||||
|
async def validate_session_servermodel(self, session_id):
|
||||||
|
return await self.validate_session(session_id, 'servermodel')
|
||||||
|
|
||||||
|
# get
|
||||||
|
async def get_session_(self, session_id, type):
|
||||||
|
info = self.get_session_informations(session_id, type)
|
||||||
|
info['content'] = session_id
|
||||||
|
return info
|
||||||
|
|
||||||
|
@register('v1.config.session.server.get', None)
|
||||||
|
async def get_session_server(self, session_id):
|
||||||
|
return await self.get_session_(session_id, 'server')
|
||||||
|
|
||||||
|
@register('v1.config.session.servermodel.get', None)
|
||||||
|
async def get_session_servermodel(self, session_id):
|
||||||
|
return await self.get_session_(session_id, 'servermodel')
|
||||||
|
|
||||||
|
# stop
|
||||||
|
async def stop_session(self, risotto_context, session_id, type, save):
|
||||||
|
session = self.get_session(session_id, type)
|
||||||
|
if save:
|
||||||
|
await self._post_save_config(risotto_context, None, session_id)
|
||||||
|
if type == 'server':
|
||||||
|
storage = storage_server
|
||||||
|
else:
|
||||||
|
storage = storage_servermodel
|
||||||
|
storage.del_session(session_id, type)
|
||||||
|
return self.format_session(session_id, session)
|
||||||
|
|
||||||
|
@register('v1.config.session.server.stop', None)
|
||||||
|
async def stop_session_server(self, risotto_context, sessionid, save):
|
||||||
|
return await self.stop_session(sessionid, 'server', save)
|
||||||
|
|
||||||
|
@register('v1.config.session.servermodel.stop', None)
|
||||||
|
async def stop_session_servermodel(self, risotto_context, sessionid, save):
|
||||||
|
return await self.stop_session(risotto_context, sessionid, 'servermodel', save)
|
||||||
|
|
||||||
|
# GEN_CONFIG
|
||||||
|
#__________________________________________________________________
|
||||||
|
|
||||||
|
async def _post_save_config(self, risotto_context, request, sessionid):
|
||||||
|
self.valid_user(sessionid, risotto_context)
|
||||||
|
lib.save_values(sessionid, 'save')
|
||||||
|
id_ = storage.get_id(sessionid)
|
||||||
|
if storage.get_type(sessionid) == 'server':
|
||||||
|
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:
|
||||||
|
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)
|
||||||
|
return {}
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
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, sessionid, 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(sessionid)
|
||||||
|
config_server, orig_config = self.transform_orig_config(orig_config)
|
||||||
|
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(self, config)
|
||||||
|
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[sessionid] = {'config': config,
|
||||||
|
# do not delete meta, so keep it!
|
||||||
|
'meta': meta,
|
||||||
|
'orig_config': orig_config,
|
||||||
|
'id': server_id,
|
||||||
|
'timestamp': time.time(),
|
||||||
|
'username': username,
|
||||||
|
'option': None,
|
||||||
|
'namespace': 'creole',
|
||||||
|
'config_exists': config_exists}
|
||||||
|
self.set_config_mode(sessionid, 'normal')
|
||||||
|
self.set_config_debug(sessionid, False)
|
||||||
|
|
||||||
|
def list_sessions(self):
|
||||||
|
for sessionid, session in self.sessions.items():
|
||||||
|
yield {'sessionid': sessionid,
|
||||||
|
'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 GenConfigError('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 get_id(self, id_):
|
||||||
|
return self.get_session(id_)['id']
|
||||||
|
|
||||||
|
def set_config_mode(self, id_, mode):
|
||||||
|
""" Define which edition mode to select
|
||||||
|
:param id_: session id
|
||||||
|
:type id_: `str`
|
||||||
|
:param mode: possible values = ['basic', 'normal', 'expert']
|
||||||
|
:type mode: `str`
|
||||||
|
:returns: session mode value
|
||||||
|
:type :`bool`
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
# store mode in session in case config object gets reloader
|
||||||
|
self.sessions[id_]['mode'] = mode
|
||||||
|
|
||||||
|
def set_config_debug(self, id_, is_debug):
|
||||||
|
""" Enable/Disable debug mode
|
||||||
|
:param id_: session id
|
||||||
|
:type id_: `str`
|
||||||
|
:param is_debug: True to enable debug mode
|
||||||
|
:type is_debug: `bool`
|
||||||
|
:returns: session debug value
|
||||||
|
:type :`bool`
|
||||||
|
"""
|
||||||
|
config = self.get_session(id_)['config']
|
||||||
|
if is_debug:
|
||||||
|
config.property.pop('hidden')
|
||||||
|
else:
|
||||||
|
config.property.add('hidden')
|
||||||
|
self.sessions[id_]['debug'] = is_debug
|
||||||
|
return is_debug
|
||||||
|
|
||||||
|
|
||||||
|
class StorageServer(Storage):
|
||||||
|
def transform_orig_config(self, orig_config):
|
||||||
|
config_server = "std_{}".format(server_id)
|
||||||
|
orig_config = orig_config.config(config_server)
|
||||||
|
return config_server, orig_config
|
||||||
|
|
||||||
|
def set_owner(self, config):
|
||||||
|
config.owner.set(username)
|
||||||
|
|
||||||
|
|
||||||
|
class StorageServermodel(Storage):
|
||||||
|
def transform_orig_config(self, orig_config):
|
||||||
|
config_server = "v_{}".format(server_id)
|
||||||
|
return config_server, orig_config
|
||||||
|
|
||||||
|
def set_owner(self, config):
|
||||||
|
config.owner.set('servermodel_' + username)
|
||||||
|
|
||||||
|
|
||||||
|
storage_server = StorageServer()
|
||||||
|
storage_servermodel = StorageServermodel()
|
Loading…
Reference in New Issue