Compare commits

..

28 Commits

Author SHA1 Message Date
94168554f2 role support 2019-12-27 15:09:38 +01:00
50aa8019ab update tests 2019-12-26 15:33:51 +01:00
1d25d3a582 tiramisu is now async 2019-12-26 11:38:31 +01:00
41af2512b5 extra support 2019-12-22 18:41:35 +01:00
4de9bde691 server>template 2019-12-20 10:58:12 +01:00
6b8a88e103 Temporary fixes for internal default value 2019-12-20 10:37:27 +01:00
9335fbb16e message servermodel.get_by_id 2019-12-20 10:19:49 +01:00
78b7129605 Fixes local paths 2019-12-20 10:17:37 +01:00
b5ddefdaac Compute messages root path against project root dir in development environment 2019-12-20 10:16:12 +01:00
ddd97fb59c can create a server 2019-12-19 17:24:20 +01:00
77ed63784b really create schema 2019-12-19 15:00:24 +01:00
f7a97cf575 source>release>applicationservice>servermodel 2019-12-19 12:25:16 +01:00
a092b597f8 Modify distribution 2019-12-19 09:19:09 +01:00
e19e718e22 import servermodel 2019-12-18 17:11:42 +01:00
b006eda133 Do not raise error if folder exists 2019-12-18 10:08:11 +01:00
62ab525219 Open file in write mode 2019-12-17 17:07:58 +01:00
10969ab1e0 reworks on tests 2019-12-16 17:14:58 +01:00
f0042f2a37 Fetch releases.yml to assert if Source is valid 2019-12-16 16:35:48 +01:00
7f0411da4d local_<servermodel_name> application has to be created in internal source 2019-12-16 16:17:37 +01:00
91aac5399a Add Distribution attributute to releases 2019-12-16 15:49:20 +01:00
b4c48ebc10 add source.list, source.create and source.get messages 2019-12-16 15:13:28 +01:00
b567fd88ac WIP load servermodel with parent 2019-12-13 17:17:07 +01:00
eccc5c4098 WIP2 2019-12-13 16:42:10 +01:00
a7934e37d7 WIP 2019-12-13 13:55:30 +01:00
7dc6ce7845 L’extension doit être créée pour la base de données risotto 2019-12-09 16:28:47 +01:00
84850182f6 Activation de l’extension hstore 2019-12-09 16:22:13 +01:00
dcaf7da3bc Test d’un service utilisant une base de données. 2019-12-09 14:28:13 +01:00
8c91e01a2b Vocabulary 2019-12-09 14:28:13 +01:00
85 changed files with 2335 additions and 1151 deletions

View File

@ -11,20 +11,27 @@ Accéder à un message :
wget http://localhost:8080/v1/config.session.server.start wget http://localhost:8080/v1/config.session.server.start
``` ```
[Documentation WIKI cadoles](https://doku.cadoles.com/dokuwiki/doku.php?id=documentation:risotto) n'est qu'un liens vers la doc officiel (pour le moment) [dans la forge](https://forge.cadoles.com/Infra/eole-risotto/wiki) Démarrer un serveur LemonLDAP de test
```
docker pull coudot/lemonldap-ng
echo "127.0.0.1 auth.example.com manager.example.com test1.example.com test2.example.com" >> /etc/hosts
docker run -d --add-host reload.example.com:127.0.0.1 -p 80:80 coudot/lemonldap-ng
```
**branche :** Démarrer un serveur postgresql de test
```
podman pull docker.io/library/postgres:11-alpine
podman run -dt -p 5432:5432 postgres:11-alpine
master psql -U postgres -h localhost -c "CREATE ROLE risotto WITH LOGIN PASSWORD 'risotto';"
psql -U postgres -h localhost -c "CREATE DATABASE risotto;"
psql -U postgres -h localhost -c "GRANT ALL ON DATABASE risotto TO risotto;"
psql -U postgres -h localhost -c "CREATE EXTENSION hstore;" risotto
```
develop Gestion de la base de données avec Sqitch
dist/risotto/risotto-2.7.1/develop : pour faire des paquets ```
cpanm --quiet --notest App::Sqitch
dist/risotto/risotto-2.8.0/develop : pour faire des paquets sqitch init risotto --uri https://forge.cadoles.com/Infra/risotto --engine pg
```
docker : n'est pas a jour
feature/service_servermodel :
jwt :

View File

@ -0,0 +1,35 @@
---
uri: applicationservice.create
description: |
Créé un service applicatif.
pattern: rpc
public: true
parameters:
applicationservice_name:
type: String
shortarg: n
description: |
Nom du service applicatif à créer.
applicationservice_description:
type: String
shortarg: d
description: |
Description du service applicatif à créer.
applicationservice_dependencies:
type: '[]Number'
shortarg: a
description: ID des services applicatif donc dépendant le service applicatif.
default: []
release_id:
type: Number
shortarg: r
description: |
Identifiant de la version associée au service applicatif.
response:
type: ApplicationService
description: Informations sur le service applicatif créé.

View File

@ -0,0 +1,22 @@
uri: applicationservice.dataset.updated
description: |
Les services applicatifs sont mis à jour.
pattern: rpc
public: true
parameters:
source_name:
type: String
shortarg: s
description: Nom de la source.
release_distribution:
type: String
shortarg: r
description: Distribution de la source.
response:
type: ReturnStatus
description: Code de retour sur linjection des services applicatifs en base.

View File

@ -0,0 +1,25 @@
---
uri: applicationservice.describe
description: |
Décrit un service applicatif.
pattern: rpc
public: true
parameters:
applicationservice_name:
type: String
shortarg: n
description: |
Nom du service applicatif à créer.
release_id:
type: Number
shortarg: r
description: |
Identifiant de la version associée au service applicatif.
response:
type: ApplicationService
description: Informations sur le service applicatif.

View File

@ -0,0 +1,36 @@
---
uri: applicationservice.get_by_id
description: |
Retourne un service applicatif suivant l'identifiant.
pattern: rpc
public: true
parameters:
applicationservice_id:
type: Number
shortarg: i
description: |
ID du service applicatif à créer.
response:
type: ApplicationService
description: Informations sur le service applicatif créé.
errors:
- uri: servermodel.create.error.database_not_available
- uri: servermodel.create.error.duplicate_servermodel
- uri: servermodel.create.error.invalid_parentservermodel_id
- uri: servermodel.create.error.invalid_source_id
- uri: servermodel.create.error.unknown_parentservermodel_id
- uri: servermodel.create.error.unknown_source_id
- uri: servermodel.create.error.servermodelname_not_provided
related:
- servermodel.list
- servermodel.describe
- servermodel.update
- servermodel.delete
- servermodel.event

View File

@ -4,14 +4,10 @@ uri: config.configuration.server.deploy
description: | description: |
Déployer la configuration d'un serveur. Déployer la configuration d'un serveur.
sampleuse: ~
pattern: event pattern: event
public: false public: false
domain: config-domain
parameters: parameters:
server_id: server_id:
type: Number type: Number

View File

@ -4,14 +4,10 @@ uri: config.configuration.server.get
description: | description: |
Récupère le contenu de la configuration. Récupère le contenu de la configuration.
sampleuse: ~
pattern: rpc pattern: rpc
public: false public: false
domain: config-domain
parameters: parameters:
server_id: server_id:
type: Number type: Number

View File

@ -4,14 +4,10 @@ uri: config.configuration.server.updated
description: | description: |
Une configuration de serveur a été mise à jour. Une configuration de serveur a été mise à jour.
sampleuse: ~
pattern: event pattern: event
public: false public: false
domain: config-domain
parameters: parameters:
server_id: server_id:
type: Number type: Number

View File

@ -1,54 +0,0 @@
---
uri: server.create
description: |
Crée un serveur.
sampleuse: |
zephir-client server.create -s MonJoliServeur -d "un bien joli serveur" -m 1 -p MyPassPhrase
pattern: rpc
public: true
domain: server-domain
parameters:
servername:
type: String
shortarg: s
description: |
Nom du serveur.
serverdescription:
type: String
shortarg: d
description: |
Description du serveur.
servermodelid:
type: Number
shortarg: m
ref: Servermodel.ServermodelId
description: |
Identifiant du modèle de serveur.
serverpassphrase:
type: String
shortarg: p
description: |
Phrase secrète de la clef privé.
response:
type: Server
description: Description du serveur créé.
errors:
- uri: server.error.database-not-available
- uri: server.error.db-connection
- uri: server.error.invalid-servermodel-id
- uri: server.error.unknown-servermodel-id
- uri: server.error.servername-not-provided
related:
- server.list
- server.describe
- server.update
- server.delete

View File

@ -1,49 +0,0 @@
---
uri: server.describe
description: |
Retourne les attributs détaillés dun serveur.
sampleuse: |
zephir-client server.describe -s 1
pattern: rpc
public: true
domain: server-domain
parameters:
serverid:
type: Number
ref: Server.ServerId
description: Identifiant du serveur.
shortarg: s
configuration:
type: Boolean
description: Inclure les valeurs de configuration.
default: false
environment:
type: Boolean
description: Inclure les variables d'environement.
default: false
peering:
type: Boolean
description: Inclure la clé d'appairage.
default: false
response:
type: ServerDescribe
description: Description du serveur.
errors:
- uri: server.error.database-not-available
- uri: server.error.db-connection
- uri: server.error.invalid-server-id
- uri: server.error.unknown-server-id
related:
- server.list
- server.create
- server.update
- server.delete

View File

@ -0,0 +1,32 @@
---
uri: servermodel.describe
description: |
Retourne les attributs détaillés d'un modèle de serveur.
pattern: rpc
public: true
parameters:
servermodel_id:
type: Number
shortarg: s
description: Identifiant du modèle de serveur à récupérer.
ref: Servermodel.ServermodelId
response:
type: Servermodel
description: Description du modèle de serveur.
errors:
- uri: servermodel.describe.error.database_not_available
- uri: servermodel.describe.error.invalid_servermodel_id
- uri: servermodel.describe.error.unknown_servermodel_id
related:
- servermodel.list
- servermodel.create
- servermodel.update
- servermodel.delete
- servermodel.event

View File

@ -0,0 +1,27 @@
---
uri: server.create
description: Crée un serveur.
pattern: rpc
public: true
parameters:
server_name:
type: String
shortarg: s
description: Nom du serveur.
server_description:
type: String
shortarg: d
description: Description du serveur.
server_servermodel_id:
type: Number
shortarg: m
ref: Servermodel.ServermodelId
description: Identifiant du modèle de serveur.
response:
type: Server
description: Description du serveur créé.

View File

@ -7,8 +7,6 @@ pattern: event
public: false public: false
domain: server-domain
parameters: parameters:
type: Server type: Server
description: Description du serveur. description: Description du serveur.

View File

@ -1,8 +1,6 @@
--- ---
uri: server.deleted uri: server.deleted
sampleuse: ~
description: | description: |
Un serveur a été supprimé. Un serveur a été supprimé.
@ -10,8 +8,6 @@ pattern: event
public: false public: false
domain: server-domain
parameters: parameters:
server_id: server_id:
type: Number type: Number

View File

@ -0,0 +1,18 @@
---
uri: server.describe
description: Retourne les attributs détaillés dun serveur.
pattern: rpc
public: true
parameters:
server_name:
type: String
ref: Server.ServerName
description: Nom du serveur.
response:
type: Server
description: Description du serveur.

View File

@ -4,15 +4,10 @@ uri: server.list
description: | description: |
Liste les serveurs disponibles. Liste les serveurs disponibles.
sampleuse: |
zephir-client server.list
public: true public: true
pattern: rpc pattern: rpc
domain: server-domain
response: response:
type: '[]Server' type: '[]Server'
description: Retourne la liste des serveurs. description: Retourne la liste des serveurs.

View File

@ -7,8 +7,6 @@ pattern: event
public: false public: false
domain: servermodel-domain
parameters: parameters:
type: Servermodel type: Servermodel
description: Informations sur les modèles de serveur créés. description: Informations sur les modèles de serveur créés.

View File

@ -0,0 +1,22 @@
uri: servermodel.dataset.updated
description: |
Initialise la table pour les modèles de serveur.
pattern: rpc
public: true
parameters:
source_name:
type: String
shortarg: s
description: Nom de la source.
release_distribution:
type: String
shortarg: r
description: Distribution de la version.
response:
type: ReturnStatus
description: Code de retour sur linjection des modèles de serveur en base.

View File

@ -1,67 +0,0 @@
---
uri: servermodel.describe
description: |
Retourne les attributs détaillés d'un modèle de serveur.
sampleuse: |
zephir-client servermodel.describe -s 1
pattern: rpc
public: true
domain: servermodel-domain
parameters:
servermodelid:
type: Number
shortarg: s
description: Identifiant du modèle de serveur à récupérer.
ref: Servermodel.ServermodelId
inheritance:
type: Boolean
shortarg: i
description: Inclure les données héritées des modèles de serveur parents.
default: true
resolvdepends:
type: Boolean
shortarg: r
description: Résoudre les dépendances de services.
default: true
schema:
type: Boolean
shortarg: c
description: Inclure le schema de configuration (réaggrège les données provenant du datasource).
default: false
probes:
type: Boolean
shortarg: p
description: Inclure les informations sur les sondes de la configuration.
default: false
creolefuncs:
type: Boolean
shortarg: o
description: Inclure les fonctions Creole.
default: false
conffiles:
type: Boolean
shortarg: f
description: Inclure les fichier creole au format tar encodé en base64
default: false
response:
type: Servermodel
description: Description du modèle de serveur.
errors:
- uri: servermodel.describe.error.database_not_available
- uri: servermodel.describe.error.invalid_servermodel_id
- uri: servermodel.describe.error.unknown_servermodel_id
related:
- servermodel.list
- servermodel.create
- servermodel.update
- servermodel.delete
- servermodel.event

View File

@ -0,0 +1,19 @@
---
uri: servermodel.get_by_id
description: Retourne les attributs détaillés d'un modèle de serveur suivant son identifiant.
pattern: rpc
public: false
parameters:
servermodel_id:
type: Number
shortarg: s
description: Identifiant du modèle de serveur à récupérer.
ref: Servermodel.ServermodelId
response:
type: Servermodel
description: Description du modèle de serveur.

View File

@ -4,17 +4,12 @@ uri: servermodel.list
description: | description: |
Retourne la liste des modèles de serveur disponibles. Retourne la liste des modèles de serveur disponibles.
sampleuse: |
zephir-client servermodel.list
pattern: rpc pattern: rpc
public: true public: true
domain: servermodel-domain
parameters: parameters:
sourceid: source_id:
type: Number type: Number
shortarg: s shortarg: s
description: | description: |

View File

@ -7,8 +7,6 @@ pattern: event
public: false public: false
domain: servermodel-domain
parameters: parameters:
type: 'Servermodel' type: 'Servermodel'
description: Informations sur les modèles de serveur modifiés. description: Informations sur les modèles de serveur modifiés.

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
type: String type: String

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
type: String type: String

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
type: String type: String

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
response: response:
type: '[]Session' type: '[]Session'
description: | description: |

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
id: id:
type: Number type: Number

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
ref: Config.SessionId ref: Config.SessionId

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
ref: Config.SessionId ref: Config.SessionId

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
type: String type: String

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
type: String type: String

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
type: String type: String

View File

@ -7,8 +7,6 @@ pattern: rpc
public: true public: true
domain: session-domain
response: response:
type: '[]Session' type: '[]Session'
description: | description: |

View File

@ -7,8 +7,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
id: id:
type: Number type: Number

View File

@ -7,8 +7,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
ref: Config.SessionId ref: Config.SessionId

View File

@ -8,8 +8,6 @@ pattern: rpc
public: true public: true
domain: session-domain
parameters: parameters:
session_id: session_id:
ref: Config.SessionId ref: Config.SessionId

View File

@ -0,0 +1,25 @@
---
uri: source.create
description: |
Créer une source.
pattern: rpc
public: true
parameters:
source_name:
type: String
shortarg: n
description: |
Nom de la source.
source_url:
type: String
shortarg: u
description: |
URL de téléchargement de la source.
response:
type: 'Source'
description: Information sur la source.

View File

@ -0,0 +1,22 @@
uri: source.dataset.update
description: |
Initialise la table pour les versions.
pattern: rpc
public: true
parameters:
source_id:
type: Number
shortarg: s
description: ID de la source.
release_name:
type: String
shortarg: r
description: Nom de la version.
response:
type: Release
description: Informations sur la version injectée en base.

View File

@ -0,0 +1,20 @@
---
uri: source.describe
description: |
Retourne une source.
pattern: rpc
public: true
parameters:
source_name:
type: String
shortarg: n
description: |
Nom de la source.
response:
type: 'Source'
description: Information sur la source.

View File

@ -1,18 +1,13 @@
--- ---
uri: servermodel.source.list uri: source.list
description: | description: |
Retourne la liste des sources. Retourne la liste des sources.
sampleuse: |
zephir-client servermodel.source.list
pattern: rpc pattern: rpc
public: true public: true
domain: servermodel-domain
response: response:
type: '[]Dict' type: '[]Source'
description: Liste des sources disponibles. description: Liste des sources disponibles.

View File

@ -0,0 +1,29 @@
---
uri: source.release.create
description: |
Créer une version.
pattern: rpc
public: true
parameters:
source_id:
type: Number
shortarg: i
description: ID de la source.
release_name:
type: String
shortarg: n
description: |
Nom de la version.
release_distribution:
type: String
shortarg: d
description: |
Distribution de la version.
response:
type: 'Release'
description: Information sur la version.

View File

@ -0,0 +1,24 @@
---
uri: source.release.get_by_distribution
description: |
Retourne version suivant le nom de la distribution.
pattern: rpc
public: true
parameters:
source_id:
type: Number
shortarg: s
description: ID de la source.
release_distribution:
type: String
shortarg: r
description: Distribution de la version.
response:
type: 'Release'
description: La version disponibles.

View File

@ -0,0 +1,19 @@
---
uri: source.release.get_by_id
description: |
Retourne version suivant l'identifiant.
pattern: rpc
public: true
parameters:
release_id:
type: Number
shortarg: r
description: ID de la version.
response:
type: 'Release'
description: La version disponibles.

View File

@ -0,0 +1,13 @@
---
uri: source.release.list
description: |
Retourne la liste des versions.
pattern: rpc
public: true
response:
type: '[]Release'
description: Liste des versions disponibles.

View File

@ -4,21 +4,17 @@ uri: template.generate
description: | description: |
Génère et récupère les templates générés. Génère et récupère les templates générés.
sampleuse: ~
pattern: rpc pattern: rpc
public: true public: true
domain: template-domain
parameters: parameters:
server_id: server_name:
type: Number type: String
ref: Server.ServerId ref: Server.ServerName
shortarg: s shortarg: s
description: | description: |
Identifiant du serveur. Nom du serveur.
response: response:
type: Template type: Template

View File

@ -0,0 +1,28 @@
---
title: ApplicationService
type: object
description: Description d'un modèle de serveur.
properties:
applicationservice_id:
type: number
description: ID du service applicatif.
applicationservice_name:
type: string
description: Nom du service applicatif.
applicationservice_description:
type: string
description: Description du service applicatif.
applicationservice_release_id:
type: number
ref: Version.ReleaseId
description: Version du service applicatif.
applicationservice_dependencies:
type: array
items:
type: number
description: Liste des services applicatifs déclarés en dépendance de ce service applicatif.
required:
- applicationservice_id
- applicationservice_name
- applicationservice_release_id

View File

@ -0,0 +1,14 @@
---
title: ReturnStatus
type: object
description: Résultat dune commande.
properties:
retcode:
type: number
description: Code de retour de la commande.
returns:
type: string
description: Retour de la commande.
required:
- retcode
- returns

View File

@ -0,0 +1,28 @@
---
title: Release
type: object
description: Description de la version.
properties:
release_id:
type: number
description: Identifiant de la version.
release_name:
type: string
description: Le nom de la version.
release_distribution:
type: string
description: Le nom de la distribution de la version.
source_url:
type: string
description: URL de la source.
ref: Source.ReleaseId
source_name:
type: string
description: Le nom de la source.
required:
- release_id
- release_name
- release_distribution
- source_name
- source_url

View File

@ -1,44 +0,0 @@
---
title: ServerDescribe
type: object
description: Description du serveur.
properties:
serverid:
type: number
description: Identifiant du serveur.
ref: Server.ServerId
servername:
type: string
description: Nom du serveur.
serverdescription:
type: string
description: Description du serveur.
servermodelid:
type: number
description: Identifiant du modèle de serveur.
ref: Servermodel.ServermodelId
zoneid:
type: number
description: Identifiant de la zone.
ref: Zone.ZoneId
machineid:
type: number
description: Identifiant de la machine.
ref: Zone.MachineId
configuration:
type: file
description: Valeurs de configuration.
serverenvironment:
type: object
description: Variables d'environnement du serveur.
peering:
type: object
description: Clé d'appairage.
lastpeerconnection:
type: string
description: Timestamp de la dernière connexion avec le serveur.
required:
- serverid
- servername
- serverdescription
- servermodelid

View File

@ -7,33 +7,18 @@ properties:
type: number type: number
description: Identifiant du serveur. description: Identifiant du serveur.
ref: Server.ServerId ref: Server.ServerId
servername: server_name:
type: string type: string
description: Nom du serveur. description: Nom du serveur.
serverdescription: server_description:
type: string type: string
description: Description du serveur. description: Description du serveur.
servermodelid: server_servermodel_id:
type: number type: number
description: Identifiant du modèle de serveur. description: Identifiant du modèle de serveur.
ref: Servermodel.ServermodelId ref: Servermodel.ServermodelId
zoneid:
type: number
description: Identifiant de la zone.
ref: Zone.ZoneId
machineid:
type: number
description: Identifiant de la machine.
ref: Zone.MachineId
automation:
type: string
description: Moteur d'exécution.
ref: Server.automation
lastpeerconnection:
type: string
description: Timestamp de la dernière connexion avec le serveur.
required: required:
- server_id - server_id
- servername - server_name
- serverdescription - server_description
- servermodelid - server_servermodel_id

View File

@ -3,16 +3,16 @@ title: ServermodelSource
type: object type: object
description: Description de la source. description: Description de la source.
properties: properties:
sourceid: source_id:
type: number type: number
description: ID de la source. description: ID de la source.
sourcename: source_name:
type: string type: string
description: Nom de la source. description: Nom de la source.
sourceurl: source_url:
type: string type: string
description: URL de la source. description: URL de la source.
required: required:
- sourceid - source_id
- sourcename - source_name
- sourceurl - source_url

View File

@ -3,55 +3,28 @@ title: Servermodel
type: object type: object
description: Description d'un modèle de serveur. description: Description d'un modèle de serveur.
properties: properties:
servermodelid: servermodel_id:
type: number type: number
description: ID du modèle de serveur. description: ID du modèle de serveur.
ref: Servermodel.ServermodelId ref: Servermodel.ServermodelId
servermodelname: servermodel_name:
type: string type: string
description: Nom du modèle de serveur. description: Nom du modèle de serveur.
servermodeldescription: servermodel_description:
type: string type: string
description: Description du modèle de serveur. description: Description du modèle de serveur.
servermodelparentsid: servermodel_parents_id:
type: array type: array
items: items:
type: number type: number
ref: Servermodel.ServermodelId ref: Servermodel.ServermodelId
description: ID du modèle de serveur parent. description: ID du modèle de serveur parent.
subreleaseid: release_id:
type: number type: number
ref: Servermodel.SubreleaseId ref: Servermodel.SubreleaseId
description: Version du modèle de serveur. description: Version du modèle de serveur.
subreleasename:
type: string
ref: SubRelease.SubReleaseName
description: Nom de la sous-version.
sourceid:
type: number
ref: ServermodelId.SourceId
description: ID de la sous-version.
services:
type: array
items:
type: object
description: Liste des services applicatifs déclarés pour ce modèle de serveur.
schema:
type: string
description: Contenu du schema.
probes:
type: string
description: Informations sur les sondes.
creolefuncs:
type: string
description: Fonctions Creole.
conffiles:
type: string
description: Fichiers creole au format tar encodé base64
required: required:
- servermodelid - servermodel_id
- servermodelname - servermodel_name
- servermodeldescription - servermodel_description
- servermodelsubreleaseid - release_id
- sourceid
- subreleasename

View File

@ -0,0 +1,20 @@
---
title: Source
type: object
description: Description d'un source.
properties:
source_id:
type: number
description: ID de la source.
ref: Source.SourceId
source_name:
type: string
description: Nom de la source.
source_url:
type: string
description: URL de téléchargement de la source.
required:
- source_id
- source_name
- source_url

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

View File

@ -0,0 +1,97 @@
import asyncpg
import asyncio
from risotto.config import get_config
VERSION_INIT = """
-- Création de la table Source
CREATE TABLE Source (
SourceId SERIAL PRIMARY KEY,
SourceName VARCHAR(255) NOT NULL UNIQUE,
SourceURL TEXT
);
-- Création de la table Release
CREATE TABLE Release (
ReleaseId SERIAL PRIMARY KEY,
ReleaseName VARCHAR(255) NOT NULL,
ReleaseSourceId INTEGER NOT NULL,
ReleaseDistribution VARCHAR(20) CONSTRAINT releasedistribution_choice CHECK (ReleaseDistribution IN ('last', 'n-1', 'n-2')),
UNIQUE (ReleaseName, ReleaseSourceId),
UNIQUE (ReleaseDistribution, ReleaseSourceId),
FOREIGN KEY (ReleaseSourceId) REFERENCES Source(SourceId)
);
-- Création de la table Servermodel
CREATE TABLE Servermodel (
ServermodelId SERIAL PRIMARY KEY,
ServermodelName VARCHAR(255) NOT NULL,
ServermodelDescription VARCHAR(255) NOT NULL,
ServermodelParentsId INTEGER [] DEFAULT '{}',
ServermodelReleaseId INTEGER NOT NULL,
ServermodelApplicationServiceId INTEGER NOT NULL,
ServermodelUsers hstore,
UNIQUE (ServermodelName, ServermodelReleaseId)
);
-- Création de la table ApplicationService
CREATE TABLE ApplicationService (
ApplicationServiceId SERIAL PRIMARY KEY,
ApplicationServiceName VARCHAR(255) NOT NULL,
ApplicationServiceDescription VARCHAR(255) NOT NULL,
ApplicationServiceReleaseId INTEGER NOT NULL,
ApplicationServiceDependencies JSON,
UNIQUE (ApplicationServiceName, ApplicationServiceReleaseId)
);
-- Server table creation
CREATE TABLE Server (
ServerId SERIAL PRIMARY KEY,
ServerName VARCHAR(255) NOT NULL UNIQUE,
ServerDescription VARCHAR(255) NOT NULL,
ServerServermodelId INTEGER NOT NULL
);
-- User, Role and ACL table creation
CREATE TABLE RisottoUser (
UserId SERIAL PRIMARY KEY,
UserLogin VARCHAR(100) NOT NULL UNIQUE,
UserName VARCHAR(100) NOT NULL,
UserSurname VARCHAR(100) NOT NULL
);
CREATE TABLE UserRole (
RoleId SERIAL PRIMARY KEY,
RoleUserId INTEGER NOT NULL,
RoleName VARCHAR(255) NOT NULL,
RoleAttribute VARCHAR(255),
RoleAttributeValue VARCHAR(255),
FOREIGN KEY (RoleUserId) REFERENCES RisottoUser(UserId)
);
CREATE TABLE URI (
URIId SERIAL PRIMARY KEY,
URIName VARCHAR(255) NOT NULL UNIQUE
);
CREATE TABLE RoleURI (
RoleName VARCHAR(255) NOT NULL,
URIId INTEGER NOT NULL,
FOREIGN KEY (URIId) REFERENCES URI(URIId),
PRIMARY KEY (RoleName, URIId)
);
"""
async def main():
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
async with connection.transaction():
returns = await connection.execute(VERSION_INIT)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
# asyncio.run(main())

View File

@ -1,10 +1,32 @@
HTTP_PORT = 8080 HTTP_PORT = 8080
MESSAGE_ROOT_PATH = 'messages' MESSAGE_ROOT_PATH = 'messages'
ROOT_CACHE_DIR = 'cache' DEBUG = True
DEBUG = False
DATABASE_DIR = 'database' DATABASE_DIR = 'database'
INTERNAL_USER = 'internal' INTERNAL_USER = 'internal'
CONFIGURATION_DIR = 'configurations' CONFIGURATION_DIR = 'configurations'
TEMPLATE_DIR = 'templates' TEMPLATE_DIR = 'templates'
TMP_DIR = 'tmp' TMP_DIR = 'tmp'
ROUGAIL_DTD_PATH = '../rougail/data/creole.dtd' ROUGAIL_DTD_PATH = '../rougail/data/creole.dtd'
POSTGRESQL_ADDRESS = '192.168.56.106'
POSTGRESQL_PORT = 5432
import os
from pathlib import PurePosixPath
CURRENT_PATH = PurePosixPath(__file__)
def get_config():
return {'database': {'host': 'localhost',
'port': 5432,
'dbname': 'risotto',
'user': 'risotto',
'password': 'risotto',
},
'http_server': {'port': 8080},
'global': {'message_root_path': CURRENT_PATH.parents[2] / 'messages',
'debug': DEBUG,
'internal_user': 'internal',
'check_role': False,
'rougail_dtd_path': '../rougail/data/creole.dtd'},
'source': {'root_path': '/srv/seed'},
'cache': {'root_path': '/var/cache/risotto'}
}

View File

@ -5,6 +5,10 @@ from .context import Context
class Controller: class Controller:
"""Common controller used to add a service in Risotto """Common controller used to add a service in Risotto
""" """
def __init__(self,
test: bool):
pass
async def call(self, async def call(self,
uri: str, uri: str,
risotto_context: Context, risotto_context: Context,

View File

@ -2,29 +2,23 @@ from tiramisu import Config
from traceback import print_exc from traceback import print_exc
from copy import copy from copy import copy
from typing import Dict, Callable from typing import Dict, Callable
from json import dumps, loads
from .utils import _ from .utils import _
from .error import CallError, NotAllowedError from .error import CallError, NotAllowedError
from .logger import log from .logger import log
from .config import DEBUG from .config import DEBUG
from .config import get_config
from .context import Context from .context import Context
from . import register from . import register
import asyncpg
class CallDispatcher: class CallDispatcher:
def valid_public_function(self, async def valid_call_returns(self,
risotto_context: Context, risotto_context: Context,
kwargs: Dict, returns: Dict,
public_only: bool): kwargs: Dict):
if public_only and not self.messages[risotto_context.version][risotto_context.message]['public']:
msg = _(f'the message {risotto_context.message} is private')
log.error_msg(risotto_context, kwargs, msg)
raise NotAllowedError(msg)
def valid_call_returns(self,
risotto_context: Context,
returns: Dict,
kwargs: Dict):
response = self.messages[risotto_context.version][risotto_context.message]['response'] response = self.messages[risotto_context.version][risotto_context.message]['response']
module_name = risotto_context.function.__module__.split('.')[-2] module_name = risotto_context.function.__module__.split('.')[-2]
function_name = risotto_context.function.__name__ function_name = risotto_context.function.__name__
@ -35,6 +29,7 @@ class CallDispatcher:
raise CallError(str(err)) raise CallError(str(err))
else: else:
if not isinstance(returns, dict): if not isinstance(returns, dict):
log.error_msg(risotto_context, kwargs, returns)
err = _(f'function {module_name}.{function_name} has to return a dict') err = _(f'function {module_name}.{function_name} has to return a dict')
log.error_msg(risotto_context, kwargs, err) log.error_msg(risotto_context, kwargs, err)
raise CallError(str(err)) raise CallError(str(err))
@ -43,11 +38,12 @@ class CallDispatcher:
raise Exception('hu?') raise Exception('hu?')
else: else:
for ret in returns: for ret in returns:
config = Config(response, display_name=lambda self, dyn_name: self.impl_getname()) config = await Config(response,
config.property.read_write() display_name=lambda self, dyn_name: self.impl_getname())
await config.property.read_write()
try: try:
for key, value in ret.items(): for key, value in ret.items():
config.option(key).value.set(value) await config.option(key).value.set(value)
except AttributeError: except AttributeError:
err = _(f'function {module_name}.{function_name} return the unknown parameter "{key}"') err = _(f'function {module_name}.{function_name} return the unknown parameter "{key}"')
log.error_msg(risotto_context, kwargs, err) log.error_msg(risotto_context, kwargs, err)
@ -56,13 +52,13 @@ class CallDispatcher:
err = _(f'function {module_name}.{function_name} return the parameter "{key}" with an unvalid value "{value}"') err = _(f'function {module_name}.{function_name} return the parameter "{key}" with an unvalid value "{value}"')
log.error_msg(risotto_context, kwargs, err) log.error_msg(risotto_context, kwargs, err)
raise CallError(str(err)) raise CallError(str(err))
config.property.read_only() await config.property.read_only()
mandatories = list(config.value.mandatory()) mandatories = await config.value.mandatory()
if mandatories: if mandatories:
mand = [mand.split('.')[-1] for mand in mandatories] mand = [mand.split('.')[-1] for mand in mandatories]
raise ValueError(_(f'missing parameters in response: {mand} in message "{risotto_context.message}"')) raise ValueError(_(f'missing parameters in response: {mand} in message "{risotto_context.message}"'))
try: try:
config.value.dict() await config.value.dict()
except Exception as err: except Exception as err:
err = _(f'function {module_name}.{function_name} return an invalid response {err}') err = _(f'function {module_name}.{function_name} return an invalid response {err}')
log.error_msg(risotto_context, kwargs, err) log.error_msg(risotto_context, kwargs, err)
@ -72,7 +68,7 @@ class CallDispatcher:
version: str, version: str,
message: str, message: str,
old_risotto_context: Context, old_risotto_context: Context,
public_only: bool=False, check_role: bool=False,
**kwargs): **kwargs):
""" execute the function associate with specified uri """ execute the function associate with specified uri
arguments are validate before arguments are validate before
@ -81,40 +77,55 @@ class CallDispatcher:
version, version,
message, message,
'rpc') 'rpc')
self.valid_public_function(risotto_context,
kwargs,
public_only)
self.check_message_type(risotto_context, self.check_message_type(risotto_context,
kwargs) kwargs)
try: try:
tiramisu_config = self.load_kwargs_to_config(risotto_context, kw = await self.load_kwargs_to_config(risotto_context,
kwargs) f'{version}.{message}',
obj = self.messages[version][message] kwargs,
kw = tiramisu_config.option(message).value.dict() check_role)
risotto_context.function = obj['function'] function_obj = self.messages[version][message]
if obj['risotto_context']: risotto_context.function = function_obj['function']
if function_obj['risotto_context']:
kw['risotto_context'] = risotto_context kw['risotto_context'] = risotto_context
returns = await risotto_context.function(self.injected_self[obj['module']], **kw) # do not start a new database connection
if function_obj['database'] and hasattr(old_risotto_context, 'connection'):
risotto_context.connection = old_risotto_context.connection
if function_obj['database'] and not hasattr(risotto_context, 'connection'):
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
await connection.set_type_codec(
'json',
encoder=dumps,
decoder=loads,
schema='pg_catalog'
)
risotto_context.connection = connection
async with connection.transaction():
returns = await risotto_context.function(self.injected_self[function_obj['module']], **kw)
else:
returns = await risotto_context.function(self.injected_self[function_obj['module']], **kw)
except CallError as err: except CallError as err:
raise err raise err
except Exception as err: except Exception as err:
if DEBUG: if get_config().get('global').get('debug'):
print_exc() print_exc()
log.error_msg(risotto_context, log.error_msg(risotto_context,
kwargs, kwargs,
err) err)
raise CallError(str(err)) raise CallError(str(err))
# valid returns # valid returns
self.valid_call_returns(risotto_context, await self.valid_call_returns(risotto_context,
returns, returns,
kwargs) kwargs)
# log the success # log the success
log.info_msg(risotto_context, log.info_msg(risotto_context,
kwargs, kwargs,
_(f'returns {returns}')) _(f'returns {returns}'))
# notification # notification
if obj.get('notification'): if function_obj.get('notification'):
notif_version, notif_message = obj['notification'].split('.', 1) notif_version, notif_message = function_obj['notification'].split('.', 1)
if not isinstance(returns, list): if not isinstance(returns, list):
send_returns = [returns] send_returns = [returns]
else: else:
@ -128,7 +139,12 @@ class CallDispatcher:
class PublishDispatcher: class PublishDispatcher:
async def publish(self, version, message, old_risotto_context, public_only=False, **kwargs): async def publish(self,
version: str,
message: str,
old_risotto_context: Context,
check_role: bool=False,
**kwargs) -> None:
risotto_context = self.build_new_context(old_risotto_context, risotto_context = self.build_new_context(old_risotto_context,
version, version,
message, message,
@ -136,9 +152,8 @@ class PublishDispatcher:
self.check_message_type(risotto_context, self.check_message_type(risotto_context,
kwargs) kwargs)
try: try:
config = self.load_kwargs_to_config(risotto_context, config_arguments = await self.load_kwargs_to_config(risotto_context,
kwargs) kwargs)
config_arguments = config.option(message).value.dict()
except CallError as err: except CallError as err:
return return
except Exception as err: except Exception as err:
@ -163,7 +178,23 @@ class PublishDispatcher:
if function_obj['risotto_context']: if function_obj['risotto_context']:
kw['risotto_context'] = risotto_context kw['risotto_context'] = risotto_context
# send event # send event
returns = await function(self.injected_self[function_obj['module']], **kw) if function_obj['database'] and hasattr(old_risotto_context, 'connection'):
risotto_context.connection = old_risotto_context.connection
if function_obj['database'] and not hasattr(risotto_context, 'connection'):
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
await connection.set_type_codec(
'json',
encoder=dumps,
decoder=loads,
schema='pg_catalog'
)
risotto_context.connection = connection
async with connection.transaction():
returns = await function(self.injected_self[function_obj['module']], **kw)
else:
returns = await function(self.injected_self[function_obj['module']], **kw)
except Exception as err: except Exception as err:
if DEBUG: if DEBUG:
print_exc() print_exc()
@ -210,38 +241,93 @@ class Dispatcher(register.RegisterDispatcher, CallDispatcher, PublishDispatcher)
log.error_msg(risotto_context, kwargs, msg) log.error_msg(risotto_context, kwargs, msg)
raise CallError(msg) raise CallError(msg)
def load_kwargs_to_config(self, async def load_kwargs_to_config(self,
risotto_context: Context, risotto_context: Context,
kwargs: Dict): uri: str,
kwargs: Dict,
check_role: bool):
""" create a new Config et set values to it """ create a new Config et set values to it
""" """
# create a new config # create a new config
config = Config(self.option) config = await Config(self.option)
config.property.read_write() await config.property.read_write()
# set message's option # set message's option
config.option('message').value.set(risotto_context.message) await config.option('message').value.set(risotto_context.message)
# store values # store values
subconfig = config.option(risotto_context.message) subconfig = config.option(risotto_context.message)
for key, value in kwargs.items(): for key, value in kwargs.items():
try: try:
subconfig.option(key).value.set(value) await subconfig.option(key).value.set(value)
except AttributeError: except AttributeError:
if DEBUG: if DEBUG:
print_exc() print_exc()
raise AttributeError(_(f'unknown parameter "{key}"')) raise ValueError(_(f'unknown parameter in "{uri}": "{key}"'))
# check mandatories options # check mandatories options
config.property.read_only() if check_role and get_config().get('global').get('check_role'):
mandatories = list(config.value.mandatory()) await self.check_role(subconfig,
risotto_context.username,
uri)
await config.property.read_only()
mandatories = await config.value.mandatory()
if mandatories: if mandatories:
mand = [mand.split('.')[-1] for mand in mandatories] mand = [mand.split('.')[-1] for mand in mandatories]
raise ValueError(_(f'missing parameters: {mand}')) raise ValueError(_(f'missing parameters in "{uri}": {mand}'))
# return the config # return complete an validated kwargs
return config return await subconfig.value.dict()
def get_service(self, def get_service(self,
name: str): name: str):
return self.injected_self[name] return self.injected_self[name]
async def check_role(self,
config: Config,
user_login: str,
uri: str) -> None:
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
async with connection.transaction():
# Verify if user exists and get ID
sql = '''
SELECT UserId
FROM RisottoUser
WHERE UserLogin = $1
'''
user_id = await connection.fetchval(sql,
user_login)
if user_id is None:
raise NotAllowedError(_(f"You ({user_login}) don't have any account in this application"))
# Get all references for this message
refs = {}
for option in await config.list('all'):
ref = await option.information.get('ref')
if ref:
refs[ref] = str(await option.value.get())
# Check role
select_role_uri = '''
SELECT RoleName
FROM URI, RoleURI
WHERE URI.URIName = $1 AND RoleURI.URIId = URI.URIId
'''
select_role_user = '''
SELECT RoleAttribute, RoleAttributeValue
FROM UserRole
WHERE RoleUserId = $1 AND RoleName = $2
'''
for uri_role in await connection.fetch(select_role_uri,
uri):
for user_role in await connection.fetch(select_role_user,
user_id,
uri_role['rolename']):
if not user_role['roleattribute']:
return
if user_role['roleattribute'] in refs and \
user_role['roleattributevalue'] == refs[user_role['roleattribute']]:
return
raise NotAllowedError(_(f'You ({user_login}) don\'t have any authorisation to access to "{uri}"'))
dispatcher = Dispatcher() dispatcher = Dispatcher()
register.dispatcher = dispatcher register.dispatcher = dispatcher

View File

@ -8,3 +8,7 @@ class CallError(Exception):
class NotAllowedError(Exception): class NotAllowedError(Exception):
pass pass
class ExecutionError(Exception):
pass

View File

@ -61,19 +61,24 @@ class extra_route_handler:
async def handle(request): async def handle(request):
version, uri = request.match_info.get_info()['path'].rsplit('/', 2)[-2:] version, message = request.match_info.get_info()['path'].rsplit('/', 2)[-2:]
risotto_context = create_context(request) risotto_context = create_context(request)
kwargs = await request.json() kwargs = await request.json()
try: try:
text = await dispatcher.call(version, pattern = dispatcher.messages[version][message]['pattern']
uri, if pattern == 'rpc':
risotto_context, method = dispatcher.call
public_only=True, else:
**kwargs) method = dispatcher.publish
text = await method(version,
message,
risotto_context,
check_role=True,
**kwargs)
except NotAllowedError as err: except NotAllowedError as err:
raise HTTPNotFound(reason=str(err)) raise HTTPNotFound(reason=str(err))
except CallError as err: except CallError as err:
raise HTTPBadRequest(reason=str(err)) raise HTTPBadRequest(reason=str(err).replace('\n', ' '))
except Exception as err: except Exception as err:
if DEBUG: if DEBUG:
print_exc() print_exc()
@ -84,10 +89,10 @@ async def handle(request):
async def api(request, risotto_context): async def api(request, risotto_context):
global tiramisu global tiramisu
if not tiramisu: if not tiramisu:
config = Config(get_messages(load_shortarg=True, config = await Config(get_messages(load_shortarg=True,
only_public=True)[1]) only_public=True)[1])
config.property.read_write() await config.property.read_write()
tiramisu = config.option.dict(remotable='none') tiramisu = await config.option.dict(remotable='none')
return tiramisu return tiramisu
@ -102,6 +107,7 @@ async def get_app(loop):
load_services() load_services()
app = Application(loop=loop) app = Application(loop=loop)
routes = [] routes = []
await dispatcher.load()
for version, messages in dispatcher.messages.items(): for version, messages in dispatcher.messages.items():
print() print()
print(_('======== Registered messages ========')) print(_('======== Registered messages ========'))

View File

@ -53,5 +53,10 @@ class Logger:
if DEBUG: if DEBUG:
print(tmsg) print(tmsg)
def info(self,
msg):
if DEBUG:
print(msg)
log = Logger() log = Logger()

View File

@ -3,15 +3,16 @@ from os.path import join, basename, dirname
from glob import glob from glob import glob
from tiramisu import StrOption, IntOption, BoolOption, ChoiceOption, OptionDescription, SymLinkOption, \ from tiramisu import StrOption, IntOption, BoolOption, ChoiceOption, OptionDescription, SymLinkOption, \
Config, Calculation, Params, ParamOption, ParamValue, calc_value, calc_value_property_help, \ Calculation, Params, ParamOption, ParamValue, calc_value, calc_value_property_help, \
groups, Option groups, Option
from yaml import load, SafeLoader from yaml import load, SafeLoader
from os import listdir from os import listdir
from os.path import isfile from os.path import isfile
from ..config import MESSAGE_ROOT_PATH from ..config import get_config
from ..utils import _ from ..utils import _
MESSAGE_ROOT_PATH = get_config().get('global').get('message_root_path')
groups.addgroup('message') groups.addgroup('message')
@ -42,8 +43,6 @@ class MessageDefinition:
__slots__ = ('version', __slots__ = ('version',
'uri', 'uri',
'description', 'description',
'sampleuse',
'domain',
'parameters', 'parameters',
'public', 'public',
'errors', 'errors',
@ -51,7 +50,7 @@ class MessageDefinition:
'related', 'related',
'response') 'response')
def __init__(self, raw_def): def __init__(self, raw_def, message):
# default value for non mandatory key # default value for non mandatory key
self.version = u'' self.version = u''
self.parameters = OrderedDict() self.parameters = OrderedDict()
@ -59,7 +58,6 @@ class MessageDefinition:
self.errors = [] self.errors = []
self.related = [] self.related = []
self.response = None self.response = None
self.sampleuse = None
# loads yaml information into object # loads yaml information into object
for key, value in raw_def.items(): for key, value in raw_def.items():
@ -91,6 +89,8 @@ class MessageDefinition:
# message with pattern = error must be public # message with pattern = error must be public
if self.public is False and self.pattern == 'error': if self.public is False and self.pattern == 'error':
raise Exception(_('Error message must be public : {}').format(self.uri)) raise Exception(_('Error message must be public : {}').format(self.uri))
if self.uri != message:
raise Exception(_(f'yaml file name "{message}.yml" does not match uri "{self.uri}"'))
class ParameterDefinition: class ParameterDefinition:
@ -217,8 +217,8 @@ def _parse_parameters(raw_defs):
return parameters return parameters
def parse_definition(filename: str): def parse_definition(filecontent: bytes, message: str):
return MessageDefinition(load(filename, Loader=SafeLoader)) return MessageDefinition(load(filecontent, Loader=SafeLoader), message)
def is_message_defined(uri): def is_message_defined(uri):
version, message = split_message_uri(uri) version, message = split_message_uri(uri)
@ -231,7 +231,7 @@ def get_message(uri):
version, message = split_message_uri(uri) version, message = split_message_uri(uri)
path = get_message_file_path(version, message) path = get_message_file_path(version, message)
with open(path, "r") as message_file: with open(path, "r") as message_file:
message_content = parse_definition(message_file.read()) message_content = parse_definition(message_file.read(), message)
message_content.version = version message_content.version = version
return message_content return message_content
except Exception as err: except Exception as err:
@ -426,7 +426,7 @@ def _get_option(name,
'expected': ParamValue(optiondescription), 'expected': ParamValue(optiondescription),
'reverse_condition': ParamValue(True)}), 'reverse_condition': ParamValue(True)}),
calc_value_property_help)) calc_value_property_help))
description = arg.description.strip().rstrip() description = arg.description.strip().rstrip()
kwargs = {'name': name, kwargs = {'name': name,
'doc': _get_description(description, name), 'doc': _get_description(description, name),
@ -440,16 +440,19 @@ def _get_option(name,
kwargs['multi'] = True kwargs['multi'] = True
type_ = type_[2:] type_ = type_[2:]
if type_ == 'Dict': if type_ == 'Dict':
return DictOption(**kwargs) obj = DictOption(**kwargs)
elif type_ == 'String': elif type_ == 'String':
return StrOption(**kwargs) obj = StrOption(**kwargs)
elif type_ == 'Any': elif type_ == 'Any':
return AnyOption(**kwargs) obj = AnyOption(**kwargs)
elif 'Number' in type_ or type_ == 'ID' or type_ == 'Integer': elif 'Number' in type_ or type_ == 'ID' or type_ == 'Integer':
return IntOption(**kwargs) obj = IntOption(**kwargs)
elif type_ == 'Boolean': elif type_ == 'Boolean':
return BoolOption(**kwargs) obj = BoolOption(**kwargs)
raise Exception('unsupported type {} in {}'.format(type_, file_path)) else:
raise Exception('unsupported type {} in {}'.format(type_, file_path))
obj.impl_set_information('ref', arg.ref)
return obj
def _parse_args(message_def, def _parse_args(message_def,
@ -463,11 +466,15 @@ def _parse_args(message_def,
""" """
new_options = OrderedDict() new_options = OrderedDict()
for name, arg in message_def.parameters.items(): for name, arg in message_def.parameters.items():
new_options[name] = arg #new_options[name] = arg
if arg.ref: # if arg.ref:
needs.setdefault(message_def.uri, {}).setdefault(arg.ref, []).append(name) # needs.setdefault(message_def.uri, {}).setdefault(arg.ref, []).append(name)
for name, arg in new_options.items(): #for name, arg in new_options.items():
current_opt = _get_option(name, arg, file_path, select_option, optiondescription) current_opt = _get_option(name,
arg,
file_path,
select_option,
optiondescription)
options.append(current_opt) options.append(current_opt)
if hasattr(arg, 'shortarg') and arg.shortarg and load_shortarg: if hasattr(arg, 'shortarg') and arg.shortarg and load_shortarg:
options.append(SymLinkOption(arg.shortarg, current_opt)) options.append(SymLinkOption(arg.shortarg, current_opt))
@ -478,13 +485,7 @@ def _parse_responses(message_def,
"""build option with returns """build option with returns
""" """
if message_def.response.parameters is None: if message_def.response.parameters is None:
raise Exception('not implemented yet') raise Exception('uri "{}" did not returned any valid parameters.'.format(message_def.uri))
#name = 'response'
#keys['']['columns'][name] = {'description': message_def.response.description,
# 'type': message_def.response.type}
#responses = {}
#responses['keys'] = keys
#return responses
options = [] options = []
names = [] names = []
@ -506,7 +507,7 @@ def _parse_responses(message_def,
# FIXME # FIXME
'File': StrOption}.get(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} in responses of message {message_def.uri}')
if hasattr(obj, 'default'): if hasattr(obj, 'default'):
kwargs['default'] = obj.default kwargs['default'] = obj.default
else: else:
@ -622,12 +623,4 @@ def get_messages(load_shortarg=False, only_public=False):
load_shortarg) load_shortarg)
root = _get_root_option(select_option, optiondescriptions) root = _get_root_option(select_option, optiondescriptions)
try:
config = Config(root)
except Exception as err:
raise Exception('error when generating root optiondescription: {}'.format(err))
config.property.read_write()
# config.property.add('demoting_error_warning')
# return needs, responses, config
return optiondescriptions_info, root return optiondescriptions_info, root

View File

@ -1,15 +1,18 @@
from tiramisu import Config from tiramisu import Config
from inspect import signature from inspect import signature
from typing import Callable, Optional from typing import Callable, Optional
import asyncpg
from .utils import undefined, _ from .utils import undefined, _
from .error import RegistrationError from .error import RegistrationError
from .message import get_messages from .message import get_messages
from .context import Context from .context import Context
from .config import INTERNAL_USER from .config import INTERNAL_USER, get_config
def register(uris: str, def register(uris: str,
notification: str=undefined): notification: str=undefined,
database: bool=False):
""" Decorator to register function to the dispatcher """ Decorator to register function to the dispatcher
""" """
if not isinstance(uris, list): if not isinstance(uris, list):
@ -21,6 +24,7 @@ def register(uris: str,
dispatcher.set_function(version, dispatcher.set_function(version,
message, message,
notification, notification,
database,
function) function)
return decorator return decorator
@ -45,22 +49,22 @@ class RegisterDispatcher:
first_argument_index = 1 first_argument_index = 1
return [param.name for param in list(signature(function).parameters.values())[first_argument_index:]] return [param.name for param in list(signature(function).parameters.values())[first_argument_index:]]
def valid_rpc_params(self, async def valid_rpc_params(self,
version: str, version: str,
message: str, message: str,
function: Callable, function: Callable,
module_name: str): module_name: str):
""" parameters function must have strictly all arguments with the correct name """ parameters function must have strictly all arguments with the correct name
""" """
def get_message_args(): async def get_message_args():
# load config # load config
config = Config(self.option) config = await Config(self.option)
config.property.read_write() await config.property.read_write()
# set message to the uri name # set message to the uri name
config.option('message').value.set(message) await config.option('message').value.set(message)
# get message argument # get message argument
subconfig = config.option(message) dico = await config.option(message).value.dict()
return set(config.option(message).value.dict().keys()) return set(dico.keys())
def get_function_args(): def get_function_args():
function_args = self.get_function_args(function) function_args = self.get_function_args(function)
@ -70,7 +74,7 @@ class RegisterDispatcher:
return set(function_args) return set(function_args)
# get message arguments # get message arguments
message_args = get_message_args() message_args = await get_message_args()
# get function arguments # get function arguments
function_args = get_function_args() function_args = get_function_args()
# compare message arguments with function parameter # compare message arguments with function parameter
@ -88,22 +92,22 @@ class RegisterDispatcher:
msg = _(' and ').join(msg) msg = _(' and ').join(msg)
raise RegistrationError(_(f'error with {module_name}.{function_name} arguments: {msg}')) raise RegistrationError(_(f'error with {module_name}.{function_name} arguments: {msg}'))
def valid_event_params(self, async def valid_event_params(self,
version: str, version: str,
message: str, message: str,
function: Callable, function: Callable,
module_name: str): module_name: str):
""" parameters function validation for event messages """ parameters function validation for event messages
""" """
def get_message_args(): async def get_message_args():
# load config # load config
config = Config(self.option) config = await Config(self.option)
config.property.read_write() await config.property.read_write()
# set message to the message name # set message to the message name
config.option('message').value.set(message) await config.option('message').value.set(message)
# get message argument # get message argument
subconfig = config.option(message) dico = await config.option(message).value.dict()
return set(config.option(message).value.dict().keys()) return set(dico.keys())
def get_function_args(): def get_function_args():
function_args = self.get_function_args(function) function_args = self.get_function_args(function)
@ -113,7 +117,7 @@ class RegisterDispatcher:
return set(function_args) return set(function_args)
# get message arguments # get message arguments
message_args = get_message_args() message_args = await get_message_args()
# get function arguments # get function arguments
function_args = get_function_args() function_args = get_function_args()
# compare message arguments with function parameter # compare message arguments with function parameter
@ -129,6 +133,7 @@ class RegisterDispatcher:
version: str, version: str,
message: str, message: str,
notification: str, notification: str,
database: bool,
function: Callable): function: Callable):
""" register a function to an URI """ register a function to an URI
URI is a message URI is a message
@ -156,18 +161,11 @@ class RegisterDispatcher:
if 'function' in self.messages[version][message]: if 'function' in self.messages[version][message]:
raise RegistrationError(_(f'uri {version}.{message} already registered')) raise RegistrationError(_(f'uri {version}.{message} already registered'))
# valid function's arguments # check notification
if self.messages[version][message]['pattern'] == 'rpc': if self.messages[version][message]['pattern'] == 'rpc':
if notification is undefined: if notification is undefined:
function_name = function.__name__ function_name = function.__name__
raise RegistrationError(_(f'notification is mandatory when registered "{message}" with "{module_name}.{function_name}" even if you set None')) raise RegistrationError(_(f'notification is mandatory when registered "{message}" with "{module_name}.{function_name}" even if you set None'))
valid_params = self.valid_rpc_params
else:
valid_params = self.valid_event_params
valid_params(version,
message,
function,
module_name)
# register # register
if self.messages[version][message]['pattern'] == 'rpc': if self.messages[version][message]['pattern'] == 'rpc':
@ -180,6 +178,7 @@ class RegisterDispatcher:
function, function,
function_args, function_args,
inject_risotto_context, inject_risotto_context,
database,
notification) notification)
def register_rpc(self, def register_rpc(self,
@ -189,11 +188,13 @@ class RegisterDispatcher:
function: Callable, function: Callable,
function_args: list, function_args: list,
inject_risotto_context: bool, inject_risotto_context: bool,
database: bool,
notification: Optional[str]): notification: Optional[str]):
self.messages[version][message]['module'] = module_name self.messages[version][message]['module'] = module_name
self.messages[version][message]['function'] = function self.messages[version][message]['function'] = function
self.messages[version][message]['arguments'] = function_args self.messages[version][message]['arguments'] = function_args
self.messages[version][message]['risotto_context'] = inject_risotto_context self.messages[version][message]['risotto_context'] = inject_risotto_context
self.messages[version][message]['database'] = database
if notification: if notification:
self.messages[version][message]['notification'] = notification self.messages[version][message]['notification'] = notification
@ -204,6 +205,7 @@ class RegisterDispatcher:
function: Callable, function: Callable,
function_args: list, function_args: list,
inject_risotto_context: bool, inject_risotto_context: bool,
database: bool,
notification: Optional[str]): notification: Optional[str]):
if 'functions' not in self.messages[version][message]: if 'functions' not in self.messages[version][message]:
self.messages[version][message]['functions'] = [] self.messages[version][message]['functions'] = []
@ -211,16 +213,17 @@ class RegisterDispatcher:
dico = {'module': module_name, dico = {'module': module_name,
'function': function, 'function': function,
'arguments': function_args, 'arguments': function_args,
'database': database,
'risotto_context': inject_risotto_context} 'risotto_context': inject_risotto_context}
if notification and notification is not undefined: if notification and notification is not undefined:
dico['notification'] = notification dico['notification'] = notification
self.messages[version][message]['functions'].append(dico) self.messages[version][message]['functions'].append(dico)
def set_module(self, module_name, module): def set_module(self, module_name, module, test):
""" register and instanciate a new module """ register and instanciate a new module
""" """
try: try:
self.injected_self[module_name] = module.Risotto() self.injected_self[module_name] = module.Risotto(test)
except AttributeError as err: except AttributeError as err:
raise RegistrationError(_(f'unable to register the module {module_name}, this module must have Risotto class')) raise RegistrationError(_(f'unable to register the module {module_name}, this module must have Risotto class'))
@ -231,7 +234,10 @@ class RegisterDispatcher:
for version, messages in self.messages.items(): for version, messages in self.messages.items():
for message, message_obj in messages.items(): for message, message_obj in messages.items():
if not 'functions' in message_obj and not 'function' in message_obj: if not 'functions' in message_obj and not 'function' in message_obj:
missing_messages.append(message) if message_obj['pattern'] == 'event':
print(f'{message} prêche dans le désert')
else:
missing_messages.append(message)
if missing_messages: if missing_messages:
raise RegistrationError(_(f'missing uri {missing_messages}')) raise RegistrationError(_(f'missing uri {missing_messages}'))
@ -242,3 +248,39 @@ class RegisterDispatcher:
risotto_context.paths.append(f'{module_name}.on_join') risotto_context.paths.append(f'{module_name}.on_join')
risotto_context.type = None risotto_context.type = None
await module.on_join(risotto_context) await module.on_join(risotto_context)
async def insert_message(self,
connection,
uri):
sql = """INSERT INTO URI(URIName) VALUES ($1)
ON CONFLICT (URIName) DO NOTHING
"""
await connection.fetchval(sql,
uri)
async def load(self):
# valid function's arguments
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
async with connection.transaction():
for version, messages in self.messages.items():
for message, message_infos in messages.items():
if message_infos['pattern'] == 'rpc':
module_name = message_infos['module']
function = message_infos['function']
await self.valid_rpc_params(version,
message,
function,
module_name)
else:
if 'functions' in message_infos:
for function_infos in message_infos['functions']:
module_name = function_infos['module']
function = function_infos['function']
await self.valid_event_params(version,
message,
function,
module_name)
await self.insert_message(connection,
f'{version}.{message}')

View File

@ -5,7 +5,8 @@ from ..dispatcher import dispatcher
def load_services(modules=None, def load_services(modules=None,
validate: bool=True): validate: bool=True,
test: bool=False):
abs_here = dirname(abspath(__file__)) abs_here = dirname(abspath(__file__))
here = basename(abs_here) here = basename(abs_here)
module = basename(dirname(abs_here)) module = basename(dirname(abs_here))
@ -14,6 +15,6 @@ def load_services(modules=None,
for filename in modules: for filename in modules:
absfilename = join(abs_here, filename) absfilename = join(abs_here, filename)
if isdir(absfilename) and isfile(join(absfilename, '__init__.py')): if isdir(absfilename) and isfile(join(absfilename, '__init__.py')):
dispatcher.set_module(filename, import_module(f'.{here}.{filename}', module)) dispatcher.set_module(filename, import_module(f'.{here}.{filename}', module), test)
if validate: if validate:
dispatcher.validate() dispatcher.validate()

View File

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

View File

@ -0,0 +1,121 @@
from os import listdir
from os.path import join
from traceback import print_exc
from yaml import load, SafeLoader
from typing import Dict, List
from ...controller import Controller
from ...register import register
from ...config import get_config
from ...error import ExecutionError
from ...context import Context
from ...utils import _
class Risotto(Controller):
def __init__(self,
test: bool) -> None:
self.source_root_path = get_config().get('source').get('root_path')
async def _applicationservice_create(self,
risotto_context: Context,
applicationservice_name: str,
applicationservice_description: str,
applicationservice_dependencies: List[int],
release_id: int) -> Dict:
applicationservice_update_query = """INSERT INTO ApplicationService(ApplicationServiceName, ApplicationServiceDescription, ApplicationServiceDependencies, ApplicationServiceReleaseId) VALUES ($1,$2,$3,$4)
RETURNING ApplicationServiceId
"""
applicationservice_id = await risotto_context.connection.fetchval(applicationservice_update_query,
applicationservice_name,
applicationservice_description,
applicationservice_dependencies,
release_id)
return {'applicationservice_name': applicationservice_name,
'applicationservice_description': applicationservice_description,
'applicationservice_release_id': release_id,
'applicationservice_id': applicationservice_id}
@register('v1.applicationservice.create', None, database=True)
async def applicationservice_create(self,
risotto_context: Context,
applicationservice_name: str,
applicationservice_description: str,
applicationservice_dependencies: List[int],
release_id: int) -> Dict:
return await self._applicationservice_create(risotto_context,
applicationservice_name,
applicationservice_description,
applicationservice_dependencies,
release_id)
@register('v1.applicationservice.dataset.updated', None, database=True)
async def applicationservice_update(self,
risotto_context: Context,
source_name: str,
release_distribution: str) -> Dict:
source = await self.call('v1.source.describe',
risotto_context,
source_name=source_name)
release = await self.call('v1.source.release.get_by_distribution',
risotto_context,
source_id=source['source_id'],
release_distribution=release_distribution)
applicationservice_path = join(self.source_root_path,
source_name,
release['release_name'],
'applicationservice')
release_id = release['release_id']
for service in listdir(applicationservice_path):
try:
applicationservice_description_path = join(applicationservice_path,
service,
'applicationservice.yml')
with open(applicationservice_description_path, 'r') as applicationservice_yml:
applicationservice_description = load(applicationservice_yml,
Loader=SafeLoader)
except Exception as err:
if get_config().get('global').get('debug'):
print_exc()
raise ExecutionError(_(f'Error while reading {applicationservice_description_path}: {err}'))
try:
await self._applicationservice_create(risotto_context,
applicationservice_description['name'],
applicationservice_description['description'],
[], # FIXME dependencies
release_id)
except Exception as err:
if get_config().get('global').get('debug'):
print_exc()
raise ExecutionError(_(f"Error while injecting application service {applicationservice_description['name']} in database: {err}"))
return {'retcode': 0,
'returns': _('Application Services successfully loaded')}
@register('v1.applicationservice.get_by_id', None, database=True)
async def applicationservice_get_by_id(self,
risotto_context: Context,
applicationservice_id: int) -> Dict:
applicationservice_query = """
SELECT ApplicationServiceId as applicationservice_id, ApplicationServiceName as applicationservice_name, ApplicationServiceDependencies as applicationservice_dependencies, ApplicationServiceReleaseId as applicationservice_release_id
FROM applicationservice
WHERE applicationserviceid=$1"""
applicationservice = await risotto_context.connection.fetchrow(applicationservice_query,
applicationservice_id)
if applicationservice is None:
raise Exception(_(f'unknown service with ID {applicationservice_id}'))
return dict(applicationservice)
@register('v1.applicationservice.describe', None, database=True)
async def applicationservice_describe(self,
risotto_context: Context,
applicationservice_name,
release_id):
applicationservice_query = """
SELECT ApplicationServiceId as applicationservice_id, ApplicationServiceName as applicationservice_name, ApplicationServiceDependencies as applicationservice_dependencies, ApplicationServiceReleaseId as applicationservice_release_id
FROM ApplicationService
WHERE ApplicationServiceName=$1 AND ApplicationServiceReleaseId=$2"""
applicationservice = await risotto_context.connection.fetchrow(applicationservice_query,
applicationservice_name,
release_id)
if applicationservice is None:
raise Exception(_(f'unknown service {applicationservice_name} in release ID {release_id}'))
return dict(applicationservice)

View File

@ -10,7 +10,7 @@ from rougail import load as rougail_load
from ...controller import Controller from ...controller import Controller
from ...register import register from ...register import register
from ...config import ROOT_CACHE_DIR, DATABASE_DIR, DEBUG, ROUGAIL_DTD_PATH from ...config import DATABASE_DIR, DEBUG, ROUGAIL_DTD_PATH, get_config
from ...context import Context from ...context import Context
from ...utils import _ from ...utils import _
from ...error import CallError, RegistrationError from ...error import CallError, RegistrationError
@ -18,15 +18,19 @@ from ...logger import log
class Risotto(Controller): class Risotto(Controller):
servermodel = {}
server = {}
def __init__(self) -> None: def __init__(self,
for dirname in [ROOT_CACHE_DIR, DATABASE_DIR]: test) -> None:
global conf_storage
self.cache_root_path = join(get_config().get('cache').get('root_path'), 'servermodel')
for dirname in [self.cache_root_path, DATABASE_DIR]:
if not isdir(dirname): if not isdir(dirname):
raise RegistrationError(_(f'unable to find the cache dir "{dirname}"')) raise RegistrationError(_(f'unable to find the cache dir "{dirname}"'))
self.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR) if not test:
super().__init__() self.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR)
self.servermodel = {}
self.server = {}
super().__init__(test)
async def on_join(self, async def on_join(self,
risotto_context: Context) -> None: risotto_context: Context) -> None:
@ -49,118 +53,96 @@ class Risotto(Controller):
for servermodel in servermodels: for servermodel in servermodels:
try: try:
await self.load_servermodel(risotto_context, await self.load_servermodel(risotto_context,
servermodel['servermodelid'], servermodel['servermodel_id'],
servermodel['servermodelname']) servermodel['servermodel_name'])
except CallError as err: except CallError as err:
pass pass
# do link to this servermodel # do link to this servermodel
for servermodel in servermodels: for servermodel in servermodels:
if 'servermodelparentsid' in servermodel: if 'servermodel_parents_id' in servermodel:
for servermodelparentid in servermodel['servermodelparentsid']: for servermodelparentid in servermodel['servermodel_parents_id']:
self.servermodel_legacy(risotto_context, await self.servermodel_legacy(risotto_context,
servermodel['servermodelname'], servermodel['servermodel_name'],
servermodel['servermodelid'], servermodel['servermodel_id'],
servermodelparentid) servermodelparentid)
def get_funcs_filename(self, def get_funcs_filename(self,
servermodelid: int): servermodel_id: int):
return join(ROOT_CACHE_DIR, str(servermodelid)+".creolefuncs") return join(self.cache_root_path, str(servermodel_id), "funcs.py")
async def load_servermodel(self, async def load_servermodel(self,
risotto_context: Context, risotto_context: Context,
servermodelid: int, servermodel_id: int,
servermodelname: str) -> None: servermodel_name: str) -> None:
""" Loads a servermodel """ Loads a servermodel
""" """
cache_file = join(ROOT_CACHE_DIR, str(servermodelid)+".xml") cache_file = join(self.cache_root_path, str(servermodel_id), "dictionaries.xml")
funcs_file = self.get_funcs_filename(servermodelid) funcs_file = self.get_funcs_filename(servermodel_id)
log.info_msg(risotto_context, log.info_msg(risotto_context,
None, None,
f'Load servermodel {servermodelname} ({servermodelid})') f'Load servermodel {servermodel_name} ({servermodel_id})')
# use file in cache if found, otherwise retrieve it in servermodel context # use file in cache
if isfile(cache_file): with open(cache_file) as fileio:
fileio = open(cache_file) xmlroot = parse(fileio).getroot()
else: self.servermodel[servermodel_id] = await self.build_metaconfig(servermodel_id,
servermodel = await self.call('v1.servermodel.describe', servermodel_name,
risotto_context, xmlroot,
servermodelid=servermodelid, funcs_file)
inheritance=False,
resolvdepends=False,
schema=True,
creolefuncs=True)
fileio = BytesIO()
fileio.write(servermodel['schema'].encode())
fileio.seek(0)
with open(cache_file, 'w') as cache: async def build_metaconfig(self,
cache.write(servermodel['schema']) servermodel_id: int,
with open(funcs_file, 'w') as cache: servermodel_name: str,
cache.write(servermodel['creolefuncs']) xmlroot: str,
del servermodel funcs_file: str) -> MetaConfig:
# loads tiramisu config and store it
xmlroot = parse(fileio).getroot()
self.servermodel[servermodelid] = self.build_metaconfig(servermodelid,
servermodelname,
xmlroot,
funcs_file)
def build_metaconfig(self,
servermodelid: int,
servermodelname: str,
xmlroot: str,
funcs_file: str) -> MetaConfig:
""" Build metaconfig for a servermodel """ Build metaconfig for a servermodel
""" """
# build tiramisu's session ID # build tiramisu's session ID
session_id = f'v_{servermodelid}' session_id = f'v_{servermodel_id}'
optiondescription = rougail_load(xmlroot, optiondescription = rougail_load(xmlroot,
ROUGAIL_DTD_PATH, ROUGAIL_DTD_PATH,
funcs_file) funcs_file)
# build servermodel metaconfig (v_xxx.m_v_xxx) # build servermodel metaconfig (v_xxx.m_v_xxx)
metaconfig = MetaConfig([], metaconfig = await MetaConfig([],
optiondescription=optiondescription, optiondescription=optiondescription,
persistent=True, persistent=True,
session_id=session_id, session_id=session_id,
storage=self.save_storage) storage=self.save_storage)
mixconfig = MixConfig(children=[], mixconfig = await MixConfig(children=[],
optiondescription=optiondescription, optiondescription=optiondescription,
persistent=True, persistent=True,
session_id='m_' + session_id, session_id='m_' + session_id,
storage=self.save_storage) storage=self.save_storage)
metaconfig.config.add(mixconfig) await metaconfig.config.add(mixconfig)
# change default rights # change default rights
ro_origin = metaconfig.property.getdefault('read_only', 'append') ro_origin = await metaconfig.property.getdefault('read_only', 'append')
ro_append = frozenset(ro_origin - {'force_store_value'}) ro_append = frozenset(ro_origin - {'force_store_value'})
rw_origin = metaconfig.property.getdefault('read_write', 'append') rw_origin = await metaconfig.property.getdefault('read_write', 'append')
rw_append = frozenset(rw_origin - {'force_store_value'}) rw_append = frozenset(rw_origin - {'force_store_value'})
metaconfig.property.setdefault(ro_append, 'read_only', 'append') await metaconfig.property.setdefault(ro_append, 'read_only', 'append')
metaconfig.property.setdefault(rw_append, 'read_write', 'append') await metaconfig.property.setdefault(rw_append, 'read_write', 'append')
metaconfig.property.read_only() await metaconfig.property.read_only()
metaconfig.permissive.add('basic') await metaconfig.permissive.add('basic')
metaconfig.permissive.add('normal') await metaconfig.permissive.add('normal')
metaconfig.permissive.add('expert') await metaconfig.permissive.add('expert')
# set informtion and owner # set informtion and owner
metaconfig.owner.set('v_{}'.format(servermodelname)) await metaconfig.owner.set('v_{}'.format(servermodel_name))
metaconfig.information.set('servermodel_id', servermodelid) await metaconfig.information.set('servermodel_id', servermodel_id)
metaconfig.information.set('servermodel_name', servermodelname) await metaconfig.information.set('servermodel_name', servermodel_name)
# return configuration # return configuration
return metaconfig return metaconfig
def servermodel_legacy(self, async def servermodel_legacy(self,
risotto_context: Context, risotto_context: Context,
servermodel_name: str, servermodel_name: str,
servermodel_id: int, servermodel_id: int,
servermodel_parent_id: int) -> None: servermodel_parent_id: int) -> None:
""" Make link between parent and children """ Make link between parent and children
""" """
if servermodel_parent_id is None: if servermodel_parent_id is None:
@ -173,7 +155,7 @@ class Risotto(Controller):
msg) msg)
return return
servermodel_parent = self.servermodel[servermodel_parent_id] servermodel_parent = self.servermodel[servermodel_parent_id]
servermodel_parent_name = servermodel_parent.information.get('servermodel_name') servermodel_parent_name = await servermodel_parent.information.get('servermodel_name')
if DEBUG: if DEBUG:
msg = _(f'Create legacy of servermodel {servermodel_name} ({servermodel_id}) with parent {servermodel_parent_name} ({servermodel_parent_id})') msg = _(f'Create legacy of servermodel {servermodel_name} ({servermodel_id}) with parent {servermodel_parent_name} ({servermodel_parent_id})')
log.info_msg(risotto_context, log.info_msg(risotto_context,
@ -181,9 +163,9 @@ class Risotto(Controller):
msg) msg)
# do link # do link
mix = servermodel_parent.config.get('m_v_' + str(servermodel_parent_id)) mix = await servermodel_parent.config.get('m_v_' + str(servermodel_parent_id))
try: try:
mix.config.add(self.servermodel[servermodel_id]) await mix.config.add(self.servermodel[servermodel_id])
except Exception as err: except Exception as err:
if DEBUG: if DEBUG:
log.error_msg(risotto_context, log.error_msg(risotto_context,
@ -203,84 +185,84 @@ class Risotto(Controller):
# loads servers # loads servers
for server in servers: for server in servers:
try: try:
self.load_server(risotto_context, await self.load_server(risotto_context,
server['server_id'], server['server_id'],
server['servername'], server['server_name'],
server['servermodelid']) server['server_servermodel_id'])
except Exception as err: except Exception as err:
if DEBUG: if DEBUG:
print_exc() print_exc()
servername = server['servername'] server_name = server['server_name']
server_id = server['server_id'] server_id = server['server_id']
msg = _(f'unable to load server {servername} ({server_id}): {err}') msg = _(f'unable to load server {server_name} ({server_id}): {err}')
log.error_msg(risotto_context, log.error_msg(risotto_context,
None, None,
msg) msg)
def load_server(self, async def load_server(self,
risotto_context: Context, risotto_context: Context,
server_id: int, server_id: int,
servername: str, server_name: str,
servermodelid: int) -> None: server_servermodel_id: int) -> None:
""" Loads a server """ Loads a server
""" """
if server_id in self.server: if server_id in self.server:
return return
log.info_msg(risotto_context, log.info_msg(risotto_context,
None, None,
f'Load server {servername} ({server_id})') f'Load server {server_name} ({server_id})')
if not servermodelid in self.servermodel: if not server_servermodel_id in self.servermodel:
msg = f'unable to find servermodel with id {servermodelid}' msg = f'unable to find servermodel with id {server_servermodel_id}'
log.error_msg(risotto_context, log.error_msg(risotto_context,
None, None,
msg) msg)
raise CallError(msg) raise CallError(msg)
# check if server was already created # check if server was already created
session_id = f's_{server_id}' session_id = f's_{server_id}'
# get the servermodel's metaconfig # get the servermodel's metaconfig
metaconfig = self.servermodel[servermodelid] metaconfig = self.servermodel[server_servermodel_id]
# create server configuration and server 'to deploy' configuration and store it # create server configuration and server 'to deploy' configuration and store it
self.server[server_id] = {'server': self.build_config(session_id, self.server[server_id] = {'server': await self.build_config(session_id,
server_id, server_id,
servername, server_name,
metaconfig), metaconfig),
'server_to_deploy': self.build_config(f'std_{server_id}', 'server_to_deploy': await self.build_config(f'std_{server_id}',
server_id, server_id,
servername, server_name,
metaconfig), metaconfig),
'funcs_file': self.get_funcs_filename(servermodelid)} 'funcs_file': self.get_funcs_filename(server_servermodel_id)}
def build_config(self, async def build_config(self,
session_id: str, session_id: str,
server_id: int, server_id: int,
servername: str, server_name: str,
metaconfig: MetaConfig) -> None: metaconfig: MetaConfig) -> None:
""" build server's config """ build server's config
""" """
config = metaconfig.config.new(session_id, config = await metaconfig.config.new(session_id,
storage=self.save_storage, storage=self.save_storage,
persistent=True) persistent=True)
config.information.set('server_id', server_id) await config.information.set('server_id', server_id)
config.information.set('server_name', servername) await config.information.set('server_name', server_name)
config.owner.set(servername) await config.owner.set(server_name)
config.property.read_only() await config.property.read_only()
return config return config
@register('v1.server.created') @register('v1.server.created')
async def server_created(self, async def server_created(self,
risotto_context: Context, risotto_context: Context,
server_id: int, server_id: int,
servername: str, server_name: str,
servermodelid: int) -> None: server_servermodel_id: int) -> None:
""" Loads server's configuration when a new server is created """ Loads server's configuration when a new server is created
""" """
self.load_server(risotto_context, await self.load_server(risotto_context,
server_id, server_id,
servername, server_name,
servermodelid) server_servermodel_id)
@register('v1.server.deleted') @register('v1.server.deleted')
async def server_deleted(self, async def server_deleted(self,
@ -288,99 +270,99 @@ class Risotto(Controller):
# delete config to it's parents # delete config to it's parents
for server_type in ['server', 'server_to_deploy']: for server_type in ['server', 'server_to_deploy']:
config = self.server[server_id]['server'] config = self.server[server_id]['server']
for parent in config.config.parents(): for parent in await config.config.parents():
parent.config.pop(config.config.name()) await parent.config.pop(await config.config.name())
delete_session(storage=self.save_storage, delete_session(storage=self.save_storage,
session_id=config.config.name()) session_id=await config.config.name())
# delete metaconfig # delete metaconfig
del self.server[server_id] del self.server[server_id]
@register('v1.servermodel.created') @register('v1.servermodel.created')
async def servermodel_created(self, async def servermodel_created(self,
risotto_context: Context, risotto_context: Context,
servermodelid: int, servermodel_id: int,
servermodelname: str, servermodel_name: str,
servermodelparentsid: List[int]) -> None: servermodel_parents_id: List[int]) -> None:
""" when servermodels are created, load it and do link """ when servermodels are created, load it and do link
""" """
await self.load_and_link_servermodel(risotto_context, await self.load_and_link_servermodel(risotto_context,
servermodelid, servermodel_id,
servermodelname, servermodel_name,
servermodelparentsid) servermodel_parents_id)
async def load_and_link_servermodel(self, async def load_and_link_servermodel(self,
risotto_context: Context, risotto_context: Context,
servermodelid: int, servermodel_id: int,
servermodelname: str, servermodel_name: str,
servermodelparentsid: List[int]) -> None: servermodel_parents_id: List[int]) -> None:
await self.load_servermodel(risotto_context, await self.load_servermodel(risotto_context,
servermodelid, servermodel_id,
servermodelname) servermodel_name)
if servermodelparentsid is not None: if servermodel_parents_id is not None:
for servermodelparentid in servermodelparentsid: for servermodelparentid in servermodel_parents_id:
self.servermodel_legacy(risotto_context, await self.servermodel_legacy(risotto_context,
servermodelname, servermodel_name,
servermodelid, servermodel_id,
servermodelparentid) servermodelparentid)
def servermodel_delete(self, async def servermodel_delete(self,
servermodelid: int) -> List[MetaConfig]: servermodel_id: int) -> List[MetaConfig]:
metaconfig = self.servermodel.pop(servermodelid) metaconfig = self.servermodel.pop(servermodel_id)
mixconfig = next(metaconfig.config.list()) mixconfig = await metaconfig.config.list()[0]
children = [] children = []
for child in mixconfig.config.list(): for child in await mixconfig.config.list():
children.append(child) children.append(child)
mixconfig.config.pop(child.config.name()) await mixconfig.config.pop(await child.config.name())
metaconfig.config.pop(mixconfig.config.name()) await metaconfig.config.pop(await mixconfig.config.name())
delete_session(storage=self.save_storage, delete_session(storage=self.save_storage,
session_id=mixconfig.config.name()) session_id=await mixconfig.config.name())
del mixconfig del mixconfig
for parent in metaconfig.config.parents(): for parent in await metaconfig.config.parents():
parent.config.pop(metaconfig.config.name()) await parent.config.pop(await metaconfig.config.name())
delete_session(storage=self.save_storage, delete_session(storage=self.save_storage,
session_id=metaconfig.config.name()) session_id=await metaconfig.config.name())
return children return children
#
@register('v1.servermodel.updated') # @register('v1.servermodel.updated')
async def servermodel_updated(self, # async def servermodel_updated(self,
risotto_context: Context, # risotto_context: Context,
servermodelid: int, # servermodel_id: int,
servermodelname: str, # servermodel_name: str,
servermodelparentsid: List[int]) -> None: # servermodel_parents_id: List[int]) -> None:
log.info_msg(risotto_context, # log.info_msg(risotto_context,
None, # None,
f'Reload servermodel {servermodelname} ({servermodelid})') # f'Reload servermodel {servermodel_name} ({servermodel_id})')
# unlink cache to force download new aggregated file # # unlink cache to force download new aggregated file
cache_file = join(ROOT_CACHE_DIR, str(servermodelid)+".xml") # cache_file = join(self.cache_root_path, str(servermodel_id)+".xml")
if isfile(cache_file): # if isfile(cache_file):
unlink(cache_file) # unlink(cache_file)
#
# store all informations # # store all informations
if servermodelid in self.servermodel: # if servermodel_id in self.servermodel:
old_values = self.servermodel[servermodelid].value.exportation() # old_values = await self.servermodel[servermodel_id].value.exportation()
old_permissives = self.servermodel[servermodelid].permissive.exportation() # old_permissives = await self.servermodel[servermodel_id].permissive.exportation()
old_properties = self.servermodel[servermodelid].property.exportation() # old_properties = await self.servermodel[servermodel_id].property.exportation()
children = self.servermodel_delete(servermodelid) # children = await self.servermodel_delete(servermodel_id)
else: # else:
old_values = None # old_values = None
#
# create new one # # create new one
await self.load_and_link_servermodel(risotto_context, # await self.load_and_link_servermodel(risotto_context,
servermodelid, # servermodel_id,
servermodelname, # servermodel_name,
servermodelparentsid) # servermodel_parents_id)
#
# migrates informations # # migrates informations
if old_values is not None: # if old_values is not None:
self.servermodel[servermodelid].value.importation(old_values) # await self.servermodel[servermodel_id].value.importation(old_values)
self.servermodel[servermodelid].permissive.importation(old_permissives) # await self.servermodel[servermodel_id].permissive.importation(old_permissives)
self.servermodel[servermodelid].property.importation(old_properties) # await self.servermodel[servermodel_id].property.importation(old_properties)
for child in children: # for child in children:
self.servermodel_legacy(risotto_context, # await self.servermodel_legacy(risotto_context,
child.information.get('servermodel_name'), # await child.information.get('servermodel_name'),
child.information.get('servermodel_id'), # await child.information.get('servermodel_id'),
servermodelid) # servermodel_id)
@register('v1.config.configuration.server.get', None) @register('v1.config.configuration.server.get', None)
async def get_configuration(self, async def get_configuration(self,
@ -398,9 +380,9 @@ class Risotto(Controller):
else: else:
server = self.server[server_id]['server_to_deploy'] server = self.server[server_id]['server_to_deploy']
server.property.read_only() await server.property.read_only()
try: try:
configuration = server.value.dict(fullpath=True) configuration = await server.value.dict(fullpath=True)
except: except:
if deployed: if deployed:
msg = _(f'No configuration available for server {server_id}') msg = _(f'No configuration available for server {server_id}')
@ -423,19 +405,19 @@ class Risotto(Controller):
config_std = self.server[server_id]['server_to_deploy'] config_std = self.server[server_id]['server_to_deploy']
# when deploy, calculate force_store_value # when deploy, calculate force_store_value
ro = config_std.property.getdefault('read_only', 'append') ro = await config_std.property.getdefault('read_only', 'append')
if 'force_store_value' not in ro: if 'force_store_value' not in ro:
ro = frozenset(list(ro) + ['force_store_value']) ro = frozenset(list(ro) + ['force_store_value'])
config_std.property.setdefault(ro, 'read_only', 'append') await config_std.property.setdefault(ro, 'read_only', 'append')
rw = config_std.property.getdefault('read_write', 'append') rw = await config_std.property.getdefault('read_write', 'append')
rw = frozenset(list(rw) + ['force_store_value']) rw = frozenset(list(rw) + ['force_store_value'])
config_std.property.setdefault(rw, 'read_write', 'append') await config_std.property.setdefault(rw, 'read_write', 'append')
config_std.property.add('force_store_value') await config_std.property.add('force_store_value')
# copy informations from server 'to deploy' configuration to server configuration # copy informations from server 'to deploy' configuration to server configuration
config.value.importation(config_std.value.exportation()) await config.value.importation(await config_std.value.exportation())
config.permissive.importation(config_std.permissive.exportation()) await config.permissive.importation(await config_std.permissive.exportation())
config.property.importation(config_std.property.exportation()) await config.property.importation(await config_std.property.exportation())
return {'server_id': server_id, return {'server_id': server_id,
'deployed': True} 'deployed': True}

View File

@ -1,8 +1,56 @@
from typing import Dict
from ...controller import Controller from ...controller import Controller
from ...register import register from ...register import register
from ...context import Context
from ...utils import _
class Risotto(Controller): class Risotto(Controller):
@register('v1.server.list', None) @register('v1.server.list', None, database=True)
async def server_list(self): async def server_list(self,
return [{'server_id': 1, 'servername': 'one', 'serverdescription': 'the first', 'servermodelid': 1}] risotto_context: Context) -> Dict:
sql = '''
SELECT ServerId as server_id, ServerName as server_name, ServerDescription as server_description, ServerServermodelId as server_servermodel_id
FROM Server
'''
servers = await risotto_context.connection.fetch(sql)
return [dict(r) for r in servers]
@register('v1.server.create', 'v1.server.created', database=True)
async def server_create(self,
risotto_context: Context,
server_name: str,
server_description: str,
server_servermodel_id: int) -> Dict:
await self.call('v1.servermodel.get_by_id',
risotto_context,
servermodel_id=server_servermodel_id)
server_insert = """INSERT INTO Server(ServerName, ServerDescription, ServerServermodelId)
VALUES ($1,$2,$3)
RETURNING ServerId
"""
server_id = await risotto_context.connection.fetchval(server_insert,
server_name,
server_description,
server_servermodel_id)
return {'server_id': server_id,
'server_name': server_name,
'server_description': server_description,
'server_servermodel_id': server_servermodel_id}
@register('v1.server.describe', None, database=True)
async def server_describe(self,
risotto_context: Context,
server_name: str) -> Dict:
sql = '''
SELECT ServerId as server_id, ServerName as server_name, ServerDescription as server_description, ServerServermodelId as server_servermodel_id
FROM Server
WHERE ServerName = $1
'''
server = await risotto_context.connection.fetchrow(sql,
server_name)
if not server:
raise Exception(_(f'unable to find server with name {server_name}'))
return dict(server)

View File

@ -1,65 +1,393 @@
from shutil import rmtree, copyfile
from os import listdir, makedirs
from os.path import join, isdir, isfile
from yaml import load, SafeLoader
from traceback import print_exc
from typing import Dict, List, Optional
from rougail import CreoleObjSpace
from rougail.config import dtdfilename
from ...controller import Controller from ...controller import Controller
from ...register import register from ...register import register
from ...utils import _
from ...context import Context
from ...config import get_config
from ...error import ExecutionError
from ...logger import log
class Risotto(Controller): class Risotto(Controller):
@register('v1.servermodel.list', None) def __init__(self,
async def servermodel_list(self, sourceid): test: bool) -> None:
return [{'servermodelid': 1, self.source_root_path = get_config().get('source').get('root_path')
'servermodelname': 'name', self.cache_root_path = join(get_config().get('cache').get('root_path'), 'servermodel')
'subreleasename': 'name', if not isdir(self.cache_root_path):
'sourceid': 1, makedirs(join(self.cache_root_path))
'servermodeldescription': 'description'}]
@register('v1.servermodel.describe', None) async def on_join(self,
async def servermodel_describe(self, inheritance, creolefuncs, servermodelid, schema, conffiles, resolvdepends, probes): risotto_context: Context) -> None:
schema = """<?xml version='1.0' encoding='UTF-8'?> internal_source = await self.call('v1.source.create',
<creole> risotto_context,
<family name="containers"> source_name='internal',
<family name="container0" doc="test"> source_url='none')
<family doc="files" name="files"> internal_release = await self.call('v1.source.release.create',
<family doc="file0" name="file0"> risotto_context,
<variable doc="" multi="False" name="mkdir" type="boolean"> source_id=internal_source['source_id'],
<value>False</value> release_name='none',
</variable> release_distribution='stable')
<variable doc="" multi="False" name="name" type="string"> self.internal_release_id = internal_release['release_id']
<value>/etc/mailname</value>
</variable> def servermodel_gen_funcs(self,
<variable doc="" multi="False" name="rm" type="boolean"> servermodel_name: str,
<value>False</value> servermodel_id: int,
</variable> dependencies: Dict,
<variable doc="" multi="False" name="source" type="string"> release_cache: Dict) -> None:
<value>mailname</value> as_names = []
</variable> dest_file = self.get_servermodel_cache(servermodel_id, 'funcs.py')
<variable doc="" multi="False" name="activate" type="boolean"> with open(dest_file, 'wb') as funcs:
<value>True</value> funcs.write(b'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\n\n')
</variable> for applicationservice_id, applicationservice_infos in dependencies.items():
</family> applicationservice_name, as_release_id = applicationservice_infos
</family> path = join(self.source_root_path,
<property>basic</property> release_cache[as_release_id]['source_name'],
</family> release_cache[as_release_id]['release_name'],
</family> 'applicationservice',
<family doc="" name="creole"> applicationservice_name,
<family doc="general" name="general"> 'funcs')
<property>normal</property> if isdir(path):
<variable doc="No change" multi="False" name="mode_conteneur_actif" type="choice"> as_names.append(applicationservice_name)
<choice type="string">oui</choice> for fil in listdir(path):
<choice type="string">non</choice> if not fil.endswith('.py'):
<property>mandatory</property> continue
<property>normal</property> fil_path = join(path, fil)
<value type="string">non</value> with open(fil_path, 'rb') as fh:
</variable> funcs.write(f'# {fil_path}\n'.encode())
<leader doc="master" name="master"> funcs.write(fh.read())
<property>normal</property> funcs.write(b'\n')
<variable doc="master" multi="True" name="master" type="string"/>
<variable doc="slave1" multi="True" name="slave1" type="string"> as_names_str = '", "'.join(as_names)
<property>normal</property> log.info(_(f'gen funcs for "{servermodel_name}" with application services "{as_names_str}"'))
</variable> eolobj = CreoleObjSpace(dtdfilename)
<variable doc="slave2" multi="True" name="slave2" type="string">
<property>normal</property> def servermodel_gen_schema(self,
</variable> servermodel_name: str,
</leader> servermodel_id: int,
</family> dependencies: Dict,
<separators/> release_cache: Dict) -> None:
</family> paths = []
</creole>""" extras = []
return {'servermodelid': 1, 'servermodelname': 'name', 'servermodeldescription': 'description', 'subreleasename': 'name', 'sourceid': 1, 'schema': schema, 'creolefuncs': ''} as_names = set()
for applicationservice_id, applicationservice_infos in dependencies.items():
applicationservice_name, as_release_id = applicationservice_infos
# load creole dictionaries
path = join(self.source_root_path,
release_cache[as_release_id]['source_name'],
release_cache[as_release_id]['release_name'],
'applicationservice',
applicationservice_name,
'dictionaries')
if isdir(path):
as_names.add(applicationservice_name)
paths.append(path)
# load extra dictionaries
path = join(self.source_root_path,
release_cache[as_release_id]['source_name'],
release_cache[as_release_id]['release_name'],
'applicationservice',
applicationservice_name,
'extras')
if isdir(path):
for namespace in listdir(path):
extra_dir = join(path, namespace)
if not isdir(extra_dir):
continue
as_names.add(applicationservice_name)
extras.append((namespace, [extra_dir]))
eolobj = CreoleObjSpace(dtdfilename)
as_names_str = '", "'.join(as_names)
log.info(_(f'gen schema for "{servermodel_name}" with application services "{as_names_str}"'))
eolobj.create_or_populate_from_xml('creole', paths)
for extra in extras:
eolobj.create_or_populate_from_xml(extra[0], extra[1])
# FIXME extra
funcs_file = self.get_servermodel_cache(servermodel_id, 'funcs.py')
eolobj.space_visitor(funcs_file)
dest_dir = self.get_servermodel_cache(servermodel_id, 'dictionaries.xml')
eolobj.save(dest_dir)
def get_servermodel_cache(self,
servermodel_id: int,
subdir: Optional[str]=None) -> str:
if subdir:
return join(self.cache_root_path, str(servermodel_id), subdir)
return join(self.cache_root_path, str(servermodel_id))
def servermodel_copy_templates(self,
servermodel_name: str,
servermodel_id: int,
dependencies: Dict,
release_cache: Dict) -> None:
as_names = []
dest_dir = self.get_servermodel_cache(servermodel_id, 'templates')
makedirs(dest_dir)
for applicationservice_id, applicationservice_infos in dependencies.items():
applicationservice_name, as_release_id = applicationservice_infos
path = join(self.source_root_path,
release_cache[as_release_id]['source_name'],
release_cache[as_release_id]['release_name'],
'applicationservice',
applicationservice_name,
'templates')
if isdir(path):
for template in listdir(path):
template_path = join(dest_dir, template)
if isfile(template_path):
as_names_str = '", "'.join(as_names)
raise Exception(_(f'duplicate "{template}" when copying template from "{applicationservice_name}" to "{dest_dir}" for servermodel "{servermodel_name}" (previous application services was "{as_names_str}"'))
copyfile(join(path, template), template_path)
as_names.append(applicationservice_name)
as_names_str = '", "'.join(as_names)
log.info(_(f'copy templates for "{servermodel_name}" with application services "{as_names_str}"'))
async def _servermodel_create(self,
risotto_context: Context,
servermodel_name: str,
servermodel_description: str,
servermodel_parents_id: List[int],
dependencies: List[int],
release_id: int,
release_cache: Dict=None) -> Dict:
servermodel_update = """INSERT INTO Servermodel(ServermodelName, ServermodelDescription, ServermodelParentsId, ServermodelReleaseId, ServermodelApplicationServiceId)
VALUES ($1,$2,$3,$4,$5)
RETURNING ServermodelId
"""
as_name = f"local_{servermodel_name}"
as_description = f'local application service for {servermodel_name}'
applicationservice = await self.call('v1.applicationservice.create',
risotto_context,
applicationservice_name=as_name,
applicationservice_description=as_description,
applicationservice_dependencies=dependencies,
release_id=self.internal_release_id)
applicationservice_id = applicationservice['applicationservice_id']
servermodel_id = await risotto_context.connection.fetchval(servermodel_update,
servermodel_name,
servermodel_description,
servermodel_parents_id,
release_id,
applicationservice_id)
dest_dir = self.get_servermodel_cache(servermodel_id)
if isdir(dest_dir):
rmtree(dest_dir)
makedirs(dest_dir)
# get all dependencies for this application service
dependencies = await self.get_applicationservices(risotto_context,
applicationservice_id)
# build cache to have all release informations
if release_cache is None:
release_cache = {}
for applicationservice_id, applicationservice_infos in dependencies.items():
applicationservice_name, as_release_id = applicationservice_infos
if as_release_id not in release_cache:
release_cache[as_release_id] = await self.call('v1.source.release.get_by_id',
risotto_context,
release_id=as_release_id)
self.servermodel_gen_funcs(servermodel_name,
servermodel_id,
dependencies,
release_cache)
self.servermodel_gen_schema(servermodel_name,
servermodel_id,
dependencies,
release_cache)
self.servermodel_copy_templates(servermodel_name,
servermodel_id,
dependencies,
release_cache)
sm_dict = {'servermodel_name': servermodel_name,
'servermodel_description': servermodel_description,
'servermodel_parents_id': servermodel_parents_id,
'release_id': release_id,
'servermodel_id': servermodel_id}
await self.publish('v1.servermodel.created',
risotto_context,
**sm_dict)
return sm_dict
def parse_parents(self,
servermodels: Dict,
servermodel: Dict,
parents: List=None) -> List:
if parents is None:
parents = [servermodel['name']]
parent = servermodel['parent']
if parent in servermodels:
parents.append(parent)
self.parse_parents(servermodels, servermodels[parent], parents)
return parents
async def get_servermodel_id_by_name(self,
risotto_context: Context,
servermodel_name: str,
release_id: int):
sql = 'SELECT ServermodelId as servermodel_id FROM Servermodel WHERE ServermodelName = $1 AND ServermodelReleaseId = $2',
return await risotto_context.connection.fetchval(sql,
servermodel_name,
release_id)['servermodel_id']
@register('v1.servermodel.dataset.updated', None, database=True)
async def servermodel_update(self,
risotto_context: Context,
source_name: str,
release_distribution: int):
source = await self.call('v1.source.describe',
risotto_context,
source_name=source_name)
release = await self.call('v1.source.release.get_by_distribution',
risotto_context,
source_id=source['source_id'],
release_distribution=release_distribution)
release_id = release['release_id']
servermodel_path = join(self.source_root_path,
source_name,
release['release_name'],
'servermodel')
servermodels = {}
for servermodel in listdir(servermodel_path):
if not servermodel.endswith('.yml'):
continue
servermodel_description_path = join(servermodel_path, servermodel)
try:
with open(servermodel_description_path, 'r') as servermodel_yml:
servermodel_description = load(servermodel_yml,
Loader=SafeLoader)
except Exception as err:
if get_config().get('global').get('debug'):
print_exc()
raise ExecutionError(_(f'Error while reading {servermodel_description_path}: {err}'))
servermodels[servermodel_description['name']] = servermodel_description
servermodels[servermodel_description['name']]['done'] = False
release_cache = {release['release_id']: release}
for servermodel in servermodels.values():
if not servermodel['done']:
# parent needs to create before child, so retrieve all parents
parents = self.parse_parents(servermodels,
servermodel)
parents.reverse()
servermodelparent_id = []
for new_servermodel in parents:
if not servermodels[new_servermodel]['done']:
servermodel_description = servermodels[new_servermodel]
parent = servermodel_description['parent']
if not servermodelparent_id and parent is not None:
# parent is a str, so get ID
servermodelparent_id = [await self.get_servermodel_id_by_name(risotto_context,
parent,
release_id)]
# link application service with this servermodel
dependencies = []
for depend in servermodels[new_servermodel]['applicationservices']:
applicationservice = await self.call('v1.applicationservice.describe',
risotto_context,
applicationservice_name=depend,
release_id=release_id)
dependencies.append(applicationservice['applicationservice_id'])
sm_name = servermodel_description['name']
sm_description = servermodel_description['description']
try:
servermodel_ob = await self._servermodel_create(risotto_context,
sm_name,
sm_description,
servermodelparent_id,
dependencies,
release_id,
release_cache)
servermodel_id = servermodel_ob['servermodel_id']
except Exception as err:
if get_config().get('global').get('debug'):
print_exc()
raise ExecutionError(_(f"Error while injecting servermodel {sm_name} in database: {err}"))
servermodelparent_id = [servermodel_id]
servermodel_description['done'] = True
return {'retcode': 0, 'returns': _('Servermodels successfully loaded')}
@register('v1.servermodel.list', None, database=True)
async def servermodel_list(self,
risotto_context: Context,
source_id: int):
sql = '''
SELECT ServermodelId as servermodel_id, ServermodelName as servermodel_name, ServermodelDescription as servermodel_description, ServermodelParentsId as servermodel_parents_id, ServermodelReleaseId as release_id
FROM Servermodel
'''
servermodels = await risotto_context.connection.fetch(sql)
return [dict(r) for r in servermodels]
@register('v1.servermodel.get_by_id', None, database=True)
async def servermodel_get_by_id(self,
risotto_context: Context,
servermodel_id: int):
sql = '''
SELECT ServermodelId as servermodel_id, ServermodelName as servermodel_name, ServermodelDescription as servermodel_description, ServermodelParentsId as servermodel_parents_id, ServermodelReleaseId as release_id
FROM Servermodel
WHERE ServermodelId=$1
'''
servermodel = await risotto_context.connection.fetchrow(sql,
servermodel_id)
if not servermodel:
raise Exception(_(f'{servermodel_id} is not a valid ID for a servermodel'))
return dict(servermodel)
async def _parse_depends(self,
risotto_context: Context,
applicationservice_id: int,
or_depends: list,
ids: list) -> None:
applicationservice = await self.call('v1.applicationservice.get_by_id',
risotto_context,
applicationservice_id=applicationservice_id)
ids[applicationservice_id] = (applicationservice['applicationservice_name'],
applicationservice['applicationservice_release_id'])
for depend in applicationservice['applicationservice_dependencies']:
if isinstance(depend, dict):
or_depends.append(depend['or'])
elif depend not in ids:
await self._parse_depends(risotto_context,
depend,
or_depends,
ids)
async def _parse_or_depends(self,
risotto_context: Context,
or_depends: list,
ids: list) -> None:
new_or_depends = []
set_ids = set(ids)
for or_depend in or_depends:
if not set(or_depend) & set_ids:
applicationservice_id= or_depend[0]
await self._parse_depends(risotto_context,
applicationservice_id,
new_or_depends,
ids)
if new_or_depends:
await self._parse_or_depends(risotto_context,
new_or_depends,
ids)
async def get_applicationservices(self,
risotto_context: Context,
applicationservice_id: int) -> list:
"""Return consolidated dependencies or raise.
"""
or_depends = []
ids = {}
await self._parse_depends(risotto_context,
applicationservice_id,
or_depends,
ids)
await self._parse_or_depends(risotto_context,
or_depends,
ids)
return ids

View File

@ -9,7 +9,6 @@ from ...http import register as register_http
from ...config import DEBUG from ...config import DEBUG
from ...context import Context from ...context import Context
from ...utils import _ from ...utils import _
from ...error import CallError
from .storage import storage_server, storage_servermodel from .storage import storage_server, storage_servermodel
from ...controller import Controller from ...controller import Controller
from ...register import register from ...register import register
@ -17,7 +16,8 @@ from ...dispatcher import dispatcher
class Risotto(Controller): class Risotto(Controller):
def __init__(self): def __init__(self,
test):
self.modify_storage = Storage(engine='dictionary') self.modify_storage = Storage(engine='dictionary')
def get_storage(self, def get_storage(self,
@ -85,24 +85,25 @@ class Risotto(Controller):
# check if a session already exists # check if a session already exists
sessions = storage.get_sessions() sessions = storage.get_sessions()
for session in sessions.values(): for sess_id, session in sessions.items():
if sess['id'] == id: if session['id'] == id:
if sess['username'] == risotto_context.username: if session['username'] == risotto_context.username:
# same user so returns it # same user so returns it
return self.format_session(session['session_id'], session) return self.format_session(sess_id,
session)
else: else:
raise CallError(_(f'{username} already edits this configuration')) raise Exception(_(f'{username} already edits this configuration'))
# create a new session # create a new session
while True: while True:
session_id = 'z' + hexlify(urandom(23)).decode() session_id = 'z' + hexlify(urandom(23)).decode()
if not session_id in sessions: if not session_id in sessions:
break break
storage.add_session(session_id, await storage.add_session(session_id,
config, config,
id, id,
risotto_context.username, risotto_context.username,
self.modify_storage) self.modify_storage)
# return session's information # return session's information
return self.get_session_informations(risotto_context, return self.get_session_informations(risotto_context,
@ -134,12 +135,12 @@ class Risotto(Controller):
namespace) namespace)
if mode is not None: if mode is not None:
if mode not in ('basic', 'normal', 'expert'): if mode not in ('basic', 'normal', 'expert'):
raise CallError(f'unknown mode {mode}') raise Exception(f'unknown mode {mode}')
storage.set_config_mode(session_id, await storage.set_config_mode(session_id,
mode) mode)
if debug is not None: if debug is not None:
storage.set_config_debug(session_id, await storage.set_config_debug(session_id,
debug) debug)
return self.get_session_informations(risotto_context, return self.get_session_informations(risotto_context,
session_id, session_id,
type) type)
@ -158,21 +159,23 @@ class Risotto(Controller):
session_id, session_id,
type) type)
# if multi and not follower the value is in fact in value_multi # if multi and not follower the value is in fact in value_multi
option = session['option'].option(name).option # FIXME option = session['option'].option(name).option
if option.ismulti() and not option.isfollower(): option = session['config'].option(name).option
if await option.ismulti() and not await option.isfollower():
value = value_multi value = value_multi
namespace = session['namespace'] #FIXME namespace = session['namespace']
update = {'name': f'{namespace}.{name}', #FIXME update = {'name': f'{namespace}.{name}',
update = {'name': name,
'action': action, 'action': action,
'value': value} 'value': value}
if index is not None: if index is not None:
update['index'] = index update['index'] = index
updates = {'updates': [update]} updates = {'updates': [update]}
ret = session['option'].updates(updates) ret = await session['option'].updates(updates)
if update['name'] in ret: if update['name'] in ret:
for val in ret[update['name']][index]: for val in ret[update['name']][index]:
if isinstance(val, ValueError): if isinstance(val, ValueError):
raise CallError(val) raise Exception(val)
ret = {'session_id': session_id, ret = {'session_id': session_id,
'name': name} 'name': name}
if index is not None: if index is not None:
@ -188,19 +191,23 @@ class Risotto(Controller):
session_id, session_id,
type) type)
try: try:
session['config'].forcepermissive.option(session['namespace']).value.dict() await session['config'].forcepermissive.option(session['namespace']).value.dict()
except Exception as err: except Exception as err:
raise CallError(str(err)) raise Exception(str(err))
if type == 'server': if type == 'server':
mandatories = list(session['config'].forcepermissive.value.mandatory()) config = session['config']
await config.property.read_only()
mandatories = list(await config.value.mandatory())
await config.property.read_write()
if mandatories: if mandatories:
# FIXME mandatories = [mandatory.split('.', 1)[1] for mandatory in mandatories]
if len(mandatories) == 1: if len(mandatories) == 1:
mandatories = mandatories[0] mandatories = mandatories[0]
msg = _('the parameter "--{mandatories}" is mandatory') msg = _(f'the parameter "--{mandatories}" is mandatory')
else: else:
mandatories = '", "--'.join(mandatories) mandatories = '", "--'.join(mandatories)
msg = _('parameters "{mandatories}" are mandatories') msg = _(f'parameters "--{mandatories}" are mandatories')
raise CallError(msg) raise Exception(msg)
return self.format_session(session_id, return self.format_session(session_id,
session) session)
@ -215,9 +222,11 @@ class Risotto(Controller):
type) type)
info = self.format_session(session_id, session) info = self.format_session(session_id, session)
if name is not None: if name is not None:
info['content'] = {name: session['option'].option(name).value.get()} content = {name: await session['config'].option(name).value.get()}
else: else:
info['content'] = session['option'].value.dict() content = await session['option'].value.dict(fullpath=True,
leader_to_list=True)
info['content'] = content
return info return info
@register(['v1.session.server.stop', 'v1.session.servermodel.stop'], None) @register(['v1.session.server.stop', 'v1.session.servermodel.stop'], None)
@ -237,8 +246,8 @@ class Risotto(Controller):
config = config_module.servermodel[id_] config = config_module.servermodel[id_]
if save: if save:
modif_config = session['config'] modif_config = session['config']
config.value.importation(modif_config.value.exportation()) await config.value.importation(await modif_config.value.exportation())
config.permissive.importation(modif_config.permissive.exportation()) await config.permissive.importation(await modif_config.permissive.exportation())
storage.del_session(session_id) storage.del_session(session_id)
return self.format_session(session_id, session) return self.format_session(session_id, session)
@ -249,7 +258,7 @@ class Risotto(Controller):
session_id: str) -> Dict: session_id: str) -> Dict:
session = storage_server.get_session(session_id, session = storage_server.get_session(session_id,
risotto_context.username) risotto_context.username)
return session['option'].dict(remotable='all') return await session['option'].dict(remotable='all')
@register_http('v1', '/config/servermodel/{session_id}') @register_http('v1', '/config/servermodel/{session_id}')
async def get_servermodel_api(self, async def get_servermodel_api(self,
@ -258,4 +267,4 @@ class Risotto(Controller):
session_id: str) -> Dict: session_id: str) -> Dict:
session = storage_servermodel.get_session(session_id, session = storage_servermodel.get_session(session_id,
risotto_context.username) risotto_context.username)
return session['option'].dict(remotable='all') return await session['option'].dict(remotable='all')

View File

@ -15,26 +15,26 @@ class Storage(object):
def __init__(self): def __init__(self):
self.sessions = {} self.sessions = {}
def add_session(self, async def add_session(self,
session_id: int, session_id: int,
orig_config: Config, orig_config: Config,
server_id: int, server_id: int,
username: str, username: str,
config_storage): config_storage):
prefix_id = f'{session_id}_' prefix_id = f'{session_id}_'
config_name = self.get_config_name(server_id) config_name = self.get_config_name(server_id)
config_id = f'{prefix_id}{config_name}' config_id = f'{prefix_id}{config_name}'
# copy Config and all it's parents # copy Config and all it's parents
meta = orig_config.config.deepcopy(session_id=config_id, meta = await orig_config.config.deepcopy(session_id=config_id,
storage=config_storage, storage=config_storage,
metaconfig_prefix=prefix_id) metaconfig_prefix=prefix_id)
# retrieve the copied config (not metaconfig) # retrieve the copied config (not metaconfig)
config = meta config = meta
while True: while True:
try: try:
children = list(config.config.list()) children = list(await config.config.list())
if not children: if not children:
# it's an empty metaconfig # it's an empty metaconfig
break break
@ -42,10 +42,10 @@ class Storage(object):
except: except:
# it's a config, so no "list" method # it's a config, so no "list" method
break break
config.property.read_write() await config.property.read_write()
# set the default owner # set the default owner
self.set_owner(config, await self.set_owner(config,
username) username)
# store it # store it
self.sessions[session_id] = {'config': config, self.sessions[session_id] = {'config': config,
@ -54,34 +54,34 @@ class Storage(object):
'id': server_id, 'id': server_id,
'timestamp': int(time.time()), 'timestamp': int(time.time()),
'username': username} 'username': username}
self.set_config_mode(session_id, await self.set_config_mode(session_id,
'normal') 'normal')
self.set_config_debug(session_id, await self.set_config_debug(session_id,
False) False)
self.set_namespace(session_id, self.set_namespace(session_id,
'creole') 'creole')
def set_config_mode(self, async def set_config_mode(self,
id: int, id: int,
mode: str): mode: str):
""" Define which edition mode to select """ Define which edition mode to select
""" """
config = self.sessions[id]['config'] config = self.sessions[id]['config']
for mode_level in modes.values(): for mode_level in modes.values():
if modes[mode] < mode_level: if modes[mode] < mode_level:
config.property.add(mode_level.name) await config.property.add(mode_level.name)
else: else:
config.property.pop(mode_level.name) await config.property.pop(mode_level.name)
self.sessions[id]['mode'] = mode self.sessions[id]['mode'] = mode
def set_config_debug(self, id_, is_debug): async def set_config_debug(self, id_, is_debug):
""" Enable/Disable debug mode """ Enable/Disable debug mode
""" """
config = self.sessions[id_]['config'] config = self.sessions[id_]['config']
if is_debug: if is_debug:
config.property.pop('hidden') await config.property.pop('hidden')
else: else:
config.property.add('hidden') await config.property.add('hidden')
self.sessions[id_]['debug'] = is_debug self.sessions[id_]['debug'] = is_debug
def set_namespace(self, def set_namespace(self,
@ -97,7 +97,7 @@ class Storage(object):
session_id: int, session_id: int,
username: str) -> Dict: username: str) -> Dict:
if session_id not in self.sessions: if session_id not in self.sessions:
raise Exception(f'the session {id} not exists') raise Exception(f'the session "{session_id}" not exists')
session = self.sessions[session_id] session = self.sessions[session_id]
if username != session['username']: if username != session['username']:
raise NotAllowedError() raise NotAllowedError()
@ -113,10 +113,10 @@ class StorageServer(Storage):
server_id: int): server_id: int):
return f'std_{server_id}' return f'std_{server_id}'
def set_owner(self, async def set_owner(self,
config: Config, config: Config,
username: str): username: str):
config.owner.set(username) await config.owner.set(username)
class StorageServermodel(Storage): class StorageServermodel(Storage):
@ -124,10 +124,10 @@ class StorageServermodel(Storage):
server_id: int): server_id: int):
return f'v_{server_id}' return f'v_{server_id}'
def set_owner(self, async def set_owner(self,
config: Config, config: Config,
username: str): username: str):
config.owner.set('servermodel_' + username) await config.owner.set('servermodel_' + username)
storage_server = StorageServer() storage_server = StorageServer()

View File

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

View File

@ -0,0 +1,145 @@
from typing import Dict, List
from ...controller import Controller
from ...register import register
from ...context import Context
import requests
import yaml
import os
from ...utils import _
from ...config import get_config
class Risotto(Controller):
@register('v1.source.create', None, database=True)
async def source_create(self,
risotto_context: Context,
source_name: str,
source_url: str) -> Dict:
source_upsert = """INSERT INTO Source(SourceName, SourceURL) VALUES ($1, $2)
ON CONFLICT (SourceName) DO UPDATE SET SourceURL = $2
RETURNING SourceId
"""
# If given url is not 'none' (a.k.a internal source)
# Look for file releases.yml at given url
# If such a file exists, consider source a valid one and create source in database.
if source_url != 'none':
try:
releases = yaml.load(requests.get(source_url.rstrip('/') + '/releases.yml').content, Loader=yaml.SafeLoader)
except requests.exceptions.ConnectionError as err:
raise Exception(_('Invalid URL'))
except yaml.scanner.ScannerError as err:
raise Exception(_('Invalid releases.yml file'))
except:
raise Exception(_('Invalid source'))
else:
releases = {'1.0.0': {'distribution': 'last'}}
os.makedirs(os.path.join(get_config().get('source').get('root_path'), source_name), exist_ok=True)
with open(os.path.join(get_config().get('source').get('root_path'), source_name, 'releases.yml'), 'w') as release_file:
yaml.dump(releases, release_file)
source_id = await risotto_context.connection.fetchval(source_upsert,
source_name,
source_url)
return {'source_name': source_name,
'source_url': source_url,
'source_id': source_id}
@register('v1.source.describe', None, database=True)
async def source_describe(self,
risotto_context: Context,
source_name: str) -> Dict:
source_get = """SELECT SourceId as source_id, SourceName as source_name, SourceURL as source_url
FROM Source
WHERE SourceName = $1
"""
source = await risotto_context.connection.fetchrow(source_get,
source_name)
if not source:
raise Exception(_(f'unknown source with name {source_name}'))
return dict(source)
@register('v1.source.list', None, database=True)
async def source_list(self,
risotto_context: Context) -> List[Dict]:
source_list = """SELECT SourceId as source_id, SourceName as source_name, SourceURL as source_url
FROM Source
"""
result = await risotto_context.connection.fetch(source_list)
return [dict(r) for r in result]
@register('v1.source.dataset.update', None, database=True)
async def version_update(self,
risotto_context: Context,
source_id: int,
release_name: str):
# source.release.create is an upsert, do not using it
release_insert = """INSERT INTO Release(ReleaseName, ReleaseSourceId) VALUES ($1, $2)
RETURNING ReleaseId
"""
release_id = await risotto_context.connection.fetchval(release_insert,
release_name,
source_id)
return {'release_id': release_id,
'release_name': release_name}
@register('v1.source.release.create', None, database=True)
async def source_release_create(self,
risotto_context: Context,
source_id: int,
release_name: str,
release_distribution: str) -> Dict:
source_get = """SELECT SourceId as source_id, SourceName as source_name, SourceURL as source_url
FROM Source
WHERE SourceId = $1
"""
release_upsert = """INSERT INTO Release(ReleaseName, ReleaseSourceId, ReleaseDistribution) VALUES ($1, $2, $3)
ON CONFLICT (ReleaseName, ReleaseSourceId) DO UPDATE SET ReleaseName = $1
RETURNING ReleaseId
"""
source = dict(await risotto_context.connection.fetchrow(source_get,
source_id))
release_id = await risotto_context.connection.fetchval(release_upsert,
release_name,
source_id,
release_distribution)
del source['source_id']
source['release_id'] = release_id
source['release_name'] = release_name
source['release_distribution'] = release_distribution
return source
@register('v1.source.release.list', None, database=True)
async def release_list(self,
risotto_context):
release_query = """SELECT ReleaseId as release_id, SourceName as source_name, SourceURL as source_url, ReleaseName as release_name, ReleaseDistribution as release_distribution
FROM Release, Source
WHERE Source.SourceId=Release.ReleaseSourceId"""
result = await risotto_context.connection.fetch(release_query)
return [dict(r) for r in result]
@register('v1.source.release.get_by_id', None, database=True)
async def release_get_by_id(self,
risotto_context: Context,
release_id: int) -> Dict:
release_query = """SELECT ReleaseId as release_id, SourceName as source_name, SourceURL as source_url, ReleaseName as release_name, ReleaseDistribution as release_distribution
FROM Release, Source
WHERE Release.ReleaseId = $1 AND Source.SourceId = Release.ReleaseSourceId"""
result = await risotto_context.connection.fetchrow(release_query,
release_id)
if not result:
raise Exception(_(f'unknown release id {release_id}'))
return dict(result)
@register('v1.source.release.get_by_distribution', None, database=True)
async def release_get_by_distribution(self,
risotto_context: Context,
source_id: int,
release_distribution: str) -> Dict:
release_query = """SELECT ReleaseId as release_id, SourceName as source_name, SourceURL as source_url, ReleaseName as release_name, ReleaseDistribution as release_distribution
FROM Release, Source
WHERE Release.ReleaseSourceId = $1 AND Release.ReleaseDistribution = $2 AND Source.SourceId = Release.ReleaseSourceId"""
result = await risotto_context.connection.fetchrow(release_query,
source_id,
release_distribution)
if not result:
raise Exception(_(f'unknown distribution {release_distribution} with source {source_id}'))
return dict(result)

View File

@ -1,21 +1,31 @@
from os import mkdir from os import mkdir
from os.path import isdir, join from os.path import isdir, join
from shutil import rmtree from shutil import rmtree
from typing import Dict
from rougail.template import generate from rougail.template import generate
from tiramisu import Storage from tiramisu import Storage
from ...config import ROOT_CACHE_DIR, CONFIGURATION_DIR, TEMPLATE_DIR, TMP_DIR from ...config import CONFIGURATION_DIR, TMP_DIR, get_config
from ...controller import Controller from ...controller import Controller
from ...register import register from ...register import register
from ...dispatcher import dispatcher from ...dispatcher import dispatcher
from ...utils import _
class Risotto(Controller): class Risotto(Controller):
def __init__(self): def __init__(self,
test: bool) -> None:
self.storage = Storage(engine='dictionary') self.storage = Storage(engine='dictionary')
self.cache_root_path = join(get_config().get('cache').get('root_path'), 'servermodel')
@register('v1.template.generate', None) @register('v1.template.generate', None)
async def template_get(self, async def template_get(self,
server_id: int): risotto_context,
server_name: str) -> Dict:
server = await self.call('v1.server.describe',
risotto_context,
server_name=server_name)
server_id = server['server_id']
servermodel_id = server['server_servermodel_id']
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
server = config_module.server[server_id] server = config_module.server[server_id]
config = meta = server['server'].config.deepcopy(storage=self.storage) config = meta = server['server'].config.deepcopy(storage=self.storage)
@ -28,7 +38,6 @@ class Risotto(Controller):
config = children[0] config = children[0]
else: else:
break break
print(config.value.dict())
configurations_dir = join(CONFIGURATION_DIR, configurations_dir = join(CONFIGURATION_DIR,
str(server_id)) str(server_id))
if isdir(configurations_dir): if isdir(configurations_dir):
@ -38,7 +47,7 @@ class Risotto(Controller):
if isdir(tmp_dir): if isdir(tmp_dir):
rmtree(tmp_dir) rmtree(tmp_dir)
mkdir(tmp_dir) mkdir(tmp_dir)
templates_dir = join(TEMPLATE_DIR, str(server_id)) templates_dir = join(self.cache_root_path, str(servermodel_id), 'templates')
generate(config, generate(config,
server['funcs_file'], server['funcs_file'],
templates_dir, templates_dir,

View File

@ -0,0 +1,50 @@
<?xml version='1.0' encoding='UTF-8'?>
<creole>
<family name="containers">
<family name="container0" doc="test">
<family doc="files" name="files">
<family doc="file0" name="file0">
<variable doc="" multi="False" name="mkdir" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="name" type="string">
<value>/etc/mailname</value>
</variable>
<variable doc="" multi="False" name="rm" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="source" type="string">
<value>mailname</value>
</variable>
<variable doc="" multi="False" name="activate" type="boolean">
<value>True</value>
</variable>
</family>
</family>
<property>basic</property>
</family>
</family>
<family doc="" name="creole">
<family doc="general" name="general">
<property>normal</property>
<variable doc="No change" multi="False" name="mode_conteneur_actif" type="choice">
<choice type="string">oui</choice>
<choice type="string">non</choice>
<property>mandatory</property>
<property>normal</property>
<value type="string">non</value>
</variable>
<leader doc="master" name="master">
<property>normal</property>
<variable doc="master" multi="True" name="master" type="string"/>
<variable doc="slave1" multi="True" name="slave1" type="string">
<property>normal</property>
</variable>
<variable doc="slave2" multi="True" name="slave2" type="string">
<property>normal</property>
</variable>
</leader>
</family>
<separators/>
</family>
</creole>

0
tests/data/1/funcs.py Normal file
View File

View File

@ -0,0 +1,50 @@
<?xml version='1.0' encoding='UTF-8'?>
<creole>
<family name="containers">
<family name="container0" doc="test">
<family doc="files" name="files">
<family doc="file0" name="file0">
<variable doc="" multi="False" name="mkdir" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="name" type="string">
<value>/etc/mailname</value>
</variable>
<variable doc="" multi="False" name="rm" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="source" type="string">
<value>mailname</value>
</variable>
<variable doc="" multi="False" name="activate" type="boolean">
<value>True</value>
</variable>
</family>
</family>
<property>basic</property>
</family>
</family>
<family doc="" name="creole">
<family doc="general" name="general">
<property>normal</property>
<variable doc="No change" multi="False" name="mode_conteneur_actif" type="choice">
<choice type="string">oui</choice>
<choice type="string">non</choice>
<property>mandatory</property>
<property>normal</property>
<value type="string">non</value>
</variable>
<leader doc="master" name="master">
<property>normal</property>
<variable doc="master" multi="True" name="master" type="string"/>
<variable doc="slave1" multi="True" name="slave1" type="string">
<property>normal</property>
</variable>
<variable doc="slave2" multi="True" name="slave2" type="string">
<property>normal</property>
</variable>
</leader>
</family>
<separators/>
</family>
</creole>

0
tests/data/2/funcs.py Normal file
View File

View File

@ -0,0 +1,50 @@
<?xml version='1.0' encoding='UTF-8'?>
<creole>
<family name="containers">
<family name="container0" doc="test">
<family doc="files" name="files">
<family doc="file0" name="file0">
<variable doc="" multi="False" name="mkdir" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="name" type="string">
<value>/etc/mailname</value>
</variable>
<variable doc="" multi="False" name="rm" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="source" type="string">
<value>mailname</value>
</variable>
<variable doc="" multi="False" name="activate" type="boolean">
<value>True</value>
</variable>
</family>
</family>
<property>basic</property>
</family>
</family>
<family doc="" name="creole">
<family doc="general" name="general">
<property>normal</property>
<variable doc="No change" multi="False" name="mode_conteneur_actif" type="choice">
<choice type="string">oui</choice>
<choice type="string">non</choice>
<property>mandatory</property>
<property>normal</property>
<value type="string">non</value>
</variable>
<leader doc="master" name="master">
<property>normal</property>
<variable doc="master" multi="True" name="master" type="string"/>
<variable doc="slave1" multi="True" name="slave1" type="string">
<property>normal</property>
</variable>
<variable doc="slave2" multi="True" name="slave2" type="string">
<property>normal</property>
</variable>
</leader>
</family>
<separators/>
</family>
</creole>

0
tests/data/3/funcs.py Normal file
View File

View File

@ -5,4 +5,4 @@ from risotto.register import register
class Risotto(Controller): class Risotto(Controller):
@register('v1.server.list', None) @register('v1.server.list', None)
async def server_list(self): async def server_list(self):
return [{'server_id': 3, 'servername': 'one', 'serverdescription': 'the first', 'servermodelid': 1}] return [{'server_id': 3, 'server_name': 'one', 'server_description': 'the first', 'server_servermodel_id': 1}]

View File

@ -3,69 +3,13 @@ from risotto.register import register
class Risotto(Controller): class Risotto(Controller):
@register('v1.servermodel.list', None) @register('v1.servermodel.list', None)
async def servermodel_list(self, sourceid): async def servermodel_list(self, source_id):
return [{'servermodelid': 1, return [{'servermodel_id': 1,
'servermodelname': 'name1', 'servermodel_name': 'name1',
'subreleasename': 'name1', 'release_id': 1,
'sourceid': 1, 'servermodel_description': 'description1'},
'servermodeldescription': 'description1'}, {'servermodel_id': 2,
{'servermodelid': 2, 'servermodel_name': 'name2',
'servermodelname': 'name2', 'release_id': 2,
'subreleasename': 'name2', 'servermodel_description': 'description2',
'sourceid': 2, 'servermodel_parents_id': [1]}]
'servermodeldescription': 'description2',
'servermodelparentsid': [1]}]
@register('v1.servermodel.describe', None)
async def servermodel_describe(self, inheritance, creolefuncs, servermodelid, schema, conffiles, resolvdepends, probes):
schema = """<?xml version='1.0' encoding='UTF-8'?>
<creole>
<family name="containers">
<family name="container0" doc="test">
<family doc="files" name="files">
<family doc="file0" name="file0">
<variable doc="" multi="False" name="mkdir" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="name" type="string">
<value>/etc/mailname</value>
</variable>
<variable doc="" multi="False" name="rm" type="boolean">
<value>False</value>
</variable>
<variable doc="" multi="False" name="source" type="string">
<value>mailname</value>
</variable>
<variable doc="" multi="False" name="activate" type="boolean">
<value>True</value>
</variable>
</family>
</family>
<property>basic</property>
</family>
</family>
<family doc="" name="creole">
<family doc="general" name="general">
<property>normal</property>
<variable doc="No change" multi="False" name="mode_conteneur_actif" type="choice">
<choice type="string">oui</choice>
<choice type="string">non</choice>
<property>mandatory</property>
<property>normal</property>
<value type="string">non</value>
</variable>
<leader doc="master" name="master">
<property>normal</property>
<variable doc="master" multi="True" name="master" type="string"/>
<variable doc="slave1" multi="True" name="slave1" type="string">
<property>normal</property>
</variable>
<variable doc="slave2" multi="True" name="slave2" type="string">
<property>normal</property>
</variable>
</leader>
</family>
<separators/>
</family>
</creole>"""
return {'servermodelid': 1, 'servermodelname': 'name', 'servermodeldescription': 'description', 'subreleasename': 'name', 'sourceid': 1, 'schema': schema, 'creolefuncs': ''}

5
tests/storage.py Normal file
View File

@ -0,0 +1,5 @@
from tiramisu import Storage
from risotto.config import DATABASE_DIR
STORAGE = Storage(engine='sqlite3', dir_database=DATABASE_DIR, name='test')

View File

@ -1,20 +1,20 @@
from importlib import import_module from importlib import import_module
import pytest import pytest
from tiramisu import Storage, list_sessions, delete_session from tiramisu import list_sessions, delete_session
from .storage import STORAGE
from risotto.context import Context from risotto.context import Context
from risotto.services import load_services from risotto.services import load_services
from risotto.dispatcher import dispatcher from risotto.dispatcher import dispatcher
from risotto.config import DATABASE_DIR
def setup_module(module): def setup_module(module):
load_services(['config'], load_services(['config'],
validate=False) validate=False)
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR, name='test') config_module.save_storage = STORAGE
dispatcher.set_module('server', import_module(f'.server', 'fake_services')) dispatcher.set_module('server', import_module(f'.server', 'fake_services'), True)
dispatcher.set_module('servermodel', import_module(f'.servermodel', 'fake_services')) dispatcher.set_module('servermodel', import_module(f'.servermodel', 'fake_services'), True)
def setup_function(function): def setup_function(function):
@ -45,17 +45,19 @@ async def test_on_join():
assert config_module.server == {} assert config_module.server == {}
# #
fake_context = get_fake_context('config') fake_context = get_fake_context('config')
config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
assert list(config_module.servermodel.keys()) == [1, 2] assert list(config_module.servermodel.keys()) == [1, 2]
assert list(config_module.server) == [3] assert list(config_module.server) == [3]
assert set(config_module.server[3]) == {'server', 'server_to_deploy', 'funcs_file'} assert set(config_module.server[3]) == {'server', 'server_to_deploy', 'funcs_file'}
assert config_module.server[3]['funcs_file'] == 'cache/1.creolefuncs' assert config_module.server[3]['funcs_file'] == 'tests/data/1/funcs.py'
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_server_created(): async def test_server_created():
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') fake_context = get_fake_context('config')
config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
# #
assert list(config_module.server) == [3] assert list(config_module.server) == [3]
@ -63,18 +65,19 @@ async def test_server_created():
'server.created', 'server.created',
fake_context, fake_context,
server_id=4, server_id=4,
servername='name3', server_name='name3',
serverdescription='description3', server_description='description3',
servermodelid=2) server_servermodel_id=2)
assert list(config_module.server) == [3, 4] assert list(config_module.server) == [3, 4]
assert set(config_module.server[4]) == {'server', 'server_to_deploy', 'funcs_file'} assert set(config_module.server[4]) == {'server', 'server_to_deploy', 'funcs_file'}
assert config_module.server[4]['funcs_file'] == 'cache/2.creolefuncs' assert config_module.server[4]['funcs_file'] == 'tests/data/2/funcs.py'
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_server_deleted(): async def test_server_deleted():
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') fake_context = get_fake_context('config')
config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
# #
assert list(config_module.server) == [3] assert list(config_module.server) == [3]
@ -82,9 +85,9 @@ async def test_server_deleted():
'server.created', 'server.created',
fake_context, fake_context,
server_id=4, server_id=4,
servername='name4', server_name='name4',
serverdescription='description4', server_description='description4',
servermodelid=2) server_servermodel_id=2)
assert list(config_module.server) == [3, 4] assert list(config_module.server) == [3, 4]
await dispatcher.publish('v1', await dispatcher.publish('v1',
'server.deleted', 'server.deleted',
@ -97,6 +100,7 @@ async def test_server_deleted():
async def test_servermodel_created(): async def test_servermodel_created():
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') fake_context = get_fake_context('config')
config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
# #
assert list(config_module.servermodel) == [1, 2] assert list(config_module.servermodel) == [1, 2]
@ -105,187 +109,186 @@ async def test_servermodel_created():
await dispatcher.publish('v1', await dispatcher.publish('v1',
'servermodel.created', 'servermodel.created',
fake_context, fake_context,
servermodelid=3, servermodel_id=3,
servermodeldescription='name3', servermodel_description='name3',
subreleasename='2.7.0', release_id=1,
sourceid=1, servermodel_name='name3')
servermodelname='name3')
assert list(config_module.servermodel) == [1, 2, 3] assert list(config_module.servermodel) == [1, 2, 3]
assert not list(config_module.servermodel[3].config.parents()) assert not list(await config_module.servermodel[3].config.parents())
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_servermodel_herited_created(): async def test_servermodel_herited_created():
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') fake_context = get_fake_context('config')
config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
# #
assert list(config_module.servermodel) == [1, 2] assert list(config_module.servermodel) == [1, 2]
await dispatcher.publish('v1', await dispatcher.publish('v1',
'servermodel.created', 'servermodel.created',
fake_context, fake_context,
servermodelid=3, servermodel_id=3,
servermodelname='name3', servermodel_name='name3',
subreleasename='2.7.0', release_id=1,
sourceid=1, servermodel_description='name3',
servermodeldescription='name3', servermodel_parents_id=[1])
servermodelparentsid=[1])
assert list(config_module.servermodel) == [1, 2, 3] assert list(config_module.servermodel) == [1, 2, 3]
assert len(list(config_module.servermodel[3].config.parents())) == 1 assert len(list(await config_module.servermodel[3].config.parents())) == 1
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_servermodel_multi_herited_created(): async def test_servermodel_multi_herited_created():
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') fake_context = get_fake_context('config')
config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
# #
assert list(config_module.servermodel) == [1, 2] assert list(config_module.servermodel) == [1, 2]
await dispatcher.publish('v1', await dispatcher.publish('v1',
'servermodel.created', 'servermodel.created',
fake_context, fake_context,
servermodelid=3, servermodel_id=3,
servermodelname='name3', servermodel_name='name3',
subreleasename='2.7.0', release_id=1,
sourceid=1, servermodel_description='name3',
servermodeldescription='name3', servermodel_parents_id=[1, 2])
servermodelparentsid=[1, 2])
assert list(config_module.servermodel) == [1, 2, 3] assert list(config_module.servermodel) == [1, 2, 3]
assert len(list(config_module.servermodel[3].config.parents())) == 2 assert len(list(await config_module.servermodel[3].config.parents())) == 2
@pytest.mark.asyncio #@pytest.mark.asyncio
async def test_servermodel_updated_not_exists(): #async def test_servermodel_updated_not_exists():
config_module = dispatcher.get_service('config') # config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') # fake_context = get_fake_context('config')
await config_module.on_join(fake_context) # config_module.cache_root_path = 'tests/data'
# # await config_module.on_join(fake_context)
assert list(config_module.servermodel) == [1, 2] # #
await dispatcher.publish('v1', # assert list(config_module.servermodel) == [1, 2]
'servermodel.updated', # await dispatcher.publish('v1',
fake_context, # 'servermodel.updated',
servermodelid=3, # fake_context,
servermodelname='name3', # servermodel_id=3,
subreleasename='2.7.0', # servermodel_name='name3',
sourceid=1, # release_id=1,
servermodeldescription='name3', # servermodel_description='name3',
servermodelparentsid=[1, 2]) # servermodel_parents_id=[1, 2])
assert list(config_module.servermodel) == [1, 2, 3] # assert list(config_module.servermodel) == [1, 2, 3]
assert len(list(config_module.servermodel[3].config.parents())) == 2 # assert len(list(await config_module.servermodel[3].config.parents())) == 2
#
#
@pytest.mark.asyncio # @pytest.mark.asyncio
async def test_servermodel_updated1(): # async def test_servermodel_updated1():
config_module = dispatcher.get_service('config') # config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') # fake_context = get_fake_context('config')
await config_module.on_join(fake_context) # config_module.cache_root_path = 'tests/data'
# # await config_module.on_join(fake_context)
assert list(config_module.servermodel) == [1, 2] # #
metaconfig1 = config_module.servermodel[1] # assert list(config_module.servermodel) == [1, 2]
metaconfig2 = config_module.servermodel[2] # metaconfig1 = config_module.servermodel[1]
mixconfig1 = next(metaconfig1.config.list()) # metaconfig2 = config_module.servermodel[2]
mixconfig2 = next(metaconfig2.config.list()) # mixconfig1 = (await metaconfig1.config.list())[0]
assert len(list(metaconfig1.config.parents())) == 0 # mixconfig2 = (await metaconfig2.config.list())[0]
assert len(list(metaconfig2.config.parents())) == 1 # assert len(list(await metaconfig1.config.parents())) == 0
assert len(list(mixconfig1.config.list())) == 1 # assert len(list(await metaconfig2.config.parents())) == 1
assert len(list(mixconfig2.config.list())) == 0 # assert len(list(await mixconfig1.config.list())) == 1
# # assert len(list(await mixconfig2.config.list())) == 0
await dispatcher.publish('v1', # #
'servermodel.updated', # await dispatcher.publish('v1',
fake_context, # 'servermodel.updated',
servermodelid=1, # fake_context,
servermodelname='name1-1', # servermodel_id=1,
subreleasename='2.7.0', # servermodel_name='name1-1',
sourceid=1, # release_id=1,
servermodeldescription='name1-1') # servermodel_description='name1-1')
assert set(config_module.servermodel) == {1, 2} # assert set(config_module.servermodel) == {1, 2}
assert config_module.servermodel[1].information.get('servermodel_name') == 'name1-1' # assert config_module.servermodel[1].information.get('servermodel_name') == 'name1-1'
assert metaconfig1 != config_module.servermodel[1] # assert metaconfig1 != config_module.servermodel[1]
assert metaconfig2 == config_module.servermodel[2] # assert metaconfig2 == config_module.servermodel[2]
metaconfig1 = config_module.servermodel[1] # metaconfig1 = config_module.servermodel[1]
assert mixconfig1 != next(metaconfig1.config.list()) # assert mixconfig1 != next(metaconfig1.config.list())
mixconfig1 = next(metaconfig1.config.list()) # mixconfig1 = next(metaconfig1.config.list())
# # #
assert len(list(metaconfig1.config.parents())) == 0 # assert len(list(await metaconfig1.config.parents())) == 0
assert len(list(metaconfig2.config.parents())) == 1 # assert len(list(await metaconfig2.config.parents())) == 1
assert len(list(mixconfig1.config.list())) == 1 # assert len(list(await mixconfig1.config.list())) == 1
assert len(list(mixconfig2.config.list())) == 0 # assert len(list(await mixconfig2.config.list())) == 0
#
#
@pytest.mark.asyncio # @pytest.mark.asyncio
async def test_servermodel_updated2(): # async def test_servermodel_updated2():
config_module = dispatcher.get_service('config') # config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') # fake_context = get_fake_context('config')
await config_module.on_join(fake_context) # config_module.cache_root_path = 'tests/data'
# create a new servermodel # await config_module.on_join(fake_context)
assert list(config_module.servermodel) == [1, 2] # # create a new servermodel
mixconfig1 = next(config_module.servermodel[1].config.list()) # assert list(config_module.servermodel) == [1, 2]
mixconfig2 = next(config_module.servermodel[2].config.list()) # mixconfig1 = next(config_module.servermodel[1].config.list())
assert len(list(mixconfig1.config.list())) == 1 # mixconfig2 = next(config_module.servermodel[2].config.list())
assert len(list(mixconfig2.config.list())) == 0 # assert len(list(mixconfig1.config.list())) == 1
await dispatcher.publish('v1', # assert len(list(mixconfig2.config.list())) == 0
'servermodel.created', # await dispatcher.publish('v1',
fake_context, # 'servermodel.created',
servermodelid=3, # fake_context,
servermodelname='name3', # servermodel_id=3,
subreleasename='2.7.0', # servermodel_name='name3',
sourceid=1, # release_id=1,
servermodeldescription='name3', # servermodel_description='name3',
servermodelparentsid=[1]) # servermodel_parents_id=[1])
assert list(config_module.servermodel) == [1, 2, 3] # assert list(config_module.servermodel) == [1, 2, 3]
assert len(list(config_module.servermodel[3].config.parents())) == 1 # assert len(list(await config_module.servermodel[3].config.parents())) == 1
assert config_module.servermodel[3].information.get('servermodel_name') == 'name3' # assert await config_module.servermodel[3].information.get('servermodel_name') == 'name3'
assert len(list(mixconfig1.config.list())) == 2 # assert len(list(await mixconfig1.config.list())) == 2
assert len(list(mixconfig2.config.list())) == 0 # assert len(list(await mixconfig2.config.list())) == 0
# # #
await dispatcher.publish('v1', # await dispatcher.publish('v1',
'servermodel.updated', # 'servermodel.updated',
fake_context, # fake_context,
servermodelid=3, # servermodel_id=3,
servermodelname='name3-1', # servermodel_name='name3-1',
subreleasename='2.7.0', # release_id=1,
sourceid=1, # servermodel_description='name3-1',
servermodeldescription='name3-1', # servermodel_parents_id=[1, 2])
servermodelparentsid=[1, 2]) # assert list(config_module.servermodel) == [1, 2, 3]
assert list(config_module.servermodel) == [1, 2, 3] # assert config_module.servermodel[3].information.get('servermodel_name') == 'name3-1'
assert config_module.servermodel[3].information.get('servermodel_name') == 'name3-1' # assert len(list(mixconfig1.config.list())) == 2
assert len(list(mixconfig1.config.list())) == 2 # assert len(list(mixconfig2.config.list())) == 1
assert len(list(mixconfig2.config.list())) == 1 #
#
# @pytest.mark.asyncio
@pytest.mark.asyncio # async def test_servermodel_updated_config():
async def test_servermodel_updated_config(): # config_module = dispatcher.get_service('config')
config_module = dispatcher.get_service('config') # fake_context = get_fake_context('config')
fake_context = get_fake_context('config') # config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) # await config_module.on_join(fake_context)
# # #
config_module.servermodel[1].property.read_write() # config_module.servermodel[1].property.read_write()
assert config_module.servermodel[1].option('creole.general.mode_conteneur_actif').value.get() == 'non' # assert config_module.servermodel[1].option('creole.general.mode_conteneur_actif').value.get() == 'non'
config_module.servermodel[1].option('creole.general.mode_conteneur_actif').value.set('oui') # config_module.servermodel[1].option('creole.general.mode_conteneur_actif').value.set('oui')
assert config_module.servermodel[1].option('creole.general.mode_conteneur_actif').value.get() == 'oui' # assert config_module.servermodel[1].option('creole.general.mode_conteneur_actif').value.get() == 'oui'
# # #
await dispatcher.publish('v1', # await dispatcher.publish('v1',
'servermodel.updated', # 'servermodel.updated',
fake_context, # fake_context,
servermodelid=1, # servermodel_id=1,
servermodelname='name1-1', # servermodel_name='name1-1',
subreleasename='2.7.0', # release_id=1,
sourceid=1, # servermodel_description='name1-1')
servermodeldescription='name1-1') # assert config_module.servermodel[1].option('creole.general.mode_conteneur_actif').value.get() == 'oui'
assert config_module.servermodel[1].option('creole.general.mode_conteneur_actif').value.get() == 'oui'
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_server_configuration_get(): async def test_server_configuration_get():
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') fake_context = get_fake_context('config')
config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
# #
config_module.server[3]['server_to_deploy'].property.read_write() await config_module.server[3]['server_to_deploy'].property.read_write()
assert config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'non' assert await config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'non'
config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.set('oui') await config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.set('oui')
assert config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'oui' assert await config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'oui'
assert config_module.server[3]['server'].option('creole.general.mode_conteneur_actif').value.get() == 'non' assert await config_module.server[3]['server'].option('creole.general.mode_conteneur_actif').value.get() == 'non'
# #
values = await dispatcher.call('v1', values = await dispatcher.call('v1',
'config.configuration.server.get', 'config.configuration.server.get',
@ -319,16 +322,17 @@ async def test_server_configuration_get():
async def test_config_deployed(): async def test_config_deployed():
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
fake_context = get_fake_context('config') fake_context = get_fake_context('config')
config_module.cache_root_path = 'tests/data'
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
# #
config_module.server[3]['server_to_deploy'].property.read_write() await config_module.server[3]['server_to_deploy'].property.read_write()
assert config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'non' assert await config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'non'
config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.set('oui') await config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.set('oui')
assert config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'oui' assert await config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'oui'
assert config_module.server[3]['server'].option('creole.general.mode_conteneur_actif').value.get() == 'non' assert await config_module.server[3]['server'].option('creole.general.mode_conteneur_actif').value.get() == 'non'
values = await dispatcher.publish('v1', values = await dispatcher.publish('v1',
'config.configuration.server.deploy', 'config.configuration.server.deploy',
fake_context, fake_context,
server_id=3) server_id=3)
assert config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'oui' assert await config_module.server[3]['server_to_deploy'].option('creole.general.mode_conteneur_actif').value.get() == 'oui'
assert config_module.server[3]['server'].option('creole.general.mode_conteneur_actif').value.get() == 'oui' assert await config_module.server[3]['server'].option('creole.general.mode_conteneur_actif').value.get() == 'oui'

View File

@ -1,10 +1,9 @@
from importlib import import_module from importlib import import_module
import pytest import pytest
from tiramisu import Storage from .storage import STORAGE
from risotto.context import Context from risotto.context import Context
from risotto.services import load_services from risotto.services import load_services
from risotto.dispatcher import dispatcher from risotto.dispatcher import dispatcher
from risotto.config import DATABASE_DIR
from risotto.services.session.storage import storage_server, storage_servermodel from risotto.services.session.storage import storage_server, storage_servermodel
@ -18,11 +17,13 @@ def get_fake_context(module_name):
def setup_module(module): def setup_module(module):
load_services(['config', 'session'], load_services(['config', 'session'],
validate=False) validate=False,
test=True)
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR, name='test') config_module.save_storage = STORAGE
dispatcher.set_module('server', import_module(f'.server', 'fake_services')) dispatcher.set_module('server', import_module(f'.server', 'fake_services'), True)
dispatcher.set_module('servermodel', import_module(f'.servermodel', 'fake_services')) dispatcher.set_module('servermodel', import_module(f'.servermodel', 'fake_services'), True)
dispatcher.injected_self['servermodel'].cache_root_path = 'tests/data'
def teardown_function(function): def teardown_function(function):
@ -35,9 +36,10 @@ def teardown_function(function):
async def test_server_start(): async def test_server_start():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
await dispatcher.call('v1', await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
@ -47,16 +49,17 @@ async def test_server_start():
async def test_server_list(): async def test_server_list():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
assert not await dispatcher.call('v1', assert not await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
await dispatcher.call('v1', await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
assert await dispatcher.call('v1', assert await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
@ -65,9 +68,10 @@ async def test_server_list():
async def test_server_filter_namespace(): async def test_server_filter_namespace():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
@ -78,7 +82,7 @@ async def test_server_filter_namespace():
fake_context, fake_context,
session_id=session_id, session_id=session_id,
namespace=namespace) namespace=namespace)
list_result = await dispatcher.call('v1', list_result = await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
assert list_result[0]['namespace'] == namespace assert list_result[0]['namespace'] == namespace
@ -88,9 +92,10 @@ async def test_server_filter_namespace():
async def test_server_filter_mode(): async def test_server_filter_mode():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
@ -102,7 +107,7 @@ async def test_server_filter_mode():
fake_context, fake_context,
session_id=session_id, session_id=session_id,
mode=mode) mode=mode)
list_result = await dispatcher.call('v1', list_result = await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
assert list_result[0]['mode'] == mode assert list_result[0]['mode'] == mode
@ -112,9 +117,10 @@ async def test_server_filter_mode():
async def test_server_filter_debug(): async def test_server_filter_debug():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
@ -126,7 +132,7 @@ async def test_server_filter_debug():
fake_context, fake_context,
session_id=session_id, session_id=session_id,
debug=debug) debug=debug)
list_result = await dispatcher.call('v1', list_result = await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
assert list_result[0]['debug'] == debug assert list_result[0]['debug'] == debug
@ -137,9 +143,10 @@ async def test_server_filter_debug():
async def test_server_filter_get(): async def test_server_filter_get():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
@ -148,10 +155,8 @@ async def test_server_filter_get():
'session.server.get', 'session.server.get',
fake_context, fake_context,
session_id=session_id) session_id=session_id)
assert values == {'content': {"general.mode_conteneur_actif": "non", assert values == {'content': {"creole.general.mode_conteneur_actif": "non",
"general.master.master": [], "creole.general.master.master": []},
"general.master.slave1": [],
"general.master.slave2": []},
'debug': False, 'debug': False,
'id': 3, 'id': 3,
'mode': 'normal', 'mode': 'normal',
@ -165,9 +170,10 @@ async def test_server_filter_get():
async def test_server_filter_get_one_value(): async def test_server_filter_get_one_value():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
@ -176,8 +182,8 @@ async def test_server_filter_get_one_value():
'session.server.get', 'session.server.get',
fake_context, fake_context,
session_id=session_id, session_id=session_id,
name="general.mode_conteneur_actif") name="creole.general.mode_conteneur_actif")
assert values == {'content': {"general.mode_conteneur_actif": "non"}, assert values == {'content': {"creole.general.mode_conteneur_actif": "non"},
'debug': False, 'debug': False,
'id': 3, 'id': 3,
'mode': 'normal', 'mode': 'normal',
@ -191,9 +197,10 @@ async def test_server_filter_get_one_value():
async def test_server_filter_configure(): async def test_server_filter_configure():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
@ -203,17 +210,17 @@ async def test_server_filter_configure():
fake_context, fake_context,
session_id=session_id, session_id=session_id,
action='modify', action='modify',
name='general.mode_conteneur_actif', name='creole.general.mode_conteneur_actif',
value='oui') value='oui')
list_result = await dispatcher.call('v1', list_result = await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
values = await dispatcher.call('v1', values = await dispatcher.call('v1',
'session.server.get', 'session.server.get',
fake_context, fake_context,
session_id=session_id, session_id=session_id,
name="general.mode_conteneur_actif") name="creole.general.mode_conteneur_actif")
assert values == {'content': {"general.mode_conteneur_actif": "oui"}, assert values == {'content': {"creole.general.mode_conteneur_actif": "oui"},
'debug': False, 'debug': False,
'id': 3, 'id': 3,
'mode': 'normal', 'mode': 'normal',
@ -227,9 +234,10 @@ async def test_server_filter_configure():
async def test_server_filter_validate(): async def test_server_filter_validate():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
@ -244,24 +252,25 @@ async def test_server_filter_validate():
async def test_server_stop(): async def test_server_stop():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.server: if not config_module.server:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
assert not await dispatcher.call('v1', assert not await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
start = await dispatcher.call('v1', start = await dispatcher.call('v1',
'session.server.start', 'session.server.start',
fake_context, fake_context,
id=3) id=3)
session_id = start['session_id'] session_id = start['session_id']
assert await dispatcher.call('v1', assert await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
await dispatcher.call('v1', await dispatcher.call('v1',
'session.server.stop', 'session.server.stop',
fake_context, fake_context,
session_id=session_id) session_id=session_id)
assert not await dispatcher.call('v1', assert not await dispatcher.call('v1',
'session.server.list', 'session.server.list',
fake_context) fake_context)
@ -271,9 +280,10 @@ async def test_server_stop():
async def test_servermodel_start(): async def test_servermodel_start():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
await dispatcher.call('v1', await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
@ -283,16 +293,17 @@ async def test_servermodel_start():
async def test_servermodel_list(): async def test_servermodel_list():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
assert not await dispatcher.call('v1', assert not await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)
await dispatcher.call('v1', await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
assert await dispatcher.call('v1', assert await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)
@ -301,9 +312,10 @@ async def test_servermodel_list():
async def test_servermodel_filter_namespace(): async def test_servermodel_filter_namespace():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
@ -314,7 +326,7 @@ async def test_servermodel_filter_namespace():
fake_context, fake_context,
session_id=session_id, session_id=session_id,
namespace=namespace) namespace=namespace)
list_result = await dispatcher.call('v1', list_result = await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)
assert list_result[0]['namespace'] == namespace assert list_result[0]['namespace'] == namespace
@ -324,9 +336,10 @@ async def test_servermodel_filter_namespace():
async def test_servermodel_filter_mode(): async def test_servermodel_filter_mode():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
@ -338,7 +351,7 @@ async def test_servermodel_filter_mode():
fake_context, fake_context,
session_id=session_id, session_id=session_id,
mode=mode) mode=mode)
list_result = await dispatcher.call('v1', list_result = await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)
assert list_result[0]['mode'] == mode assert list_result[0]['mode'] == mode
@ -348,9 +361,10 @@ async def test_servermodel_filter_mode():
async def test_servermodel_filter_debug(): async def test_servermodel_filter_debug():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
@ -362,7 +376,7 @@ async def test_servermodel_filter_debug():
fake_context, fake_context,
session_id=session_id, session_id=session_id,
debug=debug) debug=debug)
list_result = await dispatcher.call('v1', list_result = await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)
assert list_result[0]['debug'] == debug assert list_result[0]['debug'] == debug
@ -372,9 +386,10 @@ async def test_servermodel_filter_debug():
async def test_servermodel_filter_get(): async def test_servermodel_filter_get():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
@ -383,10 +398,8 @@ async def test_servermodel_filter_get():
'session.servermodel.get', 'session.servermodel.get',
fake_context, fake_context,
session_id=session_id) session_id=session_id)
assert values == {'content': {"general.mode_conteneur_actif": "non", assert values == {'content': {"creole.general.mode_conteneur_actif": "non",
"general.master.master": [], "creole.general.master.master": []},
"general.master.slave1": [],
"general.master.slave2": []},
'debug': False, 'debug': False,
'id': 1, 'id': 1,
'mode': 'normal', 'mode': 'normal',
@ -400,9 +413,10 @@ async def test_servermodel_filter_get():
async def test_servermodel_filter_get_one_value(): async def test_servermodel_filter_get_one_value():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
@ -411,8 +425,8 @@ async def test_servermodel_filter_get_one_value():
'session.servermodel.get', 'session.servermodel.get',
fake_context, fake_context,
session_id=session_id, session_id=session_id,
name="general.mode_conteneur_actif") name="creole.general.mode_conteneur_actif")
assert values == {'content': {"general.mode_conteneur_actif": "non"}, assert values == {'content': {"creole.general.mode_conteneur_actif": "non"},
'debug': False, 'debug': False,
'id': 1, 'id': 1,
'mode': 'normal', 'mode': 'normal',
@ -426,9 +440,10 @@ async def test_servermodel_filter_get_one_value():
async def test_servermodel_filter_configure(): async def test_servermodel_filter_configure():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
@ -438,17 +453,17 @@ async def test_servermodel_filter_configure():
fake_context, fake_context,
session_id=session_id, session_id=session_id,
action='modify', action='modify',
name='general.mode_conteneur_actif', name='creole.general.mode_conteneur_actif',
value='oui') value='oui')
list_result = await dispatcher.call('v1', list_result = await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)
values = await dispatcher.call('v1', values = await dispatcher.call('v1',
'session.servermodel.get', 'session.servermodel.get',
fake_context, fake_context,
session_id=session_id, session_id=session_id,
name="general.mode_conteneur_actif") name="creole.general.mode_conteneur_actif")
assert values == {'content': {"general.mode_conteneur_actif": "oui"}, assert values == {'content': {"creole.general.mode_conteneur_actif": "oui"},
'debug': False, 'debug': False,
'id': 1, 'id': 1,
'mode': 'normal', 'mode': 'normal',
@ -462,9 +477,10 @@ async def test_servermodel_filter_configure():
async def test_servermodel_filter_validate(): async def test_servermodel_filter_validate():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
session = await dispatcher.call('v1', session = await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
@ -479,23 +495,24 @@ async def test_servermodel_filter_validate():
async def test_servermodel_stop(): async def test_servermodel_stop():
fake_context = get_fake_context('session') fake_context = get_fake_context('session')
config_module = dispatcher.get_service('config') config_module = dispatcher.get_service('config')
config_module.cache_root_path = 'tests/data'
if not config_module.servermodel: if not config_module.servermodel:
await config_module.on_join(fake_context) await config_module.on_join(fake_context)
assert not await dispatcher.call('v1', assert not await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)
start = await dispatcher.call('v1', start = await dispatcher.call('v1',
'session.servermodel.start', 'session.servermodel.start',
fake_context, fake_context,
id=1) id=1)
session_id = start['session_id'] session_id = start['session_id']
assert await dispatcher.call('v1', assert await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)
await dispatcher.call('v1', await dispatcher.call('v1',
'session.servermodel.stop', 'session.servermodel.stop',
fake_context, fake_context,
session_id=session_id) session_id=session_id)
assert not await dispatcher.call('v1', assert not await dispatcher.call('v1',
'session.servermodel.list', 'session.servermodel.list',
fake_context) fake_context)