Compare commits

...

11 Commits

Author SHA1 Message Date
8e0fe77274 test if a source is loaded 2021-09-13 14:58:11 +02:00
8dca850683 build image is now in risotto 2021-08-28 07:34:31 +02:00
1def6e4e4c add preprocessors function 2021-05-26 20:19:08 +02:00
2ea04e708d fix 2021-05-24 22:24:15 +02:00
4853bb47f0 risotto is now a lib 2021-05-24 20:41:04 +02:00
9d4644bedf fix 2021-05-24 16:53:50 +02:00
c0244eac8c add image file 2021-05-24 16:22:39 +02:00
b6c5dccf17 staticmethod function has no self 2021-05-23 21:39:09 +02:00
ef43b197a1 better lemur integration 2021-05-22 16:37:01 +02:00
94b6563d8f import/export informations 2021-05-18 18:55:33 +02:00
88c2c168ac add v1.user.log.query message 2021-05-12 18:36:59 +02:00
10 changed files with 502 additions and 95 deletions

View File

@ -5,7 +5,7 @@ CREATE TABLE RisottoLog(
URI VARCHAR(255),
URIS VARCHAR(255),
UserLogin VARCHAR(100) NOT NULL,
Level VARCHAR(10) NOT NULL,
Status INTEGER NOT NULL,
Kwargs JSON,
Returns JSON,
StartDate timestamp DEFAULT current_timestamp,

View File

@ -65,6 +65,18 @@ if 'CELERYRISOTTO_DB_USER' in environ:
CELERYRISOTTO_DB_USER = environ['CELERYRISOTTO_DB_USER']
else:
CELERYRISOTTO_DB_USER = config.get('CELERYRISOTTO_DB_USER', None)
if 'LEMUR_DB_NAME' in environ:
LEMUR_DB_NAME = environ['LEMUR_DB_NAME']
else:
LEMUR_DB_NAME = config.get('LEMUR_DB_NAME', None)
if 'LEMUR_DB_PASSWORD' in environ:
LEMUR_DB_PASSWORD = environ['LEMUR_DB_PASSWORD']
else:
LEMUR_DB_PASSWORD = config.get('LEMUR_DB_PASSWORD', None)
if 'LEMUR_DB_USER' in environ:
LEMUR_DB_USER = environ['LEMUR_DB_USER']
else:
LEMUR_DB_USER = config.get('LEMUR_DB_USER', None)
if 'DB_ADDRESS' in environ:
DB_ADDRESS = environ['DB_ADDRESS']
else:
@ -101,12 +113,12 @@ if 'PASSWORD_ADMIN_EMAIL' in environ:
PASSWORD_ADMIN_EMAIL = environ['PASSWORD_ADMIN_EMAIL']
else:
# this parameter is mandatory
PASSWORD_ADMIN_EMAIL = config['PASSWORD_ADMIN_EMAIL']
PASSWORD_ADMIN_EMAIL = config.get('PASSWORD_ADMIN_EMAIL', 'XXX')
if 'PASSWORD_ADMIN_PASSWORD' in environ:
PASSWORD_ADMIN_PASSWORD = environ['PASSWORD_ADMIN_PASSWORD']
else:
# this parameter is mandatory
PASSWORD_ADMIN_PASSWORD = config['PASSWORD_ADMIN_PASSWORD']
PASSWORD_ADMIN_PASSWORD = config.get('PASSWORD_ADMIN_PASSWORD', 'XXX')
if 'PASSWORD_DEVICE_IDENTIFIER' in environ:
PASSWORD_DEVICE_IDENTIFIER = environ['PASSWORD_DEVICE_IDENTIFIER']
else:
@ -123,11 +135,11 @@ else:
if 'PKI_ADMIN_PASSWORD' in environ:
PKI_ADMIN_PASSWORD = environ['PKI_ADMIN_PASSWORD']
else:
PKI_ADMIN_PASSWORD = config['PKI_ADMIN_PASSWORD']
PKI_ADMIN_PASSWORD = config.get('PKI_ADMIN_PASSWORD', 'XXX')
if 'PKI_ADMIN_EMAIL' in environ:
PKI_ADMIN_EMAIL = environ['PKI_ADMIN_EMAIL']
else:
PKI_ADMIN_EMAIL = config['PKI_ADMIN_EMAIL']
PKI_ADMIN_EMAIL = config.get('PKI_ADMIN_EMAIL', 'XXX')
if 'PKI_URL' in environ:
PKI_URL = environ['PKI_URL']
else:
@ -141,7 +153,8 @@ def dsn_factory(database, user, password, address=DB_ADDRESS):
_config = {'database': {'dsn': dsn_factory(RISOTTO_DB_NAME, RISOTTO_DB_USER, RISOTTO_DB_PASSWORD),
'tiramisu_dsn': dsn_factory(TIRAMISU_DB_NAME, TIRAMISU_DB_USER, TIRAMISU_DB_PASSWORD),
'celery_dsn': dsn_factory(CELERYRISOTTO_DB_NAME, CELERYRISOTTO_DB_USER, CELERYRISOTTO_DB_PASSWORD)
'celery_dsn': dsn_factory(CELERYRISOTTO_DB_NAME, CELERYRISOTTO_DB_USER, CELERYRISOTTO_DB_PASSWORD),
'lemur_dns': dsn_factory(LEMUR_DB_NAME, LEMUR_DB_USER, LEMUR_DB_PASSWORD),
},
'http_server': {'port': RISOTTO_PORT,
'default_user': DEFAULT_USER,

View File

@ -12,7 +12,7 @@ except:
from .config import get_config
from .utils import _, tiramisu_display_name
from .logger import log
from .dispatcher import dispatcher
from .dispatcher import get_dispatcher
from .context import Context
@ -25,7 +25,7 @@ class Controller:
def __init__(self,
test: bool,
) -> None:
pass
self.dispatcher = get_dispatcher()
async def call(self,
uri: str,
@ -42,11 +42,11 @@ class Controller:
module = message.split('.', 1)[0]
if current_module != module:
raise ValueError(_(f'cannot call to external module ("{module}") to the URI "{uri}" from "{current_module}"'))
return await dispatcher.call(version,
message,
risotto_context,
**kwargs,
)
return await self.dispatcher.call(version,
message,
risotto_context,
**kwargs,
)
async def publish(self,
uri: str,
@ -58,20 +58,19 @@ class Controller:
if args:
raise ValueError(_(f'the URI "{uri}" can only be published with keyword arguments'))
version, message = uri.split('.', 1)
await dispatcher.publish(version,
message,
risotto_context,
**kwargs,
)
await self.dispatcher.publish(version,
message,
risotto_context,
**kwargs,
)
@staticmethod
async def check_role(self,
uri: str,
username: str,
**kwargs: dict,
) -> None:
# create a new config
async with await Config(dispatcher.option) as config:
async with await Config(self.dispatcher.option) as config:
await config.property.read_write()
await config.option('message').value.set(uri)
subconfig = config.option(uri)
@ -84,10 +83,10 @@ class Controller:
raise ValueError(_(f'unknown parameter in "{uri}": "{key}"'))
except ValueOptionError as err:
raise ValueError(_(f'invalid parameter in "{uri}": {err}'))
await dispatcher.check_role(subconfig,
username,
uri,
)
await self.dispatcher.check_role(subconfig,
username,
uri,
)
async def on_join(self,
risotto_context,
@ -99,20 +98,23 @@ class TiramisuController(Controller):
def __init__(self,
test: bool,
) -> None:
self.source_imported = None
if not 'dataset_name' in vars(self):
raise Exception(f'please specify "dataset_name" to "{self.__class__.__name__}"')
self.tiramisu_cache_root_path = join(get_config()['cache']['root_path'], self.dataset_name)
super().__init__(test)
self.internal_source_name = get_config()['servermodel']['internal_source']
if not test:
db_conf = get_config()['database']['tiramisu_dsn']
self.save_storage = Storage(engine='postgres')
self.save_storage.setting(dsn=db_conf)
if self.dataset_name != 'servermodel':
self.optiondescription = None
dispatcher.set_function('v1.setting.dataset.updated',
None,
TiramisuController.dataset_updated,
self.__class__.__module__,
)
self.dispatcher.set_function('v1.setting.dataset.updated',
None,
TiramisuController.dataset_updated,
self.__class__.__module__,
)
async def on_join(self,
risotto_context: Context,
@ -130,6 +132,12 @@ class TiramisuController(Controller):
risotto_context: Context,
) -> None:
sources = await self.get_sources(risotto_context)
source_imported = sources != [self.internal_source_name]
if source_imported and self.source_imported is False:
await self.load_datas(risotto_context)
self.source_imported = source_imported
if not self.source_imported:
return
self._aggregate_tiramisu_funcs(sources)
self._convert_dictionaries_to_tiramisu(sources)
@ -175,7 +183,9 @@ except:
funcs.write(fh.read())
funcs.write(b'\n')
def _convert_dictionaries_to_tiramisu(self, sources: list) -> None:
def _convert_dictionaries_to_tiramisu(self,
sources: list,
) -> None:
funcs_file = join(self.tiramisu_cache_root_path, 'funcs.py')
tiramisu_file = join(self.tiramisu_cache_root_path, 'tiramisu.py')
dictionaries_dir = join(self.tiramisu_cache_root_path, 'dictionaries')
@ -309,6 +319,7 @@ except:
await config_std.property.read_only()
# copy informations from 'to deploy' configuration to configuration
await config.information.importation(await config_std.information.exportation())
await config.value.importation(await config_std.value.exportation())
await config.permissive.importation(await config_std.permissive.exportation())
await config.property.importation(await config_std.property.exportation())

View File

@ -18,6 +18,9 @@ from .context import Context
from . import register
DISPATCHER = None
class CallDispatcher:
async def valid_call_returns(self,
risotto_context: Context,
@ -42,14 +45,21 @@ class CallDispatcher:
for ret in returns:
async with await Config(response, display_name=lambda self, dyn_name, suffix: self.impl_getname()) as config:
await config.property.read_write()
key = None
try:
for key, value in ret.items():
await config.option(key).value.set(value)
except AttributeError:
err = _(f'function {module_name}.{function_name} return the unknown parameter "{key}" for the uri "{risotto_context.version}.{risotto_context.message}"')
except AttributeError as err:
if key is not None:
err = _(f'function {module_name}.{function_name} return the unknown parameter "{key}" for the uri "{risotto_context.version}.{risotto_context.message}"')
else:
err = _(f'function {module_name}.{function_name} return unconsistency data "{err}" for the uri "{risotto_context.version}.{risotto_context.message}"')
raise CallError(err)
except ValueError as err:
err = _(f'function {module_name}.{function_name} return the invalid parameter "{key}" for the uri "{risotto_context.version}.{risotto_context.message}": {err}')
if key is not None:
err = _(f'function {module_name}.{function_name} return the invalid parameter "{key}" for the uri "{risotto_context.version}.{risotto_context.message}": {err}')
else:
err = _(f'function {module_name}.{function_name} return unconsistency error for the uri "{risotto_context.version}.{risotto_context.message}": {err}')
raise CallError(err)
await config.property.read_only()
mandatories = await config.value.mandatory()
@ -468,6 +478,8 @@ class Dispatcher(register.RegisterDispatcher,
)
# notification
if function_obj.get('notification'):
if returns is None:
raise Exception(_(f'function "{function_obj["full_module_name"]}.{function_obj["function"].__name__}" must returns something for {function_obj["notification"]}!'))
notif_version, notif_message = function_obj['notification'].split('.', 1)
if not isinstance(returns, list):
send_returns = [returns]
@ -483,5 +495,9 @@ class Dispatcher(register.RegisterDispatcher,
return returns
dispatcher = Dispatcher()
register.dispatcher = dispatcher
def get_dispatcher():
global DISPATCHER
if DISPATCHER is None:
DISPATCHER = Dispatcher()
register.dispatcher = DISPATCHER
return DISPATCHER

View File

@ -7,7 +7,7 @@ except:
from tiramisu import Config, default_storage
from .dispatcher import dispatcher
from .dispatcher import get_dispatcher
from .utils import _
from .context import Context
from .error import CallError, NotAllowedError, RegistrationError
@ -23,9 +23,13 @@ extra_statics = {}
def create_context(request):
risotto_context = Context()
risotto_context.username = request.match_info.get('username',
get_config()['http_server']['default_user'],
)
if 'username' in dict(request.match_info):
username = request.match_info['username']
elif 'username' in request.headers:
username = request.headers['username']
else:
username = get_config()['http_server']['default_user']
risotto_context.username = username
return risotto_context
@ -66,6 +70,7 @@ class extra_route_handler:
if function_name != 'risotto.http':
risotto_module_name, submodule_name = function_name.split('.', 2)[:-1]
module_name = risotto_module_name.split('_')[-1]
dispatcher = get_dispatcher()
kwargs['self'] = dispatcher.injected_self[module_name + '.' + submodule_name]
try:
returns = await cls.function(**kwargs)
@ -89,6 +94,7 @@ async def handle(request):
risotto_context = create_context(request)
kwargs = await request.json()
try:
dispatcher = get_dispatcher()
pattern = dispatcher.messages[version][message]['pattern']
if pattern == 'rpc':
method = dispatcher.call
@ -138,6 +144,7 @@ async def api(request,
# check all URI that have an associated role
# all URI without role is concidered has a private URI
uris = []
dispatcher = get_dispatcher()
async with dispatcher.pool.acquire() as connection:
async with connection.transaction():
# Check role with ACL
@ -163,6 +170,7 @@ async def get_app(loop):
""" build all routes
"""
global extra_routes, extra_statics
dispatcher = get_dispatcher()
services.link_to_dispatcher(dispatcher)
app = Application(loop=loop)
routes = []

378
src/risotto/image.py Normal file
View File

@ -0,0 +1,378 @@
from os import listdir, walk, makedirs
from os.path import isfile, isdir, join, dirname
from yaml import load, SafeLoader
from json import load as jload, dump as jdump
from time import time
from shutil import copy2, rmtree, move
from hashlib import sha512
from subprocess import Popen
from rougail import RougailConvert, RougailConfig, RougailUpgrade
try:
from tiramisu3 import Config
except:
from tiramisu import Config
from .utils import _
DATASET_PATH = '/usr/share/risotto/'
TMP_DIRECTORY = '/tmp'
PACKER_TMP_DIRECTORY = join(TMP_DIRECTORY, 'packer')
PACKER_FILE_NAME = 'recipe.json'
IMAGES_DIRECTORY = join(TMP_DIRECTORY, 'images')
FUNCTIONS = b"""try:
from tiramisu3 import valid_network_netmask, valid_ip_netmask, valid_broadcast, valid_in_network, valid_not_equal as valid_differ, valid_not_equal, calc_value
except:
from tiramisu import valid_network_netmask, valid_ip_netmask, valid_broadcast, valid_in_network, valid_not_equal as valid_differ, valid_not_equal, calc_value
# =============================================================
# fork of risotto-setting/src/risotto_setting/config/config.py
def get_password(**kwargs):
return 'password'
def get_ip(**kwargs):
return '1.1.1.1'
def get_chain(**kwargs):
return 'chain'
def get_certificates(**kwargs):
return []
def get_certificate(**kwargs):
return 'certificate'
def get_private_key(**kwargs):
return 'private_key'
def get_linked_configuration(**kwargs):
if 'test' in kwargs and kwargs['test']:
return kwargs['test'][0]
return 'configuration'
def zone_information(**kwargs):
return 'zone'
# =============================================================
"""
class Images:
def __init__(self,
image_dir: str=None,
tmp_dir: str=None,
):
if image_dir is None:
image_dir = IMAGES_DIRECTORY
self.image_dir = image_dir
if isdir(self.image_dir):
rmtree(self.image_dir)
if tmp_dir is None:
tmp_dir = PACKER_TMP_DIRECTORY
self.tmp_dir = tmp_dir
self.load_applications()
def load_applications(self) -> None:
self.build_images = []
self.applications = {}
for distrib in listdir(join(DATASET_PATH, 'seed')):
distrib_dir = join(DATASET_PATH, 'seed', distrib, 'applicationservice')
if not isdir(distrib_dir):
continue
for release in listdir(distrib_dir):
release_dir = join(distrib_dir, release)
if not isdir(release_dir):
continue
for applicationservice in listdir(release_dir):
applicationservice_dir = join(release_dir, applicationservice)
if not isdir(applicationservice_dir):
continue
if applicationservice in self.applications:
raise Exception('multi applicationservice')
with open(join(applicationservice_dir, 'applicationservice.yml')) as yaml:
app = load(yaml, Loader=SafeLoader)
self.applications[applicationservice] = {'path': applicationservice_dir,
'yml': app,
}
if 'service' in app and app['service']:
self.build_images.append(applicationservice)
def calc_depends(self,
dependencies: list,
appname,
key_is_name=False,
):
app = self.applications[appname]['yml']
if not 'depends' in app or not app['depends']:
return
for dependency in app['depends']:
if key_is_name:
key = appname
else:
key = self.applications[dependency]['path']
if key not in dependencies:
dependencies.insert(0, key)
self.calc_depends(dependencies, dependency, key_is_name)
def list_oses(self):
oses = set()
for build in self.build_images:
dependencies = [build]
self.calc_depends(dependencies, build, True)
for dependency in dependencies:
if isdir(join(self.applications[dependency]['path'], 'packer', 'os')):
oses.add(dependency)
break
for os in oses:
dependencies = [self.applications[os]['path']]
self.calc_depends(dependencies, os)
yield os, dependencies
def list_images(self):
for build in self.build_images:
dependencies = [self.applications[build]['path']]
self.calc_depends(dependencies, build)
yield build, dependencies
async def build(self) -> None:
if isdir(self.tmp_dir):
rmtree(self.tmp_dir)
image = Image(self.image_dir,
self.tmp_dir,
)
print(_('Build OSes'))
if not isdir(join(self.image_dir, 'os')):
makedirs(join(self.image_dir, 'os'))
for application, dependencies_path in self.list_oses():
print(_(f'Build OS {application}'))
await image.build_os(application,
dependencies_path,
)
print(_('Build images'))
for application, dependencies_path in self.list_images():
print(_(f'Build image {application}'))
await image.build_image(application,
dependencies_path,
)
class Image:
def __init__(self,
image_dir: str,
tmp_dir: str,
):
self.image_dir = image_dir
self.tmp_dir = tmp_dir
@staticmethod
def copy_files(dependencies_path: list,
dst_path: str,
element: str,
) -> None:
for dependency_path in dependencies_path:
src_path = join(dependency_path,
'packer',
element,
)
root_len = len(src_path) + 1
for dir_name, subdir_names, filenames in walk(src_path):
subdir = join(dst_path, dir_name[root_len:])
if not isdir(subdir):
makedirs(subdir)
for filename in filenames:
path = join(dir_name, filename)
sub_dst_path = join(subdir, filename)
if isfile(sub_dst_path):
raise Exception(_(f'Try to copy {sub_dst_path} which is already exists'))
copy2(path, sub_dst_path)
async def load_configuration(self,
dependencies_path: list,
packer_tmp_directory: str,
) -> dict:
config = RougailConfig.copy()
dictionaries = [join(dependency_path, 'dictionaries') for dependency_path in dependencies_path if isdir(join(dependency_path, 'dictionaries'))]
upgrade = RougailUpgrade()
dest_dictionaries = join(packer_tmp_directory, 'dictionaries')
makedirs(dest_dictionaries)
dest_dictionaries_extras = join(packer_tmp_directory, 'dictionaries_extras')
makedirs(dest_dictionaries_extras)
for dependency_path in dependencies_path:
dictionaries_dir = join(dependency_path, 'dictionaries')
if isdir(dictionaries_dir):
upgrade.load_xml_from_folders(dictionaries_dir,
dest_dictionaries,
RougailConfig['variable_namespace'],
)
extra_dir = join(dependency_path, 'extras', 'packer')
if isdir(extra_dir):
upgrade.load_xml_from_folders(extra_dir,
dest_dictionaries_extras,
'packer',
)
config['dictionaries_dir'] = [dest_dictionaries]
config['extra_dictionaries'] = {'packer': [dest_dictionaries_extras]}
self.merge_funcs(config, dependencies_path, packer_tmp_directory)
packer_configuration = await self.get_packer_information(config, packer_tmp_directory)
return packer_configuration
@staticmethod
def merge_funcs(config: RougailConfig,
dependencies_path: list,
packer_tmp_directory: str,
):
functions = FUNCTIONS
for dependency_path in dependencies_path:
funcs_dir = join(dependency_path, 'funcs')
if not isdir(funcs_dir):
continue
for func in listdir(funcs_dir):
with open(join(funcs_dir, func), 'rb') as fh:
functions += fh.read()
func_name = join(packer_tmp_directory, 'func.py')
with open(func_name, 'wb') as fh:
fh.write(functions)
config['functions_file'] = func_name
@staticmethod
async def get_packer_information(config: RougailConfig,
packer_tmp_directory: str,
) -> dict:
eolobj = RougailConvert(config)
xml = eolobj.save(join(packer_tmp_directory, 'tiramisu.py'))
optiondescription = {}
exec(xml, None, optiondescription)
config = await Config(optiondescription['option_0'])
return await config.option('packer').value.dict(leader_to_list=True, flatten=True)
@staticmethod
def do_recipe_checksum(path: str,
) -> str:
files = []
root_len = len(path) + 1
for dir_name, subdir_names, filenames in walk(path):
subpath = dir_name[root_len:]
for filename in filenames:
with open(join(dir_name, filename), 'rb') as fh:
ctl_sum = sha512(fh.read()).hexdigest()
abs_path = join(subpath, filename)
files.append(f'{abs_path}/{ctl_sum}')
files.sort()
print(files, sha512('\n'.join(files).encode()).hexdigest())
return sha512('\n'.join(files).encode()).hexdigest()
def get_tmp_directory(self,
application: str,
) -> str:
return join(self.tmp_dir,
application + '_' + str(time()),
)
def get_os_filename(self,
packer_configuration: dict,
) -> str:
return join(self.image_dir,
'os',
packer_configuration['os_name'] + '_' + packer_configuration['os_version'] + '.img',
)
def get_image_filename(self,
recipe_checksum: str,
) -> str:
return join(self.image_dir,
f'{recipe_checksum}.img',
)
async def build_os(self,
application: str,
dependencies_path: list,
) -> None:
packer_tmp_directory = self.get_tmp_directory(application)
packer_configuration = await self.load_configuration(dependencies_path, packer_tmp_directory)
packer_dst_os_filename = self.get_os_filename(packer_configuration)
self.copy_files(dependencies_path,
packer_tmp_directory,
'os',
)
packer_configuration['tmp_directory'] = packer_tmp_directory
recipe = {'variables': packer_configuration}
self.build(packer_dst_os_filename,
packer_tmp_directory,
recipe,
)
async def build_image(self,
application: str,
dependencies_path: list,
) -> None:
packer_tmp_directory = self.get_tmp_directory(application)
makedirs(packer_tmp_directory)
self.copy_files(dependencies_path,
packer_tmp_directory,
'image',
)
recipe_checksum = self.do_recipe_checksum(packer_tmp_directory)
packer_dst_filename = self.get_image_filename(recipe_checksum)
packer_configuration = await self.load_configuration(dependencies_path, packer_tmp_directory)
packer_dst_os_filename = join(self.image_dir,
'os',
packer_configuration['os_name'] + '_' + packer_configuration['os_version'] + '.img',
)
packer_configuration['tmp_directory'] = packer_tmp_directory
recipe = {'variables': packer_configuration}
recipe['variables']['iso_url'] = packer_dst_os_filename
self.build(packer_dst_filename,
packer_tmp_directory,
recipe,
f'{packer_dst_os_filename}.sha256',
)
@staticmethod
def build(packer_dst_filename: str,
tmp_directory: str,
recipe: dict,
sha_file: str=None,
) -> None:
packer_filename = join(tmp_directory, PACKER_FILE_NAME)
if sha_file is not None:
with open(sha_file, 'r') as fh:
sha256 = fh.read().split(' ', 1)[0]
recipe['variables']['iso_checksum'] = sha256
with open(packer_filename, 'r') as recipe_fd:
for key, value in jload(recipe_fd).items():
recipe[key] = value
with open(packer_filename, 'w') as recipe_fd:
jdump(recipe, recipe_fd, indent=2)
preprocessors = join(tmp_directory, 'preprocessors')
if isfile(preprocessors):
proc = Popen([preprocessors],
#stdout=PIPE,
#stderr=PIPE,
cwd=tmp_directory,
)
proc.wait()
if proc.returncode:
raise Exception(_(f'error when executing {preprocessors}'))
proc = Popen(['packer', 'build', packer_filename],
#stdout=PIPE,
#stderr=PIPE,
cwd=tmp_directory,
)
proc.wait()
if proc.returncode:
raise Exception(_(f'cannot build {packer_dst_filename} with {packer_filename}'))
if not isdir(dirname(packer_dst_filename)):
makedirs(dirname(packer_dst_filename))
move(join(tmp_directory, 'image.img'), packer_dst_filename)
move(join(tmp_directory, 'image.sha256'), f'{packer_dst_filename}.sha256')
print(_(f'Image {packer_dst_filename} created'))
rmtree(tmp_directory)

View File

@ -11,6 +11,9 @@ from .config import get_config
database_lock = Lock()
LEVELS = ['Error', 'Info', 'Success', 'Started', 'Failure']
class Logger:
""" An object to manager log
"""
@ -39,9 +42,9 @@ class Logger:
) -> None:
uri = self._get_last_uri(risotto_context)
uris = " ".join(risotto_context.paths)
insert = 'INSERT INTO RisottoLog(Msg, URI, URIS, UserLogin, Level'
insert = 'INSERT INTO RisottoLog(Msg, URI, URIS, UserLogin, Status'
values = 'VALUES($1,$2,$3,$4,$5'
args = [msg, uri, uris, risotto_context.username, level]
args = [msg, uri, uris, risotto_context.username, LEVELS.index(level)]
if kwargs:
insert += ', Kwargs'
values += ',$6'
@ -67,40 +70,6 @@ class Logger:
except UndefinedTableError as err:
raise Exception(_(f'cannot access to database ({err}), was the database really created?'))
async def query(self,
risotto_context: Context,
context_id: int,
uri: Optional[str],
) -> list:
sql = '''SELECT Msg as msg, URI as uri_name, URIS as uris, UserLogin as user_login, Level as level, Kwargs as kwargs, Returns as returns, StartDate as start_date, StopDate as stop_date
FROM RisottoLog
WHERE UserLogin = $1 AND (LogId = $2 OR ContextId = $2)
'''
args = [sql, risotto_context.username, context_id]
if uri is not None:
sql += ' AND URI = $3'
args.append(uri)
ret = []
async with database_lock:
connection = await self.get_connection(risotto_context)
for row in await connection.fetch(*args):
d = {}
for key, value in row.items():
if key in ['kwargs', 'returns']:
if isinstance(value, dict):
pass
elif not value:
value = {}
else:
value = loads(value)
if key == 'uris':
value = value.split(' ')
elif key in ['start_date', 'stop_date']:
value = str(value)
d[key] = value
ret.append(d)
return ret
def _get_last_uri(self,
risotto_context: Context,
) -> str:
@ -171,7 +140,7 @@ class Logger:
print(_(f'{risotto_context.username}: START{context}:{paths_msg}: {msg}'))
await self.insert(msg,
risotto_context,
'Start',
'Started',
arguments,
start=True,
)
@ -185,11 +154,11 @@ class Logger:
print(_(f'{risotto_context.username}: SUCCESS({risotto_context.context_id}):{paths_msg}'))
sql = """UPDATE RisottoLog
SET StopDate = $2,
Level = 'SUCCESS'
Status = $3
"""
args = [datetime.now()]
args = [datetime.now(), LEVELS.index('Success')]
if returns:
sql += """, Returns = $3
sql += """, Returns = $4
"""
args.append(dumps(returns))
sql += """WHERE LogId = $1
@ -214,7 +183,7 @@ class Logger:
print(_(f'{risotto_context.username}: FAILED({risotto_context.context_id}):{paths_msg}: {err}'))
sql = """UPDATE RisottoLog
SET StopDate = $2,
Level = 'FAILED',
Status = $4,
Msg = $3
WHERE LogId = $1
"""
@ -223,7 +192,8 @@ class Logger:
await connection.execute(sql,
risotto_context.start_id,
datetime.now(),
err,
err[:254],
LEVELS.index('Failure'),
)
async def info(self,

View File

@ -19,8 +19,8 @@ from .utils import _
MESSAGE_ROOT_PATH = get_config()['global']['message_root_path']
groups.addgroup('message')
MESSAGE_TRANSLATION = translation('risotto-message', join(MESSAGE_ROOT_PATH, '..', 'locale')).gettext
CUSTOMTYPES = None
MESSAGE_TRANSLATION = None
class DictOption(Option):
@ -313,6 +313,7 @@ class CustomParam:
'string': 'String',
'number': 'Number',
'object': 'Dict',
'any': 'Any',
'array': 'Array',
'file': 'File',
'float': 'Float'}
@ -448,6 +449,7 @@ def _get_option(name,
'reverse_condition': ParamValue(True)}),
calc_value_property_help))
props.append('notunique')
description = arg.description.strip().rstrip()
kwargs = {'name': name,
'doc': _get_description(description, name),
@ -523,6 +525,7 @@ def _parse_responses(message_def,
'Number': IntOption,
'Boolean': BoolOption,
'Dict': DictOption,
'Any': AnyOption,
'Float': FloatOption,
# FIXME
'File': StrOption}.get(type_)
@ -530,8 +533,9 @@ def _parse_responses(message_def,
raise Exception(f'unknown param type {obj.type} in responses of message {message_def.message}')
if hasattr(obj, 'default'):
kwargs['default'] = obj.default
kwargs['properties'] = ('notunique',)
else:
kwargs['properties'] = ('mandatory',)
kwargs['properties'] = ('mandatory', 'notunique')
options.append(option(**kwargs))
od = OptionDescription(uri,
message_def.response.description,
@ -589,6 +593,11 @@ def get_messages(current_module_names,
):
"""generate description from yml files
"""
global MESSAGE_TRANSLATION, CUSTOMTYPES
if MESSAGE_TRANSLATION is None:
MESSAGE_TRANSLATION = translation('risotto-message', join(MESSAGE_ROOT_PATH, '..', 'locale')).gettext
if CUSTOMTYPES is None:
CUSTOMTYPES = load_customtypes()
optiondescriptions = {}
optiondescriptions_info = {}
messages = list(list_messages(uris,
@ -600,7 +609,7 @@ def get_messages(current_module_names,
select_option = ChoiceOption('message',
'Nom du message.',
tuple(messages),
properties=frozenset(['mandatory', 'positional']))
properties=frozenset(['mandatory', 'positional', 'notunique']))
for uri in messages:
message_def = get_message(uri,
current_module_names,
@ -628,6 +637,3 @@ def get_messages(current_module_names,
optiondescriptions,
)
return optiondescriptions_info, root
CUSTOMTYPES = load_customtypes()

View File

@ -83,12 +83,16 @@ def register(uris: str,
uris = [uris]
def decorator(function):
for uri in uris:
dispatcher.set_function(uri,
notification,
function,
function.__module__
)
try:
for uri in uris:
dispatcher.set_function(uri,
notification,
function,
function.__module__
)
except NameError:
# if you when register uri, please use get_dispatcher before registered uri
pass
return decorator
@ -107,6 +111,7 @@ class RegisterDispatcher:
version = obj['version']
if version not in self.messages:
self.messages[version] = {}
obj['message'] = tiramisu_message
self.messages[version][tiramisu_message] = obj
def get_function_args(self,

View File

@ -23,5 +23,5 @@ def tiramisu_display_name(kls,
if suffix:
doc += suffix
if name != doc:
name += f' ({doc})'
name += f'" "{doc}'
return name