Compare commits
55 Commits
e74391b77a
...
docker
Author | SHA1 | Date | |
---|---|---|---|
3a9243bfb8 | |||
bcd17e1038 | |||
47d5ed77d5 | |||
02c38589d4 | |||
cb2dbe135e | |||
cb0e4b5d5d | |||
722d4894a1 | |||
4020f97db0 | |||
a6383f0c2c | |||
1e223e7b57 | |||
24e5f78668 | |||
5b1cae1567 | |||
1ed86e035b | |||
94168554f2 | |||
50aa8019ab | |||
1d25d3a582 | |||
41af2512b5 | |||
4de9bde691 | |||
6b8a88e103 | |||
9335fbb16e | |||
78b7129605 | |||
b5ddefdaac | |||
ddd97fb59c | |||
77ed63784b | |||
f7a97cf575 | |||
a092b597f8 | |||
e19e718e22 | |||
b006eda133 | |||
62ab525219 | |||
10969ab1e0 | |||
f0042f2a37 | |||
7f0411da4d | |||
91aac5399a | |||
b4c48ebc10 | |||
b567fd88ac | |||
eccc5c4098 | |||
a7934e37d7 | |||
7dc6ce7845 | |||
84850182f6 | |||
dcaf7da3bc | |||
8c91e01a2b | |||
3b31f092bd | |||
3c5285a7d2 | |||
b944a609a5 | |||
bb1fdcbad0 | |||
03937baf51 | |||
939f93253e | |||
0846c4c5cc | |||
a63b41c985 | |||
8a551f85b2 | |||
847fbfc1e1 | |||
332dc61fd4 | |||
1d1b51d37b | |||
66600874b7 | |||
155136d838 |
72
README.md
Normal file
72
README.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
Projet Risotto
|
||||||
|
==============
|
||||||
|
|
||||||
|
Démarrer le server de l'API Risotto :
|
||||||
|
```
|
||||||
|
python3 script/server.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Accéder à un message :
|
||||||
|
```
|
||||||
|
wget http://localhost:8080/v1/config.session.server.start
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
Démarrer un serveur postgresql de test
|
||||||
|
```
|
||||||
|
docker run -dt -p 5432:5432 --name postgres postgres:11-alpine
|
||||||
|
docker exec -ti postgres bash
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
Gestion de la base de données avec Sqitch
|
||||||
|
|
||||||
|
```
|
||||||
|
cpanm --quiet --notest App::Sqitch
|
||||||
|
sqitch init risotto --uri https://forge.cadoles.com/Infra/risotto --engine pg
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Commande :
|
||||||
|
|
||||||
|
# Empty database:
|
||||||
|
su - postgres
|
||||||
|
psql -U postgres risotto
|
||||||
|
drop table log; drop table userrole; drop table release; drop table source; drop table server; drop table servermodel; drop table applicationservice; drop table roleuri; drop table risottouser; drop table uri;
|
||||||
|
|
||||||
|
# Import EOLE
|
||||||
|
./script/cucchiaiata source.create -n eole -u http://localhost
|
||||||
|
./script/cucchiaiata source.release.create -s eole -n 2.7.1.1 -d last
|
||||||
|
./script/cucchiaiata applicationservice.dataset.updated -s eole -r last
|
||||||
|
./script/cucchiaiata servermodel.dataset.updated -s eole -r last
|
||||||
|
|
||||||
|
# Create a server
|
||||||
|
./script/cucchiaiata server.create -s test -d description -m eolebase -n eole -r last
|
||||||
|
|
||||||
|
# Configure the server
|
||||||
|
./script/cucchiaiata session.server.start -s test
|
||||||
|
S=xxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
./script/cucchiaiata session.server.configure -s $S --creole.reseau.unbound_ip_address_cidr 192.168.1.1/24 --creole.reseau.unbound_route_address 192.168.1.2 --creole.serveur_dns.unbound_allowed_client_cidr 192.168.1.0/24 --creole.serveur_dns.unbound_local_zones cadoles.com
|
||||||
|
./script/cucchiaiata session.server.configure -s $S --creole.reseau.unbound_domain_name test.cadoles.com
|
||||||
|
./script/cucchiaiata session.server.filter -s $S -n unbound
|
||||||
|
./script/cucchiaiata session.server.configure -s $S --unbound.unbound_zone_cadoles_com.hostname_cadoles_com.hostname_cadoles_com toto titi --unbound.unbound_zone_cadoles_com.hostname_cadoles_com.ip_cadoles_com 0 192.168.1.25 --unbound.unbound_zone_cadoles_com.hostname_cadoles_com.type_cadoles_com 1 CNAME --unbound.unbound_zone_cadoles_com.hostname_cadoles_com.cname_cadoles_com 1 toto.cadoles.com
|
||||||
|
./script/cucchiaiata session.server.validate -s $S
|
||||||
|
./script/cucchiaiata session.server.stop -s $S -a
|
||||||
|
|
||||||
|
# Generate configuration
|
||||||
|
./script/cucchiaiata config.configuration.server.deploy -s test
|
||||||
|
./script/cucchiaiata template.generate -s test
|
||||||
|
|
||||||
|
# Create a new user and set role 'server_rw' for this server
|
||||||
|
./script/cucchiaiata user.create -l gnunux -n gnunux -s gnunux
|
||||||
|
./script/cucchiaiata user.role.create -u gnunux -n 'server_rw' -a 'Server.ServerName' -v test
|
6
Vocabulary.txt
Normal file
6
Vocabulary.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Message
|
||||||
|
=======
|
||||||
|
|
||||||
|
message: config.session.server.start
|
||||||
|
version: v1
|
||||||
|
uri: v1.config.session.server.start
|
29
docker/Dockerfile
Normal file
29
docker/Dockerfile
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
FROM python:3.7
|
||||||
|
|
||||||
|
# Requirements
|
||||||
|
ARG TIRAMISU_REPO_URL=https://framagit.org/tiramisu/tiramisu.git
|
||||||
|
ARG RISOTTO_REPO_URL=https://forge.cadoles.com/Infra/risotto.git
|
||||||
|
ARG ROUGAIL_REPO_URL=https://forge.cadoles.com/Infra/rougail.git
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
vim \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
jq \
|
||||||
|
&& apt-get clean
|
||||||
|
|
||||||
|
RUN git clone --branch develop ${TIRAMISU_REPO_URL} /srv/src/tiramisu
|
||||||
|
RUN git clone --branch docker ${RISOTTO_REPO_URL} /srv/src/risotto
|
||||||
|
RUN git clone --branch master ${ROUGAIL_REPO_URL} /srv/src/rougail
|
||||||
|
|
||||||
|
RUN ln -s /srv/src/tiramisu/tiramisu /usr/local/lib/python3.7
|
||||||
|
RUN ln -s /srv/src/rougail/src/rougail /usr/local/lib/python3.7
|
||||||
|
RUN ln -s /srv/src/risotto/src/risotto /usr/local/lib/python3.7
|
||||||
|
|
||||||
|
RUN pip install Cheetah3
|
||||||
|
RUN cd /srv/src/risotto && pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
RUN cp -r /srv/src/risotto/messages/ /usr/local/lib/
|
||||||
|
RUN mkdir -p /var/cache/risotto/servermodel
|
||||||
|
RUN mkdir -p /var/cache/risotto/database
|
14
docker/README.md
Normal file
14
docker/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Docker
|
||||||
|
```
|
||||||
|
cd docker
|
||||||
|
docker build -t cadoles/risotto .
|
||||||
|
docker run -t -d --name risotto cadoles/risotto
|
||||||
|
docker exec -ti risotto bash
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Docker-Compose
|
||||||
|
```
|
||||||
|
cd docker
|
||||||
|
docker-compose up
|
||||||
|
```
|
28
docker/docker-compose.yaml
Normal file
28
docker/docker-compose.yaml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
version: '2.2'
|
||||||
|
services:
|
||||||
|
risotto:
|
||||||
|
build:
|
||||||
|
context: ../
|
||||||
|
dockerfile: docker/Dockerfile
|
||||||
|
volumes:
|
||||||
|
- ../.:/srv/src/risotto
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
links:
|
||||||
|
- postgres
|
||||||
|
#command: tail -F /var/log
|
||||||
|
command: python /srv/src/risotto/script/server.py
|
||||||
|
restart: unless-stopped
|
||||||
|
postgres:
|
||||||
|
image: postgres:11-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
|
||||||
|
PGDATA: /data/postgres
|
||||||
|
volumes:
|
||||||
|
- ./postgres-init/:/docker-entrypoint-initdb.d/
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
restart: unless-stopped
|
103
docker/postgres-init/10-postgres.init.sh
Executable file
103
docker/postgres-init/10-postgres.init.sh
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
psql --username "$POSTGRES_USER" <<-EOSQL
|
||||||
|
CREATE ROLE risotto WITH LOGIN PASSWORD 'risotto';
|
||||||
|
CREATE DATABASE risotto;
|
||||||
|
GRANT ALL ON DATABASE risotto TO risotto;
|
||||||
|
\c risotto
|
||||||
|
CREATE EXTENSION hstore;
|
||||||
|
EOSQL
|
||||||
|
|
||||||
|
psql --username "risotto" --password "risotto" <<-EOSQL
|
||||||
|
-- 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)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Log table creation
|
||||||
|
|
||||||
|
CREATE TABLE log(
|
||||||
|
Msg VARCHAR(255) NOT NULL,
|
||||||
|
Level VARCHAR(10) NOT NULL,
|
||||||
|
Path VARCHAR(255),
|
||||||
|
Username VARCHAR(100) NOT NULL,
|
||||||
|
Data JSON,
|
||||||
|
Date timestamp DEFAULT current_timestamp
|
||||||
|
);
|
||||||
|
|
||||||
|
EOSQL
|
33
messages/v1/messages/applicationservice.create.yml
Normal file
33
messages/v1/messages/applicationservice.create.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
uri: applicationservice.create
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Créé un service applicatif.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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éé.
|
20
messages/v1/messages/applicationservice.dataset.updated.yml
Normal file
20
messages/v1/messages/applicationservice.dataset.updated.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
uri: applicationservice.dataset.updated
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Les services applicatifs sont mis à jour.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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 l’injection des services applicatifs en base.
|
23
messages/v1/messages/applicationservice.describe.yml
Normal file
23
messages/v1/messages/applicationservice.describe.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
uri: applicationservice.describe
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Décrit un service applicatif.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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.
|
34
messages/v1/messages/applicationservice.get_by_id.yml
Normal file
34
messages/v1/messages/applicationservice.get_by_id.yml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
uri: applicationservice.get_by_id
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Retourne un service applicatif suivant l'identifiant.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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
|
@ -1,19 +1,17 @@
|
|||||||
---
|
---
|
||||||
uri: config.configuration.server.deploy
|
uri: config.configuration.server.deploy
|
||||||
|
|
||||||
description: |
|
description: Déployer la configuration d'un serveur.
|
||||||
Déployer la configuration d'un serveur.
|
|
||||||
|
|
||||||
sampleuse: ~
|
pattern: rpc
|
||||||
|
|
||||||
pattern: event
|
|
||||||
|
|
||||||
public: false
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
server_id:
|
server_name:
|
||||||
type: Number
|
type: String
|
||||||
description: |
|
ref: Server.ServerName
|
||||||
Identifiant du serveur.
|
shortarg: s
|
||||||
|
description: Nom du serveur.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: Deploy
|
||||||
|
description: La configuration du serveur est déployée.
|
||||||
|
@ -4,23 +4,17 @@ 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
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
server_id:
|
server_name:
|
||||||
type: Number
|
type: String
|
||||||
ref: Server.ServerId
|
ref: Server.ServerName
|
||||||
description: |
|
description: Nom du serveur.
|
||||||
Identifiant de la configuration.
|
deployed:
|
||||||
deploy:
|
|
||||||
type: Boolean
|
type: Boolean
|
||||||
description: Configuration de type déployée.
|
description: Configuration de type déployée.
|
||||||
|
default: true
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigConfiguration
|
type: ConfigConfiguration
|
||||||
|
@ -4,19 +4,18 @@ 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
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
server_id:
|
server_id:
|
||||||
type: Number
|
type: Number
|
||||||
description: |
|
description: |
|
||||||
Identifiant du serveur.
|
Identifiant du serveur.
|
||||||
deploy:
|
server_name:
|
||||||
|
type: String
|
||||||
|
ref: Server.ServerName
|
||||||
|
shortarg: s
|
||||||
|
description: Nom du serveur.
|
||||||
|
deployed:
|
||||||
type: Boolean
|
type: Boolean
|
||||||
description: Configuration de type déployée.
|
description: Configuration de type déployée.
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
uri: config.session.server.start
|
|
||||||
|
|
||||||
description: |
|
|
||||||
Démarre une session de configuration pour un serveur.
|
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.server.start -c 2
|
|
||||||
|
|
||||||
pattern: rpc
|
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
id:
|
|
||||||
type: Number
|
|
||||||
ref: Server.ServerId
|
|
||||||
shortarg: c
|
|
||||||
description: |
|
|
||||||
Identifiant de la configuration.
|
|
||||||
|
|
||||||
response:
|
|
||||||
type: ConfigSession
|
|
||||||
description: Description de la session.
|
|
@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
uri: config.session.server.list
|
|
||||||
|
|
||||||
description: |
|
|
||||||
Liste les sessions de configuration des serveurs.
|
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.server.list
|
|
||||||
|
|
||||||
pattern: rpc
|
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
response:
|
|
||||||
type: '[]ConfigSession'
|
|
||||||
description: |
|
|
||||||
Liste des sessions.
|
|
@ -1,18 +0,0 @@
|
|||||||
uri: config.session.servermodel.list
|
|
||||||
|
|
||||||
description: |
|
|
||||||
Liste les sessions de configuration des modèles de serveur.
|
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.servermodel.list
|
|
||||||
|
|
||||||
pattern: rpc
|
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
response:
|
|
||||||
type: '[]ConfigSession'
|
|
||||||
description: |
|
|
||||||
Liste des sessions.
|
|
@ -1,25 +0,0 @@
|
|||||||
uri: config.session.servermodel.start
|
|
||||||
|
|
||||||
description: |
|
|
||||||
Démarre une session de configuration pour un modèle de serveur.
|
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.servermodel.start -c 2
|
|
||||||
|
|
||||||
pattern: rpc
|
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
id:
|
|
||||||
type: Number
|
|
||||||
ref: Servermodel.ServermodelId
|
|
||||||
shortarg: c
|
|
||||||
description: |
|
|
||||||
Identifiant de la configuration.
|
|
||||||
|
|
||||||
response:
|
|
||||||
type: ConfigSession
|
|
||||||
description: Description de la session.
|
|
@ -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
|
|
@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
uri: server.describe
|
|
||||||
|
|
||||||
description: |
|
|
||||||
Retourne les attributs détaillés d’un 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
|
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
uri: servermodel.created
|
|
||||||
|
|
||||||
description: Des modèles de serveur ont été créés.
|
|
||||||
|
|
||||||
pattern: event
|
|
||||||
|
|
||||||
public: false
|
|
||||||
|
|
||||||
domain: servermodel-domain
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
servermodels:
|
|
||||||
type: '[]Servermodel'
|
|
||||||
description: Informations sur les modèles de serveur créés.
|
|
@ -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
|
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
uri: servermodel.source.list
|
|
||||||
|
|
||||||
description: |
|
|
||||||
Retourne la liste des sources.
|
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client servermodel.source.list
|
|
||||||
|
|
||||||
pattern: rpc
|
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: servermodel-domain
|
|
||||||
|
|
||||||
response:
|
|
||||||
type: '[]Dict'
|
|
||||||
description: Liste des sources disponibles.
|
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
uri: servermodel.updated
|
|
||||||
|
|
||||||
description: Des modèles de serveur ont été modifiés.
|
|
||||||
|
|
||||||
pattern: event
|
|
||||||
|
|
||||||
public: false
|
|
||||||
|
|
||||||
domain: servermodel-domain
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
servermodels:
|
|
||||||
type: '[]Servermodel'
|
|
||||||
description: Informations sur les modèles de serveur modifiés.
|
|
35
messages/v1/messages/server.create.yml
Normal file
35
messages/v1/messages/server.create.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
uri: server.create
|
||||||
|
|
||||||
|
description: Crée un serveur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
server_name:
|
||||||
|
type: String
|
||||||
|
shortarg: s
|
||||||
|
description: Nom du serveur.
|
||||||
|
server_description:
|
||||||
|
type: String
|
||||||
|
shortarg: d
|
||||||
|
description: Description du serveur.
|
||||||
|
servermodel_name:
|
||||||
|
type: String
|
||||||
|
shortarg: m
|
||||||
|
ref: Servermodel.ServermodelName
|
||||||
|
description: Nom du modèle de serveur.
|
||||||
|
source_name:
|
||||||
|
type: String
|
||||||
|
shortarg: n
|
||||||
|
ref: Source.SourceName
|
||||||
|
description: Nom de la source.
|
||||||
|
release_distribution:
|
||||||
|
type: String
|
||||||
|
shortarg: r
|
||||||
|
ref: Source.ReleaseDistribution
|
||||||
|
description: Nom de la sous-version.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: Server
|
||||||
|
description: Description du serveur créé.
|
@ -5,10 +5,6 @@ description: Un serveur a été créé.
|
|||||||
|
|
||||||
pattern: event
|
pattern: event
|
||||||
|
|
||||||
public: false
|
|
||||||
|
|
||||||
domain: server-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
type: Server
|
type: Server
|
||||||
description: Description du serveur.
|
description: Description du serveur.
|
@ -1,19 +1,13 @@
|
|||||||
---
|
---
|
||||||
uri: server.deleted
|
uri: server.deleted
|
||||||
|
|
||||||
sampleuse: ~
|
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Un serveur a été supprimé.
|
Un serveur a été supprimé.
|
||||||
|
|
||||||
pattern: event
|
pattern: event
|
||||||
|
|
||||||
public: false
|
|
||||||
|
|
||||||
domain: server-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
serverid:
|
server_id:
|
||||||
type: Number
|
type: Number
|
||||||
description: |
|
description: |
|
||||||
Identifiant du serveur supprimé.
|
Identifiant du serveur supprimé.
|
17
messages/v1/messages/server.describe.yml
Normal file
17
messages/v1/messages/server.describe.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
uri: server.describe
|
||||||
|
|
||||||
|
description: Retourne les attributs détaillés d’un serveur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
server_name:
|
||||||
|
type: String
|
||||||
|
shortarg: s
|
||||||
|
ref: Server.ServerName
|
||||||
|
description: Nom du serveur.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: Server
|
||||||
|
description: Description du serveur.
|
@ -4,15 +4,8 @@ uri: server.list
|
|||||||
description: |
|
description: |
|
||||||
Liste les serveurs disponibles.
|
Liste les serveurs disponibles.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client server.list
|
|
||||||
|
|
||||||
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.
|
10
messages/v1/messages/servermodel.created.yml
Normal file
10
messages/v1/messages/servermodel.created.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
uri: servermodel.created
|
||||||
|
|
||||||
|
description: Des modèles de serveur ont été créés.
|
||||||
|
|
||||||
|
pattern: event
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
type: Servermodel
|
||||||
|
description: Informations sur les modèles de serveur créés.
|
20
messages/v1/messages/servermodel.dataset.updated.yml
Normal file
20
messages/v1/messages/servermodel.dataset.updated.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
uri: servermodel.dataset.updated
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Initialise la table pour les modèles de serveur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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 l’injection des modèles de serveur en base.
|
28
messages/v1/messages/servermodel.describe.yml
Normal file
28
messages/v1/messages/servermodel.describe.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
uri: servermodel.describe
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Retourne les attributs détaillés d'un modèle de serveur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
servermodel_name:
|
||||||
|
type: String
|
||||||
|
shortarg: s
|
||||||
|
description: Identifiant du modèle de serveur à récupérer.
|
||||||
|
ref: Servermodel.ServermodelId
|
||||||
|
source_name:
|
||||||
|
type: String
|
||||||
|
shortarg: n
|
||||||
|
description: Nom de la source.
|
||||||
|
ref: Source.SourceName
|
||||||
|
release_distribution:
|
||||||
|
type: String
|
||||||
|
shortarg: r
|
||||||
|
description: Nom de la distribution.
|
||||||
|
ref: Source.ReleaseDistribution
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: Servermodel
|
||||||
|
description: Description du modèle de serveur.
|
17
messages/v1/messages/servermodel.get_by_id.yml
Normal file
17
messages/v1/messages/servermodel.get_by_id.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
uri: servermodel.get_by_id
|
||||||
|
|
||||||
|
description: Retourne les attributs détaillés d'un modèle de serveur suivant son identifiant.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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.
|
@ -4,17 +4,10 @@ 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
|
|
||||||
|
|
||||||
domain: servermodel-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
sourceid:
|
source_id:
|
||||||
type: Number
|
type: Number
|
||||||
shortarg: s
|
shortarg: s
|
||||||
description: |
|
description: |
|
||||||
@ -24,13 +17,3 @@ parameters:
|
|||||||
response:
|
response:
|
||||||
type: '[]Servermodel'
|
type: '[]Servermodel'
|
||||||
description: Liste des modèles de serveur disponibles.
|
description: Liste des modèles de serveur disponibles.
|
||||||
|
|
||||||
errors:
|
|
||||||
- uri: servermodel.list.error.database_not_available
|
|
||||||
|
|
||||||
related:
|
|
||||||
- servermodel.describe
|
|
||||||
- servermodel.create
|
|
||||||
- servermodel.update
|
|
||||||
- servermodel.delete
|
|
||||||
- servermodel.event
|
|
10
messages/v1/messages/servermodel.updated.yml
Normal file
10
messages/v1/messages/servermodel.updated.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
uri: servermodel.updated
|
||||||
|
|
||||||
|
description: Des modèles de serveur ont été modifiés.
|
||||||
|
|
||||||
|
pattern: event
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
type: 'Servermodel'
|
||||||
|
description: Informations sur les modèles de serveur modifiés.
|
@ -1,18 +1,11 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.server.configure
|
uri: session.server.configure
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Configure le server.
|
Configure le server.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.server.configure -s 2
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
session_id:
|
session_id:
|
||||||
type: String
|
type: String
|
||||||
@ -37,7 +30,12 @@ parameters:
|
|||||||
shortarg: v
|
shortarg: v
|
||||||
description: Valeur de la variable.
|
description: Valeur de la variable.
|
||||||
default: null
|
default: null
|
||||||
|
value_multi:
|
||||||
|
type: '[]Any'
|
||||||
|
shortarg: m
|
||||||
|
description: Valeur de la variable de type multi.
|
||||||
|
default: []
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigStatus
|
type: SessionStatus
|
||||||
description: Description de la session.
|
description: Description de la session.
|
@ -1,18 +1,11 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.server.filter
|
uri: session.server.filter
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Filter la configuration a éditer.
|
Filter la configuration a éditer.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.server.filter -s 2
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
session_id:
|
session_id:
|
||||||
type: String
|
type: String
|
||||||
@ -36,5 +29,5 @@ parameters:
|
|||||||
default: null
|
default: null
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigSession
|
type: Session
|
||||||
description: Description de la session.
|
description: Description de la session.
|
@ -1,25 +1,23 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.server.get
|
uri: session.server.get
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Configure le server.
|
Récupérer la configuration du server.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.server.get -s 2
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
session_id:
|
session_id:
|
||||||
type: String
|
type: String
|
||||||
ref: Config.SessionId
|
ref: Config.SessionId
|
||||||
shortarg: s
|
shortarg: s
|
||||||
description: Identifiant de la configuration.
|
description: Identifiant de la configuration.
|
||||||
|
name:
|
||||||
|
type: String
|
||||||
|
shortarg: n
|
||||||
|
description: Nom de la variable.
|
||||||
|
default: null
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigSession
|
type: Session
|
||||||
description: Description de la session.
|
description: Description de la session.
|
12
messages/v1/messages/session.server.list.yml
Normal file
12
messages/v1/messages/session.server.list.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
uri: session.server.list
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Liste les sessions de configuration des serveurs.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: '[]Session'
|
||||||
|
description: |
|
||||||
|
Liste des sessions.
|
18
messages/v1/messages/session.server.start.yml
Normal file
18
messages/v1/messages/session.server.start.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
uri: session.server.start
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Démarre une session de configuration pour un serveur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
server_name:
|
||||||
|
type: String
|
||||||
|
ref: Server.ServerName
|
||||||
|
shortarg: s
|
||||||
|
description: Nom du serveur.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: Session
|
||||||
|
description: Description de la session.
|
@ -1,20 +1,13 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.server.stop
|
uri: session.server.stop
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Termine une session de configuration d'un serveur.
|
Termine une session de configuration d'un serveur.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.server.stop -s xxxxx
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
sessionid:
|
session_id:
|
||||||
ref: Config.SessionId
|
ref: Config.SessionId
|
||||||
type: String
|
type: String
|
||||||
shortarg: s
|
shortarg: s
|
||||||
@ -26,5 +19,5 @@ parameters:
|
|||||||
default: false
|
default: false
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigSession
|
type: Session
|
||||||
description: Description de la session.
|
description: Description de la session.
|
@ -1,18 +1,11 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.server.validate
|
uri: session.server.validate
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Valider la configuration d'un serveur.
|
Valider la configuration d'un serveur.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.server.validate -s xxxxx
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
session_id:
|
session_id:
|
||||||
ref: Config.SessionId
|
ref: Config.SessionId
|
||||||
@ -21,6 +14,6 @@ parameters:
|
|||||||
description: Identifiant de la session.
|
description: Identifiant de la session.
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigConfigurationStatus
|
type: Session
|
||||||
description: Statut de la configuration.
|
description: Statut de la configuration.
|
||||||
|
|
@ -1,18 +1,11 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.servermodel.configure
|
uri: session.servermodel.configure
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Configure le servermodel.
|
Configure le servermodel.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.servermodel.configure -s 2
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
session_id:
|
session_id:
|
||||||
type: String
|
type: String
|
||||||
@ -37,7 +30,12 @@ parameters:
|
|||||||
shortarg: v
|
shortarg: v
|
||||||
description: Valeur de la variable.
|
description: Valeur de la variable.
|
||||||
default: null
|
default: null
|
||||||
|
value_multi:
|
||||||
|
type: '[]Any'
|
||||||
|
shortarg: m
|
||||||
|
description: Valeur de la variable de type multi.
|
||||||
|
default: []
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigStatus
|
type: SessionStatus
|
||||||
description: Description de la session.
|
description: Description de la session.
|
@ -1,18 +1,11 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.servermodel.filter
|
uri: session.servermodel.filter
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Filter la configuration a éditer.
|
Filter la configuration a éditer.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.servermodel.filter -s 2
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
session_id:
|
session_id:
|
||||||
type: String
|
type: String
|
||||||
@ -36,5 +29,5 @@ parameters:
|
|||||||
default: null
|
default: null
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigSession
|
type: Session
|
||||||
description: Description de la session.
|
description: Description de la session.
|
@ -1,25 +1,23 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.servermodel.get
|
uri: session.servermodel.get
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Configure le servermodel.
|
Configure le servermodel.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.servermodel.get -s 2
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
session_id:
|
session_id:
|
||||||
type: String
|
type: String
|
||||||
ref: Config.SessionId
|
ref: Config.SessionId
|
||||||
shortarg: s
|
shortarg: s
|
||||||
description: Identifiant de la configuration.
|
description: Identifiant de la configuration.
|
||||||
|
name:
|
||||||
|
type: String
|
||||||
|
shortarg: n
|
||||||
|
description: Nom de la variable.
|
||||||
|
default: null
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigSession
|
type: Session
|
||||||
description: Description de la session.
|
description: Description de la session.
|
11
messages/v1/messages/session.servermodel.list.yml
Normal file
11
messages/v1/messages/session.servermodel.list.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
uri: session.servermodel.list
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Liste les sessions de configuration des modèles de serveur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: '[]Session'
|
||||||
|
description: |
|
||||||
|
Liste des sessions.
|
27
messages/v1/messages/session.servermodel.start.yml
Normal file
27
messages/v1/messages/session.servermodel.start.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
uri: session.servermodel.start
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Démarre une session de configuration pour un modèle de serveur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
servermodel_name:
|
||||||
|
type: String
|
||||||
|
ref: Servermodel.ServermodelName
|
||||||
|
shortarg: s
|
||||||
|
description: Nom du serveurmodel.
|
||||||
|
source_name:
|
||||||
|
type: String
|
||||||
|
shortarg: n
|
||||||
|
description: Nom de la source.
|
||||||
|
ref: Source.SourceName
|
||||||
|
release_distribution:
|
||||||
|
type: String
|
||||||
|
shortarg: r
|
||||||
|
description: Nom de la distribution.
|
||||||
|
ref: Source.ReleaseDistribution
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: Session
|
||||||
|
description: Description de la session.
|
@ -1,19 +1,12 @@
|
|||||||
uri: config.session.servermodel.stop
|
uri: session.servermodel.stop
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Termine une session de configuration d'un modèle de serveur.
|
Termine une session de configuration d'un modèle de serveur.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.servermodel.stop -s xxxxx
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
sessionid:
|
session_id:
|
||||||
ref: Config.SessionId
|
ref: Config.SessionId
|
||||||
type: String
|
type: String
|
||||||
shortarg: s
|
shortarg: s
|
||||||
@ -25,5 +18,5 @@ parameters:
|
|||||||
default: false
|
default: false
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigSession
|
type: Session
|
||||||
description: Description de la session.
|
description: Description de la session.
|
@ -1,18 +1,11 @@
|
|||||||
---
|
---
|
||||||
uri: config.session.servermodel.validate
|
uri: session.servermodel.validate
|
||||||
|
|
||||||
description: |
|
description: |
|
||||||
Valider la configuration d'un modèle serveur.
|
Valider la configuration d'un modèle serveur.
|
||||||
|
|
||||||
sampleuse: |
|
|
||||||
zephir-client config.session.servermodel.validate -s xxxxx
|
|
||||||
|
|
||||||
pattern: rpc
|
pattern: rpc
|
||||||
|
|
||||||
public: true
|
|
||||||
|
|
||||||
domain: config-domain
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
session_id:
|
session_id:
|
||||||
ref: Config.SessionId
|
ref: Config.SessionId
|
||||||
@ -21,6 +14,6 @@ parameters:
|
|||||||
description: Identifiant de la session.
|
description: Identifiant de la session.
|
||||||
|
|
||||||
response:
|
response:
|
||||||
type: ConfigConfigurationStatus
|
type: Session
|
||||||
description: Statut de la configuration.
|
description: Statut de la configuration.
|
||||||
|
|
23
messages/v1/messages/source.create.yml
Normal file
23
messages/v1/messages/source.create.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
uri: source.create
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Créer une source.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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.
|
20
messages/v1/messages/source.dataset.update.yml
Normal file
20
messages/v1/messages/source.dataset.update.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
uri: source.dataset.update
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Initialise la table pour les versions.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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.
|
18
messages/v1/messages/source.describe.yml
Normal file
18
messages/v1/messages/source.describe.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
uri: source.describe
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Retourne une source.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
source_name:
|
||||||
|
type: String
|
||||||
|
shortarg: n
|
||||||
|
description: |
|
||||||
|
Nom de la source.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: 'Source'
|
||||||
|
description: Information sur la source.
|
11
messages/v1/messages/source.list.yml
Normal file
11
messages/v1/messages/source.list.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
uri: source.list
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Retourne la liste des sources.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: '[]Source'
|
||||||
|
description: Liste des sources disponibles.
|
27
messages/v1/messages/source.release.create.yml
Normal file
27
messages/v1/messages/source.release.create.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
uri: source.release.create
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Créer une version.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
source_name:
|
||||||
|
type: String
|
||||||
|
shortarg: s
|
||||||
|
description: Nom 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.
|
21
messages/v1/messages/source.release.describe.yml
Normal file
21
messages/v1/messages/source.release.describe.yml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
uri: source.release.describe
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Retourne la sous-version.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
source_name:
|
||||||
|
type: String
|
||||||
|
shortarg: s
|
||||||
|
description: Nom de la source.
|
||||||
|
release_distribution:
|
||||||
|
type: String
|
||||||
|
shortarg: r
|
||||||
|
description: Nom de la sous-version
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: 'Release'
|
||||||
|
description: Sous-version.
|
22
messages/v1/messages/source.release.get_by_distribution.yml
Normal file
22
messages/v1/messages/source.release.get_by_distribution.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
uri: source.release.get_by_distribution
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Retourne version suivant le nom de la distribution.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
17
messages/v1/messages/source.release.get_by_id.yml
Normal file
17
messages/v1/messages/source.release.get_by_id.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
uri: source.release.get_by_id
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Retourne version suivant l'identifiant.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
release_id:
|
||||||
|
type: Number
|
||||||
|
shortarg: r
|
||||||
|
description: ID de la version.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: 'Release'
|
||||||
|
description: La version disponibles.
|
17
messages/v1/messages/source.release.list.yml
Normal file
17
messages/v1/messages/source.release.list.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
uri: source.release.list
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Retourne la liste des versions.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
source_name:
|
||||||
|
type: String
|
||||||
|
shortarg: s
|
||||||
|
description: Nom de la source.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: '[]Release'
|
||||||
|
description: Liste des versions disponibles.
|
19
messages/v1/messages/template.generate.yml
Normal file
19
messages/v1/messages/template.generate.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
uri: template.generate
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Génère et récupère les templates générés.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
server_name:
|
||||||
|
type: String
|
||||||
|
ref: Server.ServerName
|
||||||
|
shortarg: s
|
||||||
|
description: Nom du serveur.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: Template
|
||||||
|
description: |
|
||||||
|
Les fichiers de configuration générés.
|
22
messages/v1/messages/uri.role.join.yml
Normal file
22
messages/v1/messages/uri.role.join.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
uri: uri.role.join
|
||||||
|
|
||||||
|
description: Crée un rôle utilisateur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
role_name:
|
||||||
|
type: String
|
||||||
|
shortarg: r
|
||||||
|
description: Nom du rôle.
|
||||||
|
ref: User.RoleName
|
||||||
|
uri_name:
|
||||||
|
type: String
|
||||||
|
shortarg: u
|
||||||
|
description: Nom du message.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: URIRole
|
||||||
|
description: Association de rôle créé.
|
||||||
|
|
11
messages/v1/messages/uri.role.list.yml
Normal file
11
messages/v1/messages/uri.role.list.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
uri: uri.role.list
|
||||||
|
|
||||||
|
description: Liste des associations d'URI et de rôle.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: '[]URIRole'
|
||||||
|
description: Liste des associations.
|
||||||
|
|
25
messages/v1/messages/user.create.yml
Normal file
25
messages/v1/messages/user.create.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
uri: user.create
|
||||||
|
|
||||||
|
description: Crée un utilisateur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
user_login:
|
||||||
|
type: String
|
||||||
|
shortarg: l
|
||||||
|
description: Login de l'utilisateur.
|
||||||
|
ref: User.Login
|
||||||
|
user_name:
|
||||||
|
type: String
|
||||||
|
shortarg: n
|
||||||
|
description: Nom de l'utilisateur.
|
||||||
|
user_surname:
|
||||||
|
type: String
|
||||||
|
shortarg: s
|
||||||
|
description: Nom de famille de l'utilisateur.
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: User
|
||||||
|
description: Description de l'utilisateur créé.
|
18
messages/v1/messages/user.delete.yml
Normal file
18
messages/v1/messages/user.delete.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
uri: user.delete
|
||||||
|
|
||||||
|
description: Supprimer un utilisateur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
user_login:
|
||||||
|
type: String
|
||||||
|
shortarg: l
|
||||||
|
description: Login de l'utilisateur.
|
||||||
|
ref: User.Login
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: User
|
||||||
|
description: Description de l'utilisateur supprimé.
|
||||||
|
|
11
messages/v1/messages/user.list.yml
Normal file
11
messages/v1/messages/user.list.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
uri: user.list
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Liste les utilisateurs disponibles.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: '[]User'
|
||||||
|
description: Retourne la liste des utilisateurs.
|
32
messages/v1/messages/user.role.create.yml
Normal file
32
messages/v1/messages/user.role.create.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
uri: user.role.create
|
||||||
|
|
||||||
|
description: Crée un rôle utilisateur.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
user_login:
|
||||||
|
type: String
|
||||||
|
shortarg: u
|
||||||
|
description: Login de l'utilisateur.
|
||||||
|
ref: User.UserLogin
|
||||||
|
role_name:
|
||||||
|
type: String
|
||||||
|
shortarg: n
|
||||||
|
description: Nom du rôle.
|
||||||
|
ref: User.RoleName
|
||||||
|
role_attribute:
|
||||||
|
type: String
|
||||||
|
shortarg: a
|
||||||
|
description: Attribut contrôlé.
|
||||||
|
default: null
|
||||||
|
role_attribute_value:
|
||||||
|
type: String
|
||||||
|
shortarg: v
|
||||||
|
description: Valeur de l'attribut contrôlé.
|
||||||
|
default: null
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: Role
|
||||||
|
description: Description du rôle créé.
|
19
messages/v1/messages/user.role.list.yml
Normal file
19
messages/v1/messages/user.role.list.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
uri: user.role.list
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Liste les rôles disponibles.
|
||||||
|
|
||||||
|
pattern: rpc
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
user_login:
|
||||||
|
type: String
|
||||||
|
shortarg: l
|
||||||
|
description: Login de l'utilisateur associé.
|
||||||
|
ref: User.RoleName
|
||||||
|
default: null
|
||||||
|
|
||||||
|
response:
|
||||||
|
type: '[]Role'
|
||||||
|
description: Retourne la liste des rôles.
|
28
messages/v1/types/applicationservice.yml
Normal file
28
messages/v1/types/applicationservice.yml
Normal 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
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: ConfigConfigurationStatus
|
|
||||||
type: object
|
|
||||||
description: Statut de la configuration.
|
|
||||||
properties:
|
|
||||||
session_id:
|
|
||||||
type: string
|
|
||||||
description: ID de la session.
|
|
||||||
ref: Config.SessionId
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
description: Statut de la configuration (peut être ok, error, incomplete)
|
|
||||||
message:
|
|
||||||
type: string
|
|
||||||
description: Message d'erreur si la configuration a le statut error.
|
|
||||||
mandatories:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
description: Liste des variables obligatoires non renseignées si la configuration a le statut incomplete.
|
|
||||||
required:
|
|
||||||
- sessionid
|
|
||||||
- status
|
|
||||||
|
|
@ -3,8 +3,15 @@ title: ConfigConfiguration
|
|||||||
type: object
|
type: object
|
||||||
description: Description de la configuration.
|
description: Description de la configuration.
|
||||||
properties:
|
properties:
|
||||||
|
server_name:
|
||||||
|
type: string
|
||||||
|
description: Nom du serveur.
|
||||||
|
ref: Server.ServerName
|
||||||
|
deployed:
|
||||||
|
type: boolean
|
||||||
|
description: La configuration est déployée.
|
||||||
configuration:
|
configuration:
|
||||||
type: File
|
type: object
|
||||||
description: Détail de la configuration au format JSON.
|
description: Détail de la configuration au format JSON.
|
||||||
required:
|
required:
|
||||||
- configuration
|
- configuration
|
||||||
|
20
messages/v1/types/deploy.yml
Normal file
20
messages/v1/types/deploy.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: Deploy
|
||||||
|
type: object
|
||||||
|
description: État de déploiement de la configuration.
|
||||||
|
properties:
|
||||||
|
server_id:
|
||||||
|
type: number
|
||||||
|
description: ID du serveur.
|
||||||
|
ref: Server.ServerID
|
||||||
|
server_name:
|
||||||
|
type: string
|
||||||
|
ref: Server.ServerName
|
||||||
|
description: Nom du server.
|
||||||
|
deployed:
|
||||||
|
type: boolean
|
||||||
|
description: État de déploiement.
|
||||||
|
required:
|
||||||
|
- server_id
|
||||||
|
- server_name
|
||||||
|
- deployed
|
14
messages/v1/types/global.returnstatus.yml
Normal file
14
messages/v1/types/global.returnstatus.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: ReturnStatus
|
||||||
|
type: object
|
||||||
|
description: Résultat d’une commande.
|
||||||
|
properties:
|
||||||
|
retcode:
|
||||||
|
type: number
|
||||||
|
description: Code de retour de la commande.
|
||||||
|
returns:
|
||||||
|
type: string
|
||||||
|
description: Retour de la commande.
|
||||||
|
required:
|
||||||
|
- retcode
|
||||||
|
- returns
|
28
messages/v1/types/release.yml
Normal file
28
messages/v1/types/release.yml
Normal 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
|
||||||
|
|
26
messages/v1/types/role.yml
Normal file
26
messages/v1/types/role.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
title: Role
|
||||||
|
type: object
|
||||||
|
description: Description du rôle.
|
||||||
|
properties:
|
||||||
|
role_id:
|
||||||
|
type: number
|
||||||
|
description: Identifiant de l'utilisateur.
|
||||||
|
ref: User.RoleUserId
|
||||||
|
user_login:
|
||||||
|
type: string
|
||||||
|
description: Login du l'utilisateur.
|
||||||
|
ref: User.Login
|
||||||
|
role_name:
|
||||||
|
type: string
|
||||||
|
description: Nom du rôle.
|
||||||
|
role_attribute:
|
||||||
|
type: string
|
||||||
|
description: Nom de l'utilisateur.
|
||||||
|
role_attribute_value:
|
||||||
|
type: string
|
||||||
|
description: Valeur de l'attribut contrôlé.
|
||||||
|
|
||||||
|
required:
|
||||||
|
- role_id
|
||||||
|
- role_name
|
@ -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
|
|
@ -3,37 +3,22 @@ title: Server
|
|||||||
type: object
|
type: object
|
||||||
description: Description du serveur.
|
description: Description du serveur.
|
||||||
properties:
|
properties:
|
||||||
serverid:
|
server_id:
|
||||||
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:
|
||||||
- serverid
|
- server_id
|
||||||
- servername
|
- server_name
|
||||||
- serverdescription
|
- server_description
|
||||||
- servermodelid
|
- server_servermodel_id
|
||||||
|
@ -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
|
||||||
|
@ -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: File
|
|
||||||
description: Contenu du schema.
|
|
||||||
probes:
|
|
||||||
type: File
|
|
||||||
description: Informations sur les sondes.
|
|
||||||
creolefuncs:
|
|
||||||
type: File
|
|
||||||
description: Fonctions Creole.
|
|
||||||
conffiles:
|
|
||||||
type: File
|
|
||||||
description: Fichiers creole au format tar encodé base64
|
|
||||||
required:
|
required:
|
||||||
- servermodelid
|
- servermodel_id
|
||||||
- servermodelname
|
- servermodel_name
|
||||||
- servermodeldescription
|
- servermodel_description
|
||||||
- servermodelsubreleaseid
|
- release_id
|
||||||
- sourceid
|
|
||||||
- subreleasename
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: ConfigStatus
|
title: SessionStatus
|
||||||
type: object
|
type: object
|
||||||
description: Status de la modification de la configuration.
|
description: Status de la modification de la configuration.
|
||||||
properties:
|
properties:
|
||||||
@ -12,9 +12,6 @@ properties:
|
|||||||
index:
|
index:
|
||||||
type: number
|
type: number
|
||||||
description: Index de la variable a modifier.
|
description: Index de la variable a modifier.
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
description: Status de la modification.
|
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
description: Message d'erreur.
|
description: Message d'erreur.
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: ConfigSession
|
title: Session
|
||||||
type: object
|
type: object
|
||||||
description: Description de la session.
|
description: Description de la session.
|
||||||
properties:
|
properties:
|
||||||
sessionid:
|
session_id:
|
||||||
type: string
|
type: string
|
||||||
description: ID de la session.
|
description: ID de la session.
|
||||||
ref: Config.SessionId
|
ref: Config.SessionId
|
||||||
@ -27,10 +27,10 @@ properties:
|
|||||||
type: boolean
|
type: boolean
|
||||||
description: La configuration est en mode debug.
|
description: La configuration est en mode debug.
|
||||||
content:
|
content:
|
||||||
type: file
|
type: object
|
||||||
description: Contenu de la configuration.
|
description: Contenu de la configuration.
|
||||||
required:
|
required:
|
||||||
- sessionid
|
- session_id
|
||||||
- id
|
- id
|
||||||
- username
|
- username
|
||||||
- timestamp
|
- timestamp
|
20
messages/v1/types/source.yml
Normal file
20
messages/v1/types/source.yml
Normal 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
|
15
messages/v1/types/template.yml
Normal file
15
messages/v1/types/template.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: Template
|
||||||
|
type: object
|
||||||
|
description: Les fichiers de configuration générés.
|
||||||
|
properties:
|
||||||
|
server_name:
|
||||||
|
type: String
|
||||||
|
description: Nom du serveur.
|
||||||
|
ref: Server.ServerName
|
||||||
|
template_dir:
|
||||||
|
type: String
|
||||||
|
description: Nom du répertoire avec les fichiers de configuration générés.
|
||||||
|
required:
|
||||||
|
- server_name
|
||||||
|
- template_dir
|
15
messages/v1/types/uri.role.yml
Normal file
15
messages/v1/types/uri.role.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: URIRole
|
||||||
|
type: object
|
||||||
|
description: Description de l'assication du message et du rôle.
|
||||||
|
properties:
|
||||||
|
role_name:
|
||||||
|
type: string
|
||||||
|
description: Nom du rôle
|
||||||
|
ref: User.RoleName
|
||||||
|
uri_name:
|
||||||
|
type: string
|
||||||
|
description: Nom du message
|
||||||
|
required:
|
||||||
|
- role_name
|
||||||
|
- uri_name
|
24
messages/v1/types/user.yml
Normal file
24
messages/v1/types/user.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: User
|
||||||
|
type: object
|
||||||
|
description: Description de l'utilisateur.
|
||||||
|
properties:
|
||||||
|
user_id:
|
||||||
|
type: number
|
||||||
|
description: Identifiant de l'utilisateur.
|
||||||
|
ref: User.UserId
|
||||||
|
user_login:
|
||||||
|
type: string
|
||||||
|
description: Login de l'utilisateur.
|
||||||
|
user_name:
|
||||||
|
type: string
|
||||||
|
description: Nom de l'utilisateur.
|
||||||
|
user_surname:
|
||||||
|
type: string
|
||||||
|
description: Nom de famille de l'utilisateur.
|
||||||
|
|
||||||
|
required:
|
||||||
|
- user_id
|
||||||
|
- user_login
|
||||||
|
- user_name
|
||||||
|
- user_surname
|
20
messages/v1/types/userrole.yml
Normal file
20
messages/v1/types/userrole.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: UserRole
|
||||||
|
type: object
|
||||||
|
description: Description de l'association du rôle et de l'utilisateur.
|
||||||
|
properties:
|
||||||
|
user_role_id:
|
||||||
|
type: number
|
||||||
|
description: Identifiant de l'association.
|
||||||
|
ref: User.UserRoleId
|
||||||
|
user_login:
|
||||||
|
type: string
|
||||||
|
description: Login de l'utilisateur.
|
||||||
|
role_name:
|
||||||
|
type: string
|
||||||
|
description: Nom du rôle.
|
||||||
|
|
||||||
|
required:
|
||||||
|
- user_role_id
|
||||||
|
- user_login
|
||||||
|
- role_name
|
7
requirements.txt
Normal file
7
requirements.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
asyncpg==0.20.1
|
||||||
|
lxml==4.4.2
|
||||||
|
requests==2.22.0
|
||||||
|
aiohttp==3.6.2
|
||||||
|
pytest==5.3.3
|
||||||
|
PyYAML==5.3
|
||||||
|
tiramisu==3.0rc15
|
117
script/database_manager.py
Normal file
117
script/database_manager.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
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)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Log table creation
|
||||||
|
|
||||||
|
CREATE TABLE log(
|
||||||
|
Msg VARCHAR(255) NOT NULL,
|
||||||
|
Level VARCHAR(10) NOT NULL,
|
||||||
|
Path VARCHAR(255),
|
||||||
|
Username VARCHAR(100) NOT NULL,
|
||||||
|
Data JSON,
|
||||||
|
Date timestamp DEFAULT current_timestamp
|
||||||
|
);
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
db_conf = get_config().get('database')
|
||||||
|
#asyncpg.connect('postgresql://postgres@localhost/test')
|
||||||
|
engine = db_conf.get('engine')
|
||||||
|
host = db_conf.get('host')
|
||||||
|
dbname = db_conf.get('dbname')
|
||||||
|
dbuser = db_conf.get('user')
|
||||||
|
dbpassword = db_conf.get('password')
|
||||||
|
dbport = db_conf.get('port')
|
||||||
|
cfg = "{}://{}:{}@{}:{}/{}".format(engine, dbuser, dbpassword, host, dbport, dbname)
|
||||||
|
pool = await asyncpg.create_pool(cfg)
|
||||||
|
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())
|
||||||
|
|
@ -1,6 +1,12 @@
|
|||||||
from aiohttp.web import run_app
|
from asyncio import get_event_loop
|
||||||
from risotto import get_app
|
from risotto import get_app
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
run_app(get_app())
|
loop = get_event_loop()
|
||||||
|
loop.run_until_complete(get_app(loop))
|
||||||
|
try:
|
||||||
|
loop.run_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
from .utils import undefined
|
|
||||||
from .dispatcher import register, dispatcher
|
|
||||||
from .http import get_app
|
from .http import get_app
|
||||||
# just to register every route
|
|
||||||
from . import services as _services
|
|
||||||
|
|
||||||
__ALL__ = ('undefined', 'register', 'dispatcher', 'get_app')
|
__ALL__ = ('get_app',)
|
||||||
|
|
||||||
|
@ -1,3 +1,34 @@
|
|||||||
HTTP_PORT = 8080
|
|
||||||
MESSAGE_ROOT_PATH = 'messages'
|
MESSAGE_ROOT_PATH = 'messages'
|
||||||
DEBUG = False
|
DATABASE_DIR = '/var/cache/risotto/database'
|
||||||
|
INTERNAL_USER = 'internal'
|
||||||
|
CONFIGURATION_DIR = 'configurations'
|
||||||
|
TEMPLATE_DIR = 'templates'
|
||||||
|
TMP_DIR = 'tmp'
|
||||||
|
ROUGAIL_DTD_PATH = '../rougail/data/creole.dtd'
|
||||||
|
DEFAULT_USER = 'Anonymous'
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import PurePosixPath
|
||||||
|
CURRENT_PATH = PurePosixPath(__file__)
|
||||||
|
|
||||||
|
def get_config():
|
||||||
|
return {'database': {'engine': 'postgres',
|
||||||
|
'host': 'postgres',
|
||||||
|
'port': 5432,
|
||||||
|
'dbname': 'risotto',
|
||||||
|
'user': 'risotto',
|
||||||
|
'password': 'risotto',
|
||||||
|
},
|
||||||
|
'http_server': {'port': 8080,
|
||||||
|
#'default_user': "gnunux"},
|
||||||
|
'default_user': DEFAULT_USER},
|
||||||
|
'global': {'message_root_path': CURRENT_PATH.parents[2] / 'messages',
|
||||||
|
'debug': True,
|
||||||
|
'internal_user': 'internal',
|
||||||
|
'check_role': True,
|
||||||
|
'rougail_dtd_path': '../rougail/data/creole.dtd',
|
||||||
|
'admin_user': DEFAULT_USER},
|
||||||
|
'source': {'root_path': '/srv/seed'},
|
||||||
|
'cache': {'root_path': '/var/cache/risotto'}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
@ -26,3 +30,7 @@ class Controller:
|
|||||||
uri,
|
uri,
|
||||||
risotto_context,
|
risotto_context,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
|
async def on_join(self,
|
||||||
|
risotto_context):
|
||||||
|
pass
|
||||||
|
@ -1,335 +1,346 @@
|
|||||||
from tiramisu import Config
|
from tiramisu import Config
|
||||||
from inspect import signature
|
|
||||||
from traceback import print_exc
|
from traceback import print_exc
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from typing import Dict
|
from typing import Dict, Callable, List, Optional
|
||||||
|
from json import dumps, loads
|
||||||
|
|
||||||
from .utils import undefined, _
|
from .utils import _
|
||||||
from .error import RegistrationError, CallError, NotAllowedError
|
from .error import CallError, NotAllowedError
|
||||||
from .message import get_messages
|
|
||||||
from .logger import log
|
from .logger import log
|
||||||
from .config import DEBUG
|
from .config import get_config
|
||||||
from .context import Context
|
from .context import Context
|
||||||
|
from . import register
|
||||||
|
import asyncpg
|
||||||
|
|
||||||
|
|
||||||
def register(uri: str,
|
class CallDispatcher:
|
||||||
notification: str=undefined):
|
async def valid_call_returns(self,
|
||||||
""" Decorator to register function to the dispatcher
|
risotto_context: Context,
|
||||||
"""
|
function,
|
||||||
version, uri = uri.split('.', 1)
|
returns: Dict,
|
||||||
def decorator(function):
|
kwargs: Dict):
|
||||||
dispatcher.set_function(version, uri, function)
|
response = self.messages[risotto_context.version][risotto_context.message]['response']
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
class RegisterDispatcher:
|
|
||||||
def get_function_args(self, function):
|
|
||||||
# remove self
|
|
||||||
first_argument_index = 1
|
|
||||||
return [param.name for param in list(signature(function).parameters.values())[first_argument_index:]]
|
|
||||||
|
|
||||||
def _valid_rpc_params(self, version, uri, function, module_name):
|
|
||||||
""" parameters function must have strictly all arguments with the correct name
|
|
||||||
"""
|
|
||||||
def get_message_args():
|
|
||||||
# load config
|
|
||||||
config = Config(self.option)
|
|
||||||
config.property.read_write()
|
|
||||||
# set message to the uri name
|
|
||||||
config.option('message').value.set(uri)
|
|
||||||
# get message argument
|
|
||||||
subconfig = config.option(uri)
|
|
||||||
return set(config.option(uri).value.dict().keys())
|
|
||||||
|
|
||||||
def get_function_args():
|
|
||||||
function_args = self.get_function_args(function)
|
|
||||||
# risotto_context is a special argument, remove it
|
|
||||||
if function_args[0] == 'risotto_context':
|
|
||||||
function_args = function_args[1:]
|
|
||||||
return set(function_args)
|
|
||||||
|
|
||||||
# get message arguments
|
|
||||||
message_args = get_message_args()
|
|
||||||
# get function arguments
|
|
||||||
function_args = get_function_args()
|
|
||||||
# compare message arguments with function parameter
|
|
||||||
# it must not have more or less arguments
|
|
||||||
if message_args != function_args:
|
|
||||||
# raise if arguments are not equal
|
|
||||||
msg = []
|
|
||||||
missing_function_args = message_args - function_args
|
|
||||||
if missing_function_args:
|
|
||||||
msg.append(_(f'missing arguments: {missing_function_args}'))
|
|
||||||
extra_function_args = function_args - message_args
|
|
||||||
if extra_function_args:
|
|
||||||
msg.append(_(f'extra arguments: {extra_function_args}'))
|
|
||||||
function_name = function.__name__
|
|
||||||
msg = _(' and ').join(msg)
|
|
||||||
raise RegistrationError(_(f'error with {module_name}.{function_name} arguments: {msg}'))
|
|
||||||
|
|
||||||
def _valid_event_params(self, version, uri, function, module_name):
|
|
||||||
""" parameters function validation for event messages
|
|
||||||
"""
|
|
||||||
def get_message_args():
|
|
||||||
# load config
|
|
||||||
config = Config(self.option)
|
|
||||||
config.property.read_write()
|
|
||||||
# set message to the uri name
|
|
||||||
config.option('message').value.set(uri)
|
|
||||||
# get message argument
|
|
||||||
subconfig = config.option(uri)
|
|
||||||
return set(config.option(uri).value.dict().keys())
|
|
||||||
|
|
||||||
def get_function_args():
|
|
||||||
function_args = self.get_function_args(function)
|
|
||||||
# risotto_context is a special argument, remove it
|
|
||||||
if function_args[0] == 'risotto_context':
|
|
||||||
function_args = function_args[1:]
|
|
||||||
return set(function_args)
|
|
||||||
|
|
||||||
# get message arguments
|
|
||||||
message_args = get_message_args()
|
|
||||||
# get function arguments
|
|
||||||
function_args = get_function_args()
|
|
||||||
# compare message arguments with function parameter
|
|
||||||
# it can have less arguments but not more
|
|
||||||
extra_function_args = function_args - message_args
|
|
||||||
if extra_function_args:
|
|
||||||
# raise if too many arguments
|
|
||||||
function_name = function.__name__
|
|
||||||
msg = _(f'extra arguments: {extra_function_args}')
|
|
||||||
raise RegistrationError(_(f'error with {module_name}.{function_name} arguments: {msg}'))
|
|
||||||
|
|
||||||
def set_function(self, version, uri, function):
|
|
||||||
""" register a function to an URI
|
|
||||||
URI is a message
|
|
||||||
"""
|
|
||||||
# xxx module can only be register with v1.xxxx..... message
|
|
||||||
module_name = function.__module__.split('.')[-2]
|
module_name = function.__module__.split('.')[-2]
|
||||||
uri_namespace = uri.split('.', 1)[0]
|
|
||||||
if uri_namespace != module_name:
|
|
||||||
raise RegistrationError(_(f'cannot registered to {uri} message in module {module_name}'))
|
|
||||||
|
|
||||||
# check if message exists
|
|
||||||
try:
|
|
||||||
if not Config(self.option).option(uri).option.type() == 'message':
|
|
||||||
raise RegistrationError(_(f'{uri} is not a valid message'))
|
|
||||||
except AttributeError:
|
|
||||||
raise RegistrationError(_(f'{uri} is not a valid message'))
|
|
||||||
|
|
||||||
# create an uris' version if needed
|
|
||||||
if version not in self.uris:
|
|
||||||
self.uris[version] = {}
|
|
||||||
self.function_names[version] = {}
|
|
||||||
|
|
||||||
# valid function is unique per module
|
|
||||||
if module_name not in self.function_names[version]:
|
|
||||||
self.function_names[version][module_name] = []
|
|
||||||
function_name = function.__name__
|
function_name = function.__name__
|
||||||
if function_name in self.function_names[version][module_name]:
|
if response.impl_get_information('multi'):
|
||||||
raise RegistrationError(_(f'multiple registration of {module_name}.{function_name} function'))
|
if not isinstance(returns, list):
|
||||||
self.function_names[version][module_name].append(function_name)
|
err = _(f'function {module_name}.{function_name} has to return a list')
|
||||||
|
await log.error_msg(risotto_context, kwargs, err)
|
||||||
# True if first argument is the risotto_context
|
raise CallError(str(err))
|
||||||
function_args = self.get_function_args(function)
|
|
||||||
if function_args[0] == 'risotto_context':
|
|
||||||
inject_risotto_context = True
|
|
||||||
function_args.pop(0)
|
|
||||||
else:
|
else:
|
||||||
inject_risotto_context = False
|
if not isinstance(returns, dict):
|
||||||
|
await log.error_msg(risotto_context, kwargs, returns)
|
||||||
if self.messages[uri]['pattern'] == 'rpc':
|
err = _(f'function {module_name}.{function_name} has to return a dict')
|
||||||
# check if a RPC function is already register for this uri
|
await log.error_msg(risotto_context, kwargs, err)
|
||||||
if uri in self.uris[version]:
|
raise CallError(str(err))
|
||||||
raise RegistrationError(_(f'uri {uri} already registered'))
|
returns = [returns]
|
||||||
# valid function's arguments
|
if response is None:
|
||||||
self._valid_rpc_params(version, uri, function, module_name)
|
raise Exception('hu?')
|
||||||
# register this function
|
|
||||||
self.uris[version][uri] = {'module': module_name,
|
|
||||||
'function': function,
|
|
||||||
'risotto_context': inject_risotto_context}
|
|
||||||
else:
|
else:
|
||||||
# if event
|
for ret in returns:
|
||||||
# valid function's arguments
|
config = await Config(response,
|
||||||
self._valid_event_params(version, uri, function, module_name)
|
display_name=lambda self, dyn_name: self.impl_getname())
|
||||||
# register this function
|
await config.property.read_write()
|
||||||
if uri not in self.uris[version]:
|
try:
|
||||||
self.uris[version][uri] = []
|
for key, value in ret.items():
|
||||||
self.uris[version][uri].append({'module': module_name,
|
await config.option(key).value.set(value)
|
||||||
'function': function,
|
except AttributeError:
|
||||||
'arguments': function_args,
|
err = _(f'function {module_name}.{function_name} return the unknown parameter "{key}"')
|
||||||
'risotto_context': inject_risotto_context})
|
await log.error_msg(risotto_context, kwargs, err)
|
||||||
|
raise CallError(str(err))
|
||||||
|
except ValueError:
|
||||||
|
err = _(f'function {module_name}.{function_name} return the parameter "{key}" with an unvalid value "{value}"')
|
||||||
|
await log.error_msg(risotto_context, kwargs, err)
|
||||||
|
raise CallError(str(err))
|
||||||
|
await config.property.read_only()
|
||||||
|
mandatories = await config.value.mandatory()
|
||||||
|
if mandatories:
|
||||||
|
mand = [mand.split('.')[-1] for mand in mandatories]
|
||||||
|
raise ValueError(_(f'missing parameters in response: {mand} in message "{risotto_context.message}"'))
|
||||||
|
try:
|
||||||
|
await config.value.dict()
|
||||||
|
except Exception as err:
|
||||||
|
err = _(f'function {module_name}.{function_name} return an invalid response {err}')
|
||||||
|
await log.error_msg(risotto_context, kwargs, err)
|
||||||
|
raise CallError(str(err))
|
||||||
|
|
||||||
def set_module(self, module_name, module):
|
async def call(self,
|
||||||
""" register and instanciate a new module
|
version: str,
|
||||||
"""
|
message: str,
|
||||||
try:
|
old_risotto_context: Context,
|
||||||
self.modules[module_name] = module.Risotto()
|
check_role: bool=False,
|
||||||
except AttributeError as err:
|
**kwargs):
|
||||||
raise RegistrationError(_(f'unable to register the module {module_name}, this module must have Risotto class'))
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
""" check if all messages have a function
|
|
||||||
"""
|
|
||||||
# FIXME only v1 supported
|
|
||||||
missing_messages = set(self.messages.keys()) - set(self.uris['v1'].keys())
|
|
||||||
if missing_messages:
|
|
||||||
raise RegistrationError(_(f'missing uri {missing_messages}'))
|
|
||||||
|
|
||||||
|
|
||||||
class Dispatcher(RegisterDispatcher):
|
|
||||||
""" Manage message (call or publish)
|
|
||||||
so launch a function when a message is called
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
self.modules = {}
|
|
||||||
self.uris = {}
|
|
||||||
self.function_names = {}
|
|
||||||
self.messages, self.option = get_messages()
|
|
||||||
config = Config(self.option)
|
|
||||||
|
|
||||||
def new_context(self,
|
|
||||||
context: Context,
|
|
||||||
version: str,
|
|
||||||
uri: str):
|
|
||||||
new_context = Context()
|
|
||||||
new_context.paths = copy(context.paths)
|
|
||||||
new_context.paths.append(version + '.' + uri)
|
|
||||||
new_context.username = context.username
|
|
||||||
return new_context
|
|
||||||
|
|
||||||
def check_public_function(self,
|
|
||||||
version: str,
|
|
||||||
uri: str,
|
|
||||||
context: Context,
|
|
||||||
kwargs: Dict,
|
|
||||||
public_only: bool):
|
|
||||||
if public_only and not self.messages[uri]['public']:
|
|
||||||
msg = _(f'the message {version}.{uri} is private')
|
|
||||||
log.error_msg(version, uri, context, kwargs, 'call', msg)
|
|
||||||
raise NotAllowedError(msg)
|
|
||||||
|
|
||||||
def check_pattern(self,
|
|
||||||
version: str,
|
|
||||||
uri: str,
|
|
||||||
type: str,
|
|
||||||
context: Context,
|
|
||||||
kwargs: Dict):
|
|
||||||
if self.messages[uri]['pattern'] != type:
|
|
||||||
msg = _(f'{version}.{uri} is not a {type} message')
|
|
||||||
log.error_msg(version, uri, context, kwargs, 'call', msg)
|
|
||||||
raise CallError(msg)
|
|
||||||
|
|
||||||
def set_config(self,
|
|
||||||
uri: str,
|
|
||||||
kwargs: Dict):
|
|
||||||
""" create a new Config et set values to it
|
|
||||||
"""
|
|
||||||
# create a new config
|
|
||||||
config = Config(self.option)
|
|
||||||
config.property.read_write()
|
|
||||||
# set message option
|
|
||||||
config.option('message').value.set(uri)
|
|
||||||
# store values
|
|
||||||
subconfig = config.option(uri)
|
|
||||||
for key, value in kwargs.items():
|
|
||||||
try:
|
|
||||||
subconfig.option(key).value.set(value)
|
|
||||||
except AttributeError:
|
|
||||||
raise AttributeError(_(f'unknown parameter "{key}"'))
|
|
||||||
# check mandatories options
|
|
||||||
config.property.read_only()
|
|
||||||
mandatories = list(config.value.mandatory())
|
|
||||||
if mandatories:
|
|
||||||
mand = [mand.split('.')[-1] for mand in mandatories]
|
|
||||||
raise ValueError(_(f'missing parameters: {mand}'))
|
|
||||||
# return the config
|
|
||||||
return config
|
|
||||||
|
|
||||||
async def call(self, version, uri, risotto_context, public_only=False, **kwargs):
|
|
||||||
""" execute the function associate with specified uri
|
""" execute the function associate with specified uri
|
||||||
arguments are validate before
|
arguments are validate before
|
||||||
"""
|
"""
|
||||||
new_context = self.new_context(risotto_context,
|
risotto_context = self.build_new_context(old_risotto_context,
|
||||||
version,
|
version,
|
||||||
uri)
|
message,
|
||||||
self.check_public_function(version,
|
'rpc')
|
||||||
uri,
|
function_objs = [self.messages[version][message]]
|
||||||
new_context,
|
# do not start a new database connection
|
||||||
kwargs,
|
if hasattr(old_risotto_context, 'connection'):
|
||||||
public_only)
|
risotto_context.connection = old_risotto_context.connection
|
||||||
self.check_pattern(version,
|
return await self.launch(version,
|
||||||
uri,
|
message,
|
||||||
'rpc',
|
risotto_context,
|
||||||
new_context,
|
check_role,
|
||||||
kwargs)
|
kwargs,
|
||||||
try:
|
function_objs)
|
||||||
config = self.set_config(uri,
|
else:
|
||||||
kwargs)
|
async with self.pool.acquire() as connection:
|
||||||
obj = self.uris[version][uri]
|
await connection.set_type_codec(
|
||||||
kw = config.option(uri).value.dict()
|
'json',
|
||||||
if obj['risotto_context']:
|
encoder=dumps,
|
||||||
kw['risotto_context'] = new_context
|
decoder=loads,
|
||||||
returns = await obj['function'](self.modules[obj['module']], **kw)
|
schema='pg_catalog'
|
||||||
except CallError as err:
|
)
|
||||||
raise err
|
risotto_context.connection = connection
|
||||||
except Exception as err:
|
async with connection.transaction():
|
||||||
if DEBUG:
|
return await self.launch(version,
|
||||||
print_exc()
|
message,
|
||||||
log.error_msg(version, uri, new_context, kwargs, 'call', err)
|
risotto_context,
|
||||||
raise CallError(str(err))
|
check_role,
|
||||||
# FIXME notification
|
kwargs,
|
||||||
# FIXME valider le retour!
|
function_objs)
|
||||||
log.info_msg(version, uri, new_context, kwargs, 'call', _(f'returns {returns}'))
|
|
||||||
return returns
|
|
||||||
|
|
||||||
async def publish(self, version, uri, risotto_context, public_only=False, **kwargs):
|
|
||||||
new_context = self.new_context(risotto_context,
|
|
||||||
version,
|
|
||||||
uri)
|
|
||||||
self.check_pattern(version,
|
|
||||||
uri,
|
|
||||||
'event',
|
|
||||||
new_context,
|
|
||||||
kwargs)
|
|
||||||
try:
|
|
||||||
config = self.set_config(uri,
|
|
||||||
kwargs)
|
|
||||||
config_arguments = config.option(uri).value.dict()
|
|
||||||
except CallError as err:
|
|
||||||
return
|
|
||||||
except Exception as err:
|
|
||||||
# if there is a problem with arguments, just send an error et do nothing
|
|
||||||
if DEBUG:
|
|
||||||
print_exc()
|
|
||||||
log.error_msg(version, uri, new_context, kwargs, 'publish', err)
|
|
||||||
return
|
|
||||||
|
|
||||||
# config is ok, so publish the message
|
class PublishDispatcher:
|
||||||
for function_obj in self.uris[version][uri]:
|
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,
|
||||||
|
version,
|
||||||
|
message,
|
||||||
|
'event')
|
||||||
|
function_objs = self.messages[version][message].get('functions', [])
|
||||||
|
# do not start a new database connection
|
||||||
|
if hasattr(old_risotto_context, 'connection'):
|
||||||
|
risotto_context.connection = old_risotto_context.connection
|
||||||
|
return await self.launch(version,
|
||||||
|
message,
|
||||||
|
risotto_context,
|
||||||
|
check_role,
|
||||||
|
kwargs,
|
||||||
|
function_objs)
|
||||||
|
else:
|
||||||
|
async with self.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():
|
||||||
|
return await self.launch(version,
|
||||||
|
message,
|
||||||
|
risotto_context,
|
||||||
|
check_role,
|
||||||
|
kwargs,
|
||||||
|
function_objs)
|
||||||
|
|
||||||
|
|
||||||
|
class Dispatcher(register.RegisterDispatcher, CallDispatcher, PublishDispatcher):
|
||||||
|
""" Manage message (call or publish)
|
||||||
|
so launch a function when a message is called
|
||||||
|
"""
|
||||||
|
def build_new_context(self,
|
||||||
|
old_risotto_context: Context,
|
||||||
|
version: str,
|
||||||
|
message: str,
|
||||||
|
type: str):
|
||||||
|
""" This is a new call or a new publish, so create a new context
|
||||||
|
"""
|
||||||
|
uri = version + '.' + message
|
||||||
|
risotto_context = Context()
|
||||||
|
risotto_context.username = old_risotto_context.username
|
||||||
|
risotto_context.paths = copy(old_risotto_context.paths)
|
||||||
|
risotto_context.paths.append(uri)
|
||||||
|
risotto_context.uri = uri
|
||||||
|
risotto_context.type = type
|
||||||
|
risotto_context.message = message
|
||||||
|
risotto_context.version = version
|
||||||
|
return risotto_context
|
||||||
|
|
||||||
|
async def check_message_type(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
kwargs: Dict):
|
||||||
|
if self.messages[risotto_context.version][risotto_context.message]['pattern'] != risotto_context.type:
|
||||||
|
msg = _(f'{risotto_context.uri} is not a {risotto_context.type} message')
|
||||||
|
await log.error_msg(risotto_context, kwargs, msg)
|
||||||
|
raise CallError(msg)
|
||||||
|
|
||||||
|
async def load_kwargs_to_config(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
uri: str,
|
||||||
|
kwargs: Dict,
|
||||||
|
check_role: bool):
|
||||||
|
""" create a new Config et set values to it
|
||||||
|
"""
|
||||||
|
# create a new config
|
||||||
|
config = await Config(self.option)
|
||||||
|
await config.property.read_write()
|
||||||
|
# set message's option
|
||||||
|
await config.option('message').value.set(risotto_context.message)
|
||||||
|
# store values
|
||||||
|
subconfig = config.option(risotto_context.message)
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
try:
|
||||||
|
await subconfig.option(key).value.set(value)
|
||||||
|
except AttributeError:
|
||||||
|
if get_config()['global']['debug']:
|
||||||
|
print_exc()
|
||||||
|
raise ValueError(_(f'unknown parameter in "{uri}": "{key}"'))
|
||||||
|
# check mandatories options
|
||||||
|
if check_role and get_config().get('global').get('check_role'):
|
||||||
|
await self.check_role(subconfig,
|
||||||
|
risotto_context.username,
|
||||||
|
uri)
|
||||||
|
await config.property.read_only()
|
||||||
|
mandatories = await config.value.mandatory()
|
||||||
|
if mandatories:
|
||||||
|
mand = [mand.split('.')[-1] for mand in mandatories]
|
||||||
|
raise ValueError(_(f'missing parameters in "{uri}": {mand}'))
|
||||||
|
# return complete an validated kwargs
|
||||||
|
return await subconfig.value.dict()
|
||||||
|
|
||||||
|
def get_service(self,
|
||||||
|
name: str):
|
||||||
|
return self.injected_self[name]
|
||||||
|
|
||||||
|
async def check_role(self,
|
||||||
|
config: Config,
|
||||||
|
user_login: str,
|
||||||
|
uri: str) -> None:
|
||||||
|
async with self.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}"'))
|
||||||
|
|
||||||
|
async def launch(self,
|
||||||
|
version: str,
|
||||||
|
message: str,
|
||||||
|
risotto_context: Context,
|
||||||
|
check_role: bool,
|
||||||
|
kwargs: Dict,
|
||||||
|
function_objs: List) -> Optional[Dict]:
|
||||||
|
await self.check_message_type(risotto_context,
|
||||||
|
kwargs)
|
||||||
|
try:
|
||||||
|
config_arguments = await self.load_kwargs_to_config(risotto_context,
|
||||||
|
f'{version}.{message}',
|
||||||
|
kwargs,
|
||||||
|
check_role)
|
||||||
|
except Exception as err:
|
||||||
|
# if there is a problem with arguments, just send an error and do nothing
|
||||||
|
if get_config()['global']['debug']:
|
||||||
|
print_exc()
|
||||||
|
await log.error_msg(risotto_context, kwargs, err)
|
||||||
|
if risotto_context.type == 'rpc':
|
||||||
|
raise err
|
||||||
|
return
|
||||||
|
# config is ok, so send the message
|
||||||
|
for function_obj in function_objs:
|
||||||
function = function_obj['function']
|
function = function_obj['function']
|
||||||
module_name = function.__module__.split('.')[-2]
|
module_name = function.__module__.split('.')[-2]
|
||||||
function_name = function.__name__
|
function_name = function.__name__
|
||||||
info_msg = _(f'in module {module_name}.{function_name}')
|
info_msg = _(f'in module {module_name}.{function_name}')
|
||||||
try:
|
try:
|
||||||
# build argument for this function
|
# build argument for this function
|
||||||
kw = {}
|
if risotto_context.type == 'rpc':
|
||||||
for key, value in config_arguments.items():
|
kw = config_arguments
|
||||||
if key in function_obj['arguments']:
|
else:
|
||||||
kw[key] = value
|
kw = {}
|
||||||
if function_obj['risotto_context']:
|
for key, value in config_arguments.items():
|
||||||
kw['risotto_context'] = new_context
|
if key in function_obj['arguments']:
|
||||||
# send event
|
kw[key] = value
|
||||||
await function(self.modules[function_obj['module']], **kw)
|
|
||||||
|
|
||||||
|
|
||||||
|
kw['risotto_context'] = risotto_context
|
||||||
|
returns = await function(self.injected_self[function_obj['module']], **kw)
|
||||||
|
except CallError as err:
|
||||||
|
if risotto_context.type == 'rpc':
|
||||||
|
raise err
|
||||||
|
continue
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if DEBUG:
|
if get_config().get('global').get('debug'):
|
||||||
print_exc()
|
print_exc()
|
||||||
log.error_msg(version, uri, new_context, kwargs, 'publish', err, info_msg)
|
await log.error_msg(risotto_context,
|
||||||
|
kwargs,
|
||||||
|
err)
|
||||||
|
if risotto_context.type == 'rpc':
|
||||||
|
raise CallError(str(err))
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
module_name = function.__module__.split('.')[-2]
|
if risotto_context.type == 'rpc':
|
||||||
function_name = function.__name__
|
# valid returns
|
||||||
log.info_msg(version, uri, new_context, kwargs,'publish', info_msg)
|
await self.valid_call_returns(risotto_context,
|
||||||
|
function,
|
||||||
|
returns,
|
||||||
|
kwargs)
|
||||||
|
# log the success
|
||||||
|
await log.info_msg(risotto_context,
|
||||||
|
{'arguments': kwargs,
|
||||||
|
'returns': returns},
|
||||||
|
info_msg)
|
||||||
|
# notification
|
||||||
|
if function_obj.get('notification'):
|
||||||
|
notif_version, notif_message = function_obj['notification'].split('.', 1)
|
||||||
|
if not isinstance(returns, list):
|
||||||
|
send_returns = [returns]
|
||||||
|
else:
|
||||||
|
send_returns = returns
|
||||||
|
for ret in send_returns:
|
||||||
|
await self.publish(notif_version,
|
||||||
|
notif_message,
|
||||||
|
risotto_context,
|
||||||
|
**ret)
|
||||||
|
if risotto_context.type == 'rpc':
|
||||||
|
return returns
|
||||||
|
|
||||||
|
|
||||||
dispatcher = Dispatcher()
|
dispatcher = Dispatcher()
|
||||||
|
register.dispatcher = dispatcher
|
||||||
|
@ -8,3 +8,7 @@ class CallError(Exception):
|
|||||||
|
|
||||||
class NotAllowedError(Exception):
|
class NotAllowedError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ExecutionError(Exception):
|
||||||
|
pass
|
||||||
|
@ -1,72 +1,148 @@
|
|||||||
from aiohttp.web import Application, Response, get, post, HTTPBadRequest, HTTPInternalServerError, HTTPNotFound
|
from aiohttp.web import Application, Response, get, post, HTTPBadRequest, HTTPInternalServerError, HTTPNotFound
|
||||||
from tiramisu import Config
|
|
||||||
from json import dumps
|
from json import dumps
|
||||||
|
from traceback import print_exc
|
||||||
|
from tiramisu import Config
|
||||||
|
|
||||||
|
|
||||||
from .dispatcher import dispatcher
|
from .dispatcher import dispatcher
|
||||||
from .utils import _
|
from .utils import _
|
||||||
from .context import Context
|
from .context import Context
|
||||||
from .error import CallError, NotAllowedError
|
from .error import CallError, NotAllowedError, RegistrationError
|
||||||
from .message import get_messages
|
from .message import get_messages
|
||||||
from .logger import log
|
from .logger import log
|
||||||
|
from .config import get_config
|
||||||
|
from .services import load_services
|
||||||
|
|
||||||
|
|
||||||
|
def create_context(request):
|
||||||
|
risotto_context = Context()
|
||||||
|
risotto_context.username = request.match_info.get('username',
|
||||||
|
get_config()['http_server']['default_user'])
|
||||||
|
return risotto_context
|
||||||
|
|
||||||
|
|
||||||
|
def register(version: str,
|
||||||
|
path: str):
|
||||||
|
""" Decorator to register function to the http route
|
||||||
|
"""
|
||||||
|
def decorator(function):
|
||||||
|
if path in extra_routes:
|
||||||
|
raise RegistrationError(f'the route {path} is already registered')
|
||||||
|
extra_routes[path] = {'function': function,
|
||||||
|
'version': version}
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class extra_route_handler:
|
||||||
|
async def __new__(cls, request):
|
||||||
|
kwargs = dict(request.match_info)
|
||||||
|
kwargs['request'] = request
|
||||||
|
kwargs['risotto_context'] = create_context(request)
|
||||||
|
kwargs['risotto_context'].version = cls.version
|
||||||
|
kwargs['risotto_context'].paths.append(cls.path)
|
||||||
|
kwargs['risotto_context'].type = 'http_get'
|
||||||
|
function_name = cls.function.__module__
|
||||||
|
# if not 'api' function
|
||||||
|
if function_name != 'risotto.http':
|
||||||
|
module_name = function_name.split('.')[-2]
|
||||||
|
kwargs['self'] = dispatcher.injected_self[module_name]
|
||||||
|
try:
|
||||||
|
returns = await cls.function(**kwargs)
|
||||||
|
except NotAllowedError as err:
|
||||||
|
raise HTTPNotFound(reason=str(err))
|
||||||
|
except CallError as err:
|
||||||
|
raise HTTPBadRequest(reason=str(err))
|
||||||
|
except Exception as err:
|
||||||
|
if get_config()['global']['debug']:
|
||||||
|
print_exc()
|
||||||
|
raise HTTPInternalServerError(reason=str(err))
|
||||||
|
# await log.info_msg(kwargs['risotto_context'],
|
||||||
|
# dict(request.match_info))
|
||||||
|
return Response(text=dumps(returns))
|
||||||
|
|
||||||
|
|
||||||
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:]
|
||||||
context = Context()
|
risotto_context = create_context(request)
|
||||||
context.username = request.match_info.get('username', "Anonymous")
|
|
||||||
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':
|
||||||
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 get_config()['global']['debug']:
|
||||||
|
print_exc()
|
||||||
raise HTTPInternalServerError(reason=str(err))
|
raise HTTPInternalServerError(reason=str(err))
|
||||||
return Response(text=dumps({'response': text}))
|
return Response(text=dumps({'response': text}))
|
||||||
|
|
||||||
|
|
||||||
async def api(request):
|
async def api(request, risotto_context):
|
||||||
context = Context()
|
|
||||||
context.username = request.match_info.get('username', "Anonymous")
|
|
||||||
path = request.match_info.get_info()['path']
|
|
||||||
if path.endswith('/'):
|
|
||||||
path = path[:-1]
|
|
||||||
version = path.rsplit('/', 1)[-1]
|
|
||||||
log.info_msg(version, None, context, {}, None, _(f'get {version} API'))
|
|
||||||
global tiramisu
|
global tiramisu
|
||||||
if not tiramisu:
|
if not tiramisu:
|
||||||
config = Config(get_messages(load_shortarg=True,
|
# check all URI that have an associated role
|
||||||
only_public=True)[1])
|
# all URI without role is concidered has a private URI
|
||||||
tiramisu = config.option.dict(remotable='none')
|
uris = []
|
||||||
return Response(text=dumps(tiramisu))
|
async with dispatcher.pool.acquire() as connection:
|
||||||
|
async with connection.transaction():
|
||||||
|
# Check role with ACL
|
||||||
|
sql = '''
|
||||||
|
SELECT URI.URIName
|
||||||
|
FROM URI, RoleURI
|
||||||
|
WHERE RoleURI.URIId = URI.URIId
|
||||||
|
'''
|
||||||
|
uris = [uri['uriname'] for uri in await connection.fetch(sql)]
|
||||||
|
config = await Config(get_messages(load_shortarg=True, uris=uris)[1])
|
||||||
|
await config.property.read_write()
|
||||||
|
tiramisu = await config.option.dict(remotable='none')
|
||||||
|
return tiramisu
|
||||||
|
|
||||||
|
|
||||||
def get_app():
|
extra_routes = {'': {'function': api,
|
||||||
|
'version': 'v1'}}
|
||||||
|
|
||||||
|
|
||||||
|
async def get_app(loop):
|
||||||
""" build all routes
|
""" build all routes
|
||||||
"""
|
"""
|
||||||
app = Application()
|
global extra_routes
|
||||||
|
load_services()
|
||||||
|
app = Application(loop=loop)
|
||||||
routes = []
|
routes = []
|
||||||
uris = list(dispatcher.uris.items())
|
await dispatcher.load()
|
||||||
uris.sort()
|
for version, messages in dispatcher.messages.items():
|
||||||
for version, uris in dispatcher.uris.items():
|
|
||||||
print()
|
print()
|
||||||
print(_('======== Registered messages ========'))
|
print(_('======== Registered messages ========'))
|
||||||
for uri in uris:
|
for message in messages:
|
||||||
web_uri = f'/api/{version}/{uri}'
|
web_message = f'/api/{version}/{message}'
|
||||||
if dispatcher.messages[uri]['public']:
|
pattern = dispatcher.messages[version][message]['pattern']
|
||||||
print(f' - {web_uri}')
|
print(f' - {web_message} ({pattern})')
|
||||||
else:
|
routes.append(post(web_message, handle))
|
||||||
pattern = dispatcher.messages[uri]['pattern']
|
|
||||||
print(f' - {web_uri} (private {pattern})')
|
|
||||||
routes.append(post(web_uri, handle))
|
|
||||||
routes.append(get(f'/api/{version}', api))
|
|
||||||
print()
|
print()
|
||||||
|
print(_('======== Registered extra routes ========'))
|
||||||
|
for path, extra in extra_routes.items():
|
||||||
|
version = extra['version']
|
||||||
|
path = f'/api/{version}{path}'
|
||||||
|
extra['path'] = path
|
||||||
|
extra_handler = type(path, (extra_route_handler,), extra)
|
||||||
|
routes.append(get(path, extra_handler))
|
||||||
|
print(f' - {path} (http_get)')
|
||||||
|
# routes.append(get(f'/api/{version}', api))
|
||||||
|
print()
|
||||||
|
del extra_routes
|
||||||
app.add_routes(routes)
|
app.add_routes(routes)
|
||||||
return app
|
await dispatcher.on_join()
|
||||||
|
return await loop.create_server(app.make_handler(), '*', get_config()['http_server']['port'])
|
||||||
|
|
||||||
|
|
||||||
tiramisu = None
|
tiramisu = None
|
||||||
|
@ -1,56 +1,86 @@
|
|||||||
from typing import Dict
|
from typing import Dict, Any
|
||||||
|
from json import dumps
|
||||||
from .context import Context
|
from .context import Context
|
||||||
from .utils import _
|
from .utils import _
|
||||||
|
from .config import get_config
|
||||||
|
|
||||||
|
|
||||||
class Logger:
|
class Logger:
|
||||||
""" An object to manager log
|
""" An object to manager log
|
||||||
FIXME should add event to a database
|
|
||||||
"""
|
"""
|
||||||
|
async def insert(self,
|
||||||
|
msg: str,
|
||||||
|
path: str,
|
||||||
|
risotto_context: str,
|
||||||
|
level: str,
|
||||||
|
data: Any= None) -> None:
|
||||||
|
insert = 'INSERT INTO log(Msg, Path, Username, Level'
|
||||||
|
values = 'VALUES($1,$2,$3,$4'
|
||||||
|
args = [msg, path, risotto_context.username, level]
|
||||||
|
if data:
|
||||||
|
insert += ', Data'
|
||||||
|
values += ',$5'
|
||||||
|
args.append(dumps(data))
|
||||||
|
|
||||||
|
sql = insert + ') ' + values + ')'
|
||||||
|
await risotto_context.connection.fetch(sql, *args)
|
||||||
|
|
||||||
def _get_message_paths(self,
|
def _get_message_paths(self,
|
||||||
risotto_context: Context,
|
risotto_context: Context):
|
||||||
type: str):
|
|
||||||
paths = risotto_context.paths
|
paths = risotto_context.paths
|
||||||
if len(paths) == 1:
|
if risotto_context.type:
|
||||||
paths_msg = f' messages {type}ed: {paths[0]}'
|
paths_msg = f' {risotto_context.type} '
|
||||||
else:
|
else:
|
||||||
paths_msg = f' sub-messages {type}ed: '
|
paths_msg = ' '
|
||||||
|
if len(paths) == 1:
|
||||||
|
paths_msg += f'message: {paths[0]}'
|
||||||
|
else:
|
||||||
|
paths_msg += f'sub-messages: '
|
||||||
paths_msg += ' > '.join(paths)
|
paths_msg += ' > '.join(paths)
|
||||||
return paths_msg
|
return paths_msg
|
||||||
|
|
||||||
def error_msg(self,
|
async def error_msg(self,
|
||||||
version: 'str',
|
risotto_context: Context,
|
||||||
message: 'str',
|
arguments,
|
||||||
risotto_context: Context,
|
error: str,
|
||||||
arguments,
|
msg: str=''):
|
||||||
type: str,
|
|
||||||
error: str,
|
|
||||||
msg: str=''):
|
|
||||||
""" send message when an error append
|
""" send message when an error append
|
||||||
"""
|
"""
|
||||||
paths_msg = self._get_message_paths(risotto_context, type)
|
paths_msg = self._get_message_paths(risotto_context)
|
||||||
print(_(f'{risotto_context.username}: ERROR: {error} ({paths_msg} with arguments "{arguments}" {msg})'))
|
print(_(f'{risotto_context.username}: ERROR: {error} ({paths_msg} with arguments "{arguments}": {msg})'))
|
||||||
|
await self.insert(msg,
|
||||||
|
paths_msg,
|
||||||
|
risotto_context,
|
||||||
|
'Error',
|
||||||
|
arguments)
|
||||||
|
|
||||||
def info_msg(self,
|
async def info_msg(self,
|
||||||
version: 'str',
|
risotto_context: Context,
|
||||||
message: 'str',
|
arguments: Dict,
|
||||||
risotto_context: Context,
|
msg: str=''):
|
||||||
arguments: Dict,
|
|
||||||
type: str,
|
|
||||||
msg: str=''):
|
|
||||||
""" send message with common information
|
""" send message with common information
|
||||||
"""
|
"""
|
||||||
if risotto_context.paths:
|
if risotto_context.paths:
|
||||||
paths_msg = self._get_message_paths(risotto_context, type)
|
paths_msg = self._get_message_paths(risotto_context)
|
||||||
else:
|
else:
|
||||||
paths_msg = ''
|
paths_msg = ''
|
||||||
tmsg = _(f'{risotto_context.username}: INFO:{paths_msg}')
|
if get_config()['global']['debug']:
|
||||||
if arguments:
|
print(_(f'{risotto_context.username}: INFO:{paths_msg}'))
|
||||||
tmsg += _(f' with arguments "{arguments}"')
|
await self.insert(msg,
|
||||||
if msg:
|
paths_msg,
|
||||||
tmsg += f' {msg}'
|
risotto_context,
|
||||||
|
'Info',
|
||||||
|
arguments)
|
||||||
|
|
||||||
print(tmsg)
|
async def info(self,
|
||||||
|
risotto_context,
|
||||||
|
msg):
|
||||||
|
if get_config()['global']['debug']:
|
||||||
|
print(msg)
|
||||||
|
await self.insert(msg,
|
||||||
|
None,
|
||||||
|
risotto_context,
|
||||||
|
'Info')
|
||||||
|
|
||||||
|
|
||||||
log = Logger()
|
log = Logger()
|
||||||
|
@ -3,19 +3,39 @@ 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
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
class DictOption(Option):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_type = 'dict'
|
||||||
|
_display_name = _('dict')
|
||||||
|
|
||||||
|
def validate(self, value):
|
||||||
|
if not isinstance(value, dict):
|
||||||
|
raise ValueError()
|
||||||
|
|
||||||
|
|
||||||
|
class AnyOption(Option):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_type = 'any value'
|
||||||
|
_display_name = _('any')
|
||||||
|
|
||||||
|
def validate(self, value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MessageDefinition:
|
class MessageDefinition:
|
||||||
"""
|
"""
|
||||||
A MessageDefinition is a representation of a message in the Zephir application messaging context
|
A MessageDefinition is a representation of a message in the Zephir application messaging context
|
||||||
@ -23,33 +43,25 @@ class MessageDefinition:
|
|||||||
__slots__ = ('version',
|
__slots__ = ('version',
|
||||||
'uri',
|
'uri',
|
||||||
'description',
|
'description',
|
||||||
'sampleuse',
|
|
||||||
'domain',
|
|
||||||
'parameters',
|
'parameters',
|
||||||
'public',
|
|
||||||
'errors',
|
'errors',
|
||||||
'pattern',
|
'pattern',
|
||||||
'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()
|
||||||
self.public = False
|
|
||||||
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():
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
if key == 'public':
|
if key == 'pattern':
|
||||||
if not isinstance(value, bool):
|
|
||||||
raise ValueError(_("{} must be a boolean, not {}").format(key, value))
|
|
||||||
elif key == 'pattern':
|
|
||||||
if value not in ['rpc', 'event', 'error']:
|
if value not in ['rpc', 'event', 'error']:
|
||||||
raise Exception(_('unknown pattern {}').format(value))
|
raise Exception(_('unknown pattern {}').format(value))
|
||||||
elif key == 'parameters':
|
elif key == 'parameters':
|
||||||
@ -69,9 +81,8 @@ class MessageDefinition:
|
|||||||
getattr(self, key)
|
getattr(self, key)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise Exception(_('mandatory key not set {} message').format(key))
|
raise Exception(_('mandatory key not set {} message').format(key))
|
||||||
# message with pattern = error must be public
|
if self.uri != message:
|
||||||
if self.public is False and self.pattern == 'error':
|
raise Exception(_(f'yaml file name "{message}.yml" does not match uri "{self.uri}"'))
|
||||||
raise Exception(_('Error message must be public : {}').format(self.uri))
|
|
||||||
|
|
||||||
|
|
||||||
class ParameterDefinition:
|
class ParameterDefinition:
|
||||||
@ -130,11 +141,13 @@ class ResponseDefinition:
|
|||||||
'type',
|
'type',
|
||||||
'ref',
|
'ref',
|
||||||
'parameters',
|
'parameters',
|
||||||
'required')
|
'required',
|
||||||
|
'multi')
|
||||||
|
|
||||||
def __init__(self, responses):
|
def __init__(self, responses):
|
||||||
self.ref = None
|
self.ref = None
|
||||||
self.parameters = None
|
self.parameters = None
|
||||||
|
self.multi = False
|
||||||
self.required = []
|
self.required = []
|
||||||
for key, value in responses.items():
|
for key, value in responses.items():
|
||||||
if key in ['parameters', 'required']:
|
if key in ['parameters', 'required']:
|
||||||
@ -142,6 +155,7 @@ class ResponseDefinition:
|
|||||||
elif key == 'type':
|
elif key == 'type':
|
||||||
if value.startswith('[]'):
|
if value.startswith('[]'):
|
||||||
tvalue = value[2:]
|
tvalue = value[2:]
|
||||||
|
self.multi = True
|
||||||
else:
|
else:
|
||||||
tvalue = value
|
tvalue = value
|
||||||
if tvalue in customtypes:
|
if tvalue in customtypes:
|
||||||
@ -195,8 +209,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)
|
||||||
@ -209,7 +223,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:
|
||||||
@ -223,13 +237,16 @@ def split_message_uri(uri):
|
|||||||
def get_message_file_path(version, message):
|
def get_message_file_path(version, message):
|
||||||
return join(MESSAGE_ROOT_PATH, version, 'messages', message + '.yml')
|
return join(MESSAGE_ROOT_PATH, version, 'messages', message + '.yml')
|
||||||
|
|
||||||
def list_messages():
|
def list_messages(uris):
|
||||||
messages = listdir(MESSAGE_ROOT_PATH)
|
messages = listdir(MESSAGE_ROOT_PATH)
|
||||||
messages.sort()
|
messages.sort()
|
||||||
for version in messages:
|
for version in messages:
|
||||||
for message in listdir(join(MESSAGE_ROOT_PATH, version, 'messages')):
|
for message in listdir(join(MESSAGE_ROOT_PATH, version, 'messages')):
|
||||||
if message.endswith('.yml'):
|
if message.endswith('.yml'):
|
||||||
yield version + '.' + message.rsplit('.', 1)[0]
|
uri = version + '.' + message.rsplit('.', 1)[0]
|
||||||
|
if uris is not None and uri not in uris:
|
||||||
|
continue
|
||||||
|
yield uri
|
||||||
|
|
||||||
class CustomParam:
|
class CustomParam:
|
||||||
__slots__ = ('name',
|
__slots__ = ('name',
|
||||||
@ -404,7 +421,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),
|
||||||
@ -414,13 +431,23 @@ def _get_option(name,
|
|||||||
if hasattr(arg, 'default'):
|
if hasattr(arg, 'default'):
|
||||||
kwargs['default'] = arg.default
|
kwargs['default'] = arg.default
|
||||||
type_ = arg.type
|
type_ = arg.type
|
||||||
if type_ == 'Dict' or 'String' in type_ or 'Any' in type_:
|
if type_.startswith('[]'):
|
||||||
return StrOption(**kwargs)
|
kwargs['multi'] = True
|
||||||
|
type_ = type_[2:]
|
||||||
|
if type_ == 'Dict':
|
||||||
|
obj = DictOption(**kwargs)
|
||||||
|
elif type_ == 'String':
|
||||||
|
obj = StrOption(**kwargs)
|
||||||
|
elif type_ == 'Any':
|
||||||
|
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,
|
||||||
@ -434,13 +461,17 @@ 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 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))
|
||||||
|
|
||||||
|
|
||||||
@ -448,53 +479,40 @@ def _parse_responses(message_def,
|
|||||||
file_path):
|
file_path):
|
||||||
"""build option with returns
|
"""build option with returns
|
||||||
"""
|
"""
|
||||||
responses = OrderedDict()
|
if message_def.response.parameters is None:
|
||||||
if message_def.response:
|
raise Exception('uri "{}" did not returned any valid parameters.'.format(message_def.uri))
|
||||||
keys = {'': {'description': '',
|
|
||||||
'columns': {}}}
|
|
||||||
provides = {}
|
|
||||||
to_list = True
|
|
||||||
param_type = message_def.response.type
|
|
||||||
|
|
||||||
if param_type.startswith('[]'):
|
options = []
|
||||||
to_list = False
|
names = []
|
||||||
param_type = param_type[2:]
|
for name, obj in message_def.response.parameters.items():
|
||||||
if param_type in ['Dict', 'File']:
|
if name in names:
|
||||||
pass
|
raise Exception('multi response with name {} in {}'.format(name, file_path))
|
||||||
|
names.append(name)
|
||||||
|
|
||||||
if message_def.response.parameters is not None:
|
kwargs = {'name': name,
|
||||||
for name, obj in message_def.response.parameters.items():
|
'doc': obj.description.strip().rstrip()}
|
||||||
if name in responses:
|
type_ = obj.type
|
||||||
raise Exception('multi response with name {} in {}'.format(name, file_path))
|
if type_.startswith('[]'):
|
||||||
|
kwargs['multi'] = True
|
||||||
descr = obj.description.strip().rstrip()
|
type_ = type_[2:]
|
||||||
keys['']['columns'][name] = {'description': obj.description,
|
option = {'String': StrOption,
|
||||||
'type': obj.type}
|
'Number': IntOption,
|
||||||
ref = obj.ref
|
'Boolean': BoolOption,
|
||||||
if ref:
|
'Dict': DictOption,
|
||||||
provides[name] = ref
|
# FIXME
|
||||||
else:
|
'File': StrOption}.get(type_)
|
||||||
keys['']['columns'][name] = {'description': descr,
|
if not option:
|
||||||
'type': obj.type}
|
raise Exception(f'unknown param type {obj.type} in responses of message {message_def.uri}')
|
||||||
ref = obj.ref
|
if hasattr(obj, 'default'):
|
||||||
if ref:
|
kwargs['default'] = obj.default
|
||||||
provides[name] = ref
|
|
||||||
responses['keys'] = keys
|
|
||||||
responses['to_list'] = to_list
|
|
||||||
responses['to_dict'] = False
|
|
||||||
responses['provides'] = provides
|
|
||||||
else:
|
else:
|
||||||
name = 'response'
|
kwargs['properties'] = ('mandatory',)
|
||||||
keys['']['columns'][name] = {'description': message_def.response.description,
|
options.append(option(**kwargs))
|
||||||
'type': message_def.response.type}
|
od = OptionDescription(message_def.uri,
|
||||||
ref = message_def.response.ref
|
message_def.response.description,
|
||||||
if ref:
|
options)
|
||||||
provides[name] = ref
|
od.impl_set_information('multi', message_def.response.multi)
|
||||||
responses['keys'] = keys
|
return od
|
||||||
responses['to_list'] = to_list
|
|
||||||
responses['to_dict'] = True
|
|
||||||
responses['provides'] = provides
|
|
||||||
return responses
|
|
||||||
|
|
||||||
|
|
||||||
def _getoptions_from_yml(message_def,
|
def _getoptions_from_yml(message_def,
|
||||||
@ -558,20 +576,19 @@ def _get_root_option(select_option, optiondescriptions):
|
|||||||
return OptionDescription('root', 'root', options_obj)
|
return OptionDescription('root', 'root', options_obj)
|
||||||
|
|
||||||
|
|
||||||
def get_messages(load_shortarg=False, only_public=False):
|
def get_messages(load_shortarg=False,
|
||||||
|
uris=None):
|
||||||
"""generate description from yml files
|
"""generate description from yml files
|
||||||
"""
|
"""
|
||||||
optiondescriptions = OrderedDict()
|
optiondescriptions = OrderedDict()
|
||||||
optiondescriptions_name = []
|
optiondescriptions_name = []
|
||||||
optiondescriptions_info = {}
|
optiondescriptions_info = {}
|
||||||
responses = OrderedDict()
|
|
||||||
needs = OrderedDict()
|
needs = OrderedDict()
|
||||||
messages = list(list_messages())
|
messages = list(list_messages(uris))
|
||||||
messages.sort()
|
messages.sort()
|
||||||
for message_name in messages:
|
for message_name in messages:
|
||||||
message_def = get_message(message_name)
|
message_def = get_message(message_name)
|
||||||
if message_def.pattern not in ['rpc', 'event'] or \
|
if message_def.pattern not in ['rpc', 'event']:
|
||||||
(not message_def.public and only_public):
|
|
||||||
continue
|
continue
|
||||||
optiondescriptions_name.append(message_def.uri)
|
optiondescriptions_name.append(message_def.uri)
|
||||||
optiondescriptions_name.sort()
|
optiondescriptions_name.sort()
|
||||||
@ -581,14 +598,15 @@ def get_messages(load_shortarg=False, only_public=False):
|
|||||||
properties=frozenset(['mandatory', 'positional']))
|
properties=frozenset(['mandatory', 'positional']))
|
||||||
for message_name in messages:
|
for message_name in messages:
|
||||||
message_def = get_message(message_name)
|
message_def = get_message(message_name)
|
||||||
if message_def.pattern not in ['rpc', 'event'] or \
|
if message_def.pattern not in ['rpc', 'event']:
|
||||||
(not message_def.public and only_public):
|
|
||||||
continue
|
continue
|
||||||
optiondescriptions_info[message_def.uri] = {'pattern': message_def.pattern,
|
optiondescriptions_info[message_def.uri] = {'pattern': message_def.pattern}
|
||||||
'public': message_def.public}
|
if message_def.pattern == 'rpc':
|
||||||
|
optiondescriptions_info[message_def.uri]['response'] = _parse_responses(message_def,
|
||||||
|
message_name)
|
||||||
|
elif message_def.response:
|
||||||
|
raise Exception(f'response not allowed for {message_def.uri}')
|
||||||
version = message_name.split('.')[0]
|
version = message_name.split('.')[0]
|
||||||
if message_def.uri in responses:
|
|
||||||
raise Exception('uri {} allready loader'.format(message_def.uri))
|
|
||||||
_getoptions_from_yml(message_def,
|
_getoptions_from_yml(message_def,
|
||||||
version,
|
version,
|
||||||
optiondescriptions,
|
optiondescriptions,
|
||||||
@ -596,16 +614,6 @@ def get_messages(load_shortarg=False, only_public=False):
|
|||||||
needs,
|
needs,
|
||||||
select_option,
|
select_option,
|
||||||
load_shortarg)
|
load_shortarg)
|
||||||
responses[message_def.uri] = _parse_responses(message_def,
|
|
||||||
message_name)
|
|
||||||
|
|
||||||
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
|
||||||
|
282
src/risotto/register.py
Normal file
282
src/risotto/register.py
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
from tiramisu import Config
|
||||||
|
from inspect import signature
|
||||||
|
from typing import Callable, Optional
|
||||||
|
import asyncpg
|
||||||
|
from json import dumps, loads
|
||||||
|
|
||||||
|
from .utils import _
|
||||||
|
from .error import RegistrationError
|
||||||
|
from .message import get_messages
|
||||||
|
from .context import Context
|
||||||
|
from .config import INTERNAL_USER, get_config
|
||||||
|
|
||||||
|
|
||||||
|
def register(uris: str,
|
||||||
|
notification: str=None):
|
||||||
|
""" Decorator to register function to the dispatcher
|
||||||
|
"""
|
||||||
|
if not isinstance(uris, list):
|
||||||
|
uris = [uris]
|
||||||
|
|
||||||
|
def decorator(function):
|
||||||
|
for uri in uris:
|
||||||
|
version, message = uri.split('.', 1)
|
||||||
|
dispatcher.set_function(version,
|
||||||
|
message,
|
||||||
|
notification,
|
||||||
|
function)
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterDispatcher:
|
||||||
|
def __init__(self):
|
||||||
|
# reference to instanciate module (to inject self in method): {"module_name": instance_of_module}
|
||||||
|
self.injected_self = {}
|
||||||
|
# postgresql pool
|
||||||
|
self.pool = None
|
||||||
|
# list of uris with informations: {"v1": {"module_name.xxxxx": yyyyyy}}
|
||||||
|
self.messages = {}
|
||||||
|
# load tiramisu objects
|
||||||
|
messages, self.option = get_messages()
|
||||||
|
#FIXME
|
||||||
|
version = 'v1'
|
||||||
|
self.messages[version] = {}
|
||||||
|
for tiramisu_message, obj in messages.items():
|
||||||
|
self.messages[version][tiramisu_message] = obj
|
||||||
|
|
||||||
|
def get_function_args(self,
|
||||||
|
function: Callable):
|
||||||
|
# remove self
|
||||||
|
first_argument_index = 1
|
||||||
|
return [param.name for param in list(signature(function).parameters.values())[first_argument_index:]]
|
||||||
|
|
||||||
|
async def valid_rpc_params(self,
|
||||||
|
version: str,
|
||||||
|
message: str,
|
||||||
|
function: Callable,
|
||||||
|
module_name: str):
|
||||||
|
""" parameters function must have strictly all arguments with the correct name
|
||||||
|
"""
|
||||||
|
async def get_message_args():
|
||||||
|
# load config
|
||||||
|
config = await Config(self.option)
|
||||||
|
await config.property.read_write()
|
||||||
|
# set message to the uri name
|
||||||
|
await config.option('message').value.set(message)
|
||||||
|
# get message argument
|
||||||
|
dico = await config.option(message).value.dict()
|
||||||
|
return set(dico.keys())
|
||||||
|
|
||||||
|
def get_function_args():
|
||||||
|
function_args = self.get_function_args(function)
|
||||||
|
# risotto_context is a special argument, remove it
|
||||||
|
function_args = function_args[1:]
|
||||||
|
return set(function_args)
|
||||||
|
|
||||||
|
# get message arguments
|
||||||
|
message_args = await get_message_args()
|
||||||
|
# get function arguments
|
||||||
|
function_args = get_function_args()
|
||||||
|
# compare message arguments with function parameter
|
||||||
|
# it must not have more or less arguments
|
||||||
|
if message_args != function_args:
|
||||||
|
# raise if arguments are not equal
|
||||||
|
msg = []
|
||||||
|
missing_function_args = message_args - function_args
|
||||||
|
if missing_function_args:
|
||||||
|
msg.append(_(f'missing arguments: {missing_function_args}'))
|
||||||
|
extra_function_args = function_args - message_args
|
||||||
|
if extra_function_args:
|
||||||
|
msg.append(_(f'extra arguments: {extra_function_args}'))
|
||||||
|
function_name = function.__name__
|
||||||
|
msg = _(' and ').join(msg)
|
||||||
|
raise RegistrationError(_(f'error with {module_name}.{function_name} arguments: {msg}'))
|
||||||
|
|
||||||
|
async def valid_event_params(self,
|
||||||
|
version: str,
|
||||||
|
message: str,
|
||||||
|
function: Callable,
|
||||||
|
module_name: str):
|
||||||
|
""" parameters function validation for event messages
|
||||||
|
"""
|
||||||
|
async def get_message_args():
|
||||||
|
# load config
|
||||||
|
config = await Config(self.option)
|
||||||
|
await config.property.read_write()
|
||||||
|
# set message to the message name
|
||||||
|
await config.option('message').value.set(message)
|
||||||
|
# get message argument
|
||||||
|
dico = await config.option(message).value.dict()
|
||||||
|
return set(dico.keys())
|
||||||
|
|
||||||
|
def get_function_args():
|
||||||
|
function_args = self.get_function_args(function)
|
||||||
|
# risotto_context is a special argument, remove it
|
||||||
|
function_args = function_args[1:]
|
||||||
|
return set(function_args)
|
||||||
|
|
||||||
|
# get message arguments
|
||||||
|
message_args = await get_message_args()
|
||||||
|
# get function arguments
|
||||||
|
function_args = get_function_args()
|
||||||
|
# compare message arguments with function parameter
|
||||||
|
# it can have less arguments but not more
|
||||||
|
extra_function_args = function_args - message_args
|
||||||
|
if extra_function_args:
|
||||||
|
# raise if too many arguments
|
||||||
|
function_name = function.__name__
|
||||||
|
msg = _(f'extra arguments: {extra_function_args}')
|
||||||
|
raise RegistrationError(_(f'error with {module_name}.{function_name} arguments: {msg}'))
|
||||||
|
|
||||||
|
def set_function(self,
|
||||||
|
version: str,
|
||||||
|
message: str,
|
||||||
|
notification: str,
|
||||||
|
function: Callable):
|
||||||
|
""" register a function to an URI
|
||||||
|
URI is a message
|
||||||
|
"""
|
||||||
|
|
||||||
|
# check if message exists
|
||||||
|
if message not in self.messages[version]:
|
||||||
|
raise RegistrationError(_(f'the message {message} not exists'))
|
||||||
|
|
||||||
|
# xxx module can only be register with v1.xxxx..... message
|
||||||
|
module_name = function.__module__.split('.')[-2]
|
||||||
|
message_namespace = message.split('.', 1)[0]
|
||||||
|
if self.messages[version][message]['pattern'] == 'rpc' and message_namespace != module_name:
|
||||||
|
raise RegistrationError(_(f'cannot registered the "{message}" message in module "{module_name}"'))
|
||||||
|
|
||||||
|
# True if first argument is the risotto_context
|
||||||
|
function_args = self.get_function_args(function)
|
||||||
|
function_args.pop(0)
|
||||||
|
|
||||||
|
# check if already register
|
||||||
|
if 'function' in self.messages[version][message]:
|
||||||
|
raise RegistrationError(_(f'uri {version}.{message} already registered'))
|
||||||
|
|
||||||
|
# register
|
||||||
|
if self.messages[version][message]['pattern'] == 'rpc':
|
||||||
|
register = self.register_rpc
|
||||||
|
else:
|
||||||
|
register = self.register_event
|
||||||
|
register(version,
|
||||||
|
message,
|
||||||
|
module_name,
|
||||||
|
function,
|
||||||
|
function_args,
|
||||||
|
notification)
|
||||||
|
|
||||||
|
def register_rpc(self,
|
||||||
|
version: str,
|
||||||
|
message: str,
|
||||||
|
module_name: str,
|
||||||
|
function: Callable,
|
||||||
|
function_args: list,
|
||||||
|
notification: Optional[str]):
|
||||||
|
self.messages[version][message]['module'] = module_name
|
||||||
|
self.messages[version][message]['function'] = function
|
||||||
|
self.messages[version][message]['arguments'] = function_args
|
||||||
|
if notification:
|
||||||
|
self.messages[version][message]['notification'] = notification
|
||||||
|
|
||||||
|
def register_event(self,
|
||||||
|
version: str,
|
||||||
|
message: str,
|
||||||
|
module_name: str,
|
||||||
|
function: Callable,
|
||||||
|
function_args: list,
|
||||||
|
notification: Optional[str]):
|
||||||
|
if 'functions' not in self.messages[version][message]:
|
||||||
|
self.messages[version][message]['functions'] = []
|
||||||
|
|
||||||
|
dico = {'module': module_name,
|
||||||
|
'function': function,
|
||||||
|
'arguments': function_args}
|
||||||
|
if notification and notification:
|
||||||
|
dico['notification'] = notification
|
||||||
|
self.messages[version][message]['functions'].append(dico)
|
||||||
|
|
||||||
|
def set_module(self, module_name, module, test):
|
||||||
|
""" register and instanciate a new module
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.injected_self[module_name] = module.Risotto(test)
|
||||||
|
except AttributeError as err:
|
||||||
|
raise RegistrationError(_(f'unable to register the module {module_name}, this module must have Risotto class'))
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
""" check if all messages have a function
|
||||||
|
"""
|
||||||
|
missing_messages = []
|
||||||
|
for version, messages in self.messages.items():
|
||||||
|
for message, message_obj in messages.items():
|
||||||
|
if not 'functions' in message_obj and not 'function' in message_obj:
|
||||||
|
if message_obj['pattern'] == 'event':
|
||||||
|
print(f'{message} prêche dans le désert')
|
||||||
|
else:
|
||||||
|
missing_messages.append(message)
|
||||||
|
if missing_messages:
|
||||||
|
raise RegistrationError(_(f'missing uri {missing_messages}'))
|
||||||
|
|
||||||
|
async def on_join(self):
|
||||||
|
async with self.pool.acquire() as connection:
|
||||||
|
await connection.set_type_codec(
|
||||||
|
'json',
|
||||||
|
encoder=dumps,
|
||||||
|
decoder=loads,
|
||||||
|
schema='pg_catalog'
|
||||||
|
)
|
||||||
|
async with connection.transaction():
|
||||||
|
for module_name, module in self.injected_self.items():
|
||||||
|
risotto_context = Context()
|
||||||
|
risotto_context.username = INTERNAL_USER
|
||||||
|
risotto_context.paths.append(f'{module_name}.on_join')
|
||||||
|
risotto_context.type = None
|
||||||
|
risotto_context.connection = connection
|
||||||
|
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')
|
||||||
|
|
||||||
|
engine = db_conf.get('engine')
|
||||||
|
host = db_conf.get('host')
|
||||||
|
dbname = db_conf.get('dbname')
|
||||||
|
dbuser = db_conf.get('user')
|
||||||
|
dbpassword = db_conf.get('password')
|
||||||
|
dbport = db_conf.get('port')
|
||||||
|
cfg = "{}://{}:{}@{}:{}/{}".format(engine, dbuser, dbpassword, host, dbport, dbname)
|
||||||
|
self.pool = await asyncpg.create_pool(cfg)
|
||||||
|
async with self.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}')
|
||||||
|
|
@ -4,15 +4,17 @@ from importlib import import_module
|
|||||||
from ..dispatcher import dispatcher
|
from ..dispatcher import dispatcher
|
||||||
|
|
||||||
|
|
||||||
def list_import():
|
def load_services(modules=None,
|
||||||
|
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))
|
||||||
for filename in listdir(abs_here):
|
if not modules:
|
||||||
|
modules = listdir(abs_here)
|
||||||
|
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)
|
||||||
dispatcher.validate()
|
if validate:
|
||||||
|
dispatcher.validate()
|
||||||
|
|
||||||
list_import()
|
|
||||||
|
1
src/risotto/services/applicationservice/__init__.py
Normal file
1
src/risotto/services/applicationservice/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .applicationservice import Risotto
|
121
src/risotto/services/applicationservice/applicationservice.py
Normal file
121
src/risotto/services/applicationservice/applicationservice.py
Normal 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')
|
||||||
|
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')
|
||||||
|
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')
|
||||||
|
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')
|
||||||
|
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)
|
@ -1,26 +1,441 @@
|
|||||||
|
from lxml.etree import parse
|
||||||
|
from io import BytesIO
|
||||||
|
from os import unlink
|
||||||
|
from os.path import isdir, isfile, join
|
||||||
|
from traceback import print_exc
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
from tiramisu import Storage, delete_session, MetaConfig, MixConfig
|
||||||
|
from rougail import load as rougail_load
|
||||||
|
|
||||||
from ...controller import Controller
|
from ...controller import Controller
|
||||||
from ...dispatcher import register
|
from ...register import register
|
||||||
|
from ...config import DATABASE_DIR, ROUGAIL_DTD_PATH, get_config
|
||||||
|
from ...context import Context
|
||||||
|
from ...utils import _
|
||||||
|
from ...error import CallError, RegistrationError
|
||||||
|
from ...logger import log
|
||||||
|
|
||||||
|
|
||||||
class Risotto(Controller):
|
class Risotto(Controller):
|
||||||
@register('v1.config.configuration.server.updated')
|
|
||||||
async def server_created(self, server_id):
|
|
||||||
print('pouet ' + str(server_id))
|
|
||||||
|
|
||||||
@register('v1.config.session.server.start', None)
|
def __init__(self,
|
||||||
async def get_configuration(self, risotto_context, id):
|
test) -> None:
|
||||||
#stop = await self.call('v1.config.session.server.stop', risotto_context, sessionid=str(id))
|
global conf_storage
|
||||||
#await self.publish('v1.config.configuration.server.updated', risotto_context, server_id=1, deploy=True)
|
self.cache_root_path = join(get_config().get('cache').get('root_path'), 'servermodel')
|
||||||
return {'start': id}
|
for dirname in [self.cache_root_path, DATABASE_DIR]:
|
||||||
|
if not isdir(dirname):
|
||||||
|
raise RegistrationError(_(f'unable to find the cache dir "{dirname}"'))
|
||||||
|
if not test:
|
||||||
|
self.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR)
|
||||||
|
self.servermodel = {}
|
||||||
|
self.server = {}
|
||||||
|
super().__init__(test)
|
||||||
|
|
||||||
@register('v1.config.session.server.stop', None)
|
async def on_join(self,
|
||||||
async def get_configuration2(self, sessionid, save):
|
risotto_context: Context) -> None:
|
||||||
return {'stop': sessionid}
|
""" pre-load servermodel and server
|
||||||
|
"""
|
||||||
|
await self.load_servermodels(risotto_context)
|
||||||
|
await self.load_servers(risotto_context)
|
||||||
|
|
||||||
@register('v1.config.configuration.server.get', None)
|
async def load_servermodels(self,
|
||||||
async def get_configuration3(self, server_id, deploy):
|
risotto_context: Context) -> None:
|
||||||
return {'get': server_id}
|
""" load all available servermodels
|
||||||
|
"""
|
||||||
|
await log.info_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
'Load servermodels')
|
||||||
|
servermodels = await self.call('v1.servermodel.list',
|
||||||
|
risotto_context)
|
||||||
|
|
||||||
@register('v1.config.configuration.server.deploy', None)
|
# load each servermodels
|
||||||
async def get_configuration4(self, server_id):
|
for servermodel in servermodels:
|
||||||
return {'deploy': server_id}
|
try:
|
||||||
|
await self.load_servermodel(risotto_context,
|
||||||
|
servermodel['servermodel_id'],
|
||||||
|
servermodel['servermodel_name'])
|
||||||
|
except CallError as err:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# do link to this servermodel
|
||||||
|
for servermodel in servermodels:
|
||||||
|
if 'servermodel_parents_id' in servermodel:
|
||||||
|
for servermodelparentid in servermodel['servermodel_parents_id']:
|
||||||
|
await self.servermodel_legacy(risotto_context,
|
||||||
|
servermodel['servermodel_name'],
|
||||||
|
servermodel['servermodel_id'],
|
||||||
|
servermodelparentid)
|
||||||
|
|
||||||
|
def get_funcs_filename(self,
|
||||||
|
servermodel_id: int):
|
||||||
|
return join(self.cache_root_path, str(servermodel_id), "funcs.py")
|
||||||
|
|
||||||
|
async def load_servermodel(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
servermodel_id: int,
|
||||||
|
servermodel_name: str) -> None:
|
||||||
|
""" Loads a servermodel
|
||||||
|
"""
|
||||||
|
cache_file = join(self.cache_root_path, str(servermodel_id), "dictionaries.xml")
|
||||||
|
funcs_file = self.get_funcs_filename(servermodel_id)
|
||||||
|
await log.info_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
f'Load servermodel {servermodel_name} ({servermodel_id})')
|
||||||
|
|
||||||
|
# use file in cache
|
||||||
|
with open(cache_file) as fileio:
|
||||||
|
xmlroot = parse(fileio).getroot()
|
||||||
|
try:
|
||||||
|
self.servermodel[servermodel_id] = await self.build_metaconfig(servermodel_id,
|
||||||
|
servermodel_name,
|
||||||
|
xmlroot,
|
||||||
|
funcs_file)
|
||||||
|
except Exception as err:
|
||||||
|
if get_config().get('global').get('debug'):
|
||||||
|
print_exc()
|
||||||
|
msg = _(f'unable to load {servermodel_name}: {err}')
|
||||||
|
await log.error_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
msg)
|
||||||
|
|
||||||
|
async def build_metaconfig(self,
|
||||||
|
servermodel_id: int,
|
||||||
|
servermodel_name: str,
|
||||||
|
xmlroot: str,
|
||||||
|
funcs_file: str) -> MetaConfig:
|
||||||
|
""" Build metaconfig for a servermodel
|
||||||
|
"""
|
||||||
|
# build tiramisu's session ID
|
||||||
|
session_id = f'v_{servermodel_id}'
|
||||||
|
optiondescription = rougail_load(xmlroot,
|
||||||
|
ROUGAIL_DTD_PATH,
|
||||||
|
funcs_file)
|
||||||
|
|
||||||
|
# build servermodel metaconfig (v_xxx.m_v_xxx)
|
||||||
|
metaconfig = await MetaConfig([],
|
||||||
|
optiondescription=optiondescription,
|
||||||
|
persistent=True,
|
||||||
|
session_id=session_id,
|
||||||
|
storage=self.save_storage)
|
||||||
|
mixconfig = await MixConfig(children=[],
|
||||||
|
optiondescription=optiondescription,
|
||||||
|
persistent=True,
|
||||||
|
session_id='m_' + session_id,
|
||||||
|
storage=self.save_storage)
|
||||||
|
await metaconfig.config.add(mixconfig)
|
||||||
|
|
||||||
|
# change default rights
|
||||||
|
ro_origin = await metaconfig.property.getdefault('read_only', 'append')
|
||||||
|
ro_append = frozenset(ro_origin - {'force_store_value'})
|
||||||
|
rw_origin = await metaconfig.property.getdefault('read_write', 'append')
|
||||||
|
rw_append = frozenset(rw_origin - {'force_store_value'})
|
||||||
|
await metaconfig.property.setdefault(ro_append, 'read_only', 'append')
|
||||||
|
await metaconfig.property.setdefault(rw_append, 'read_write', 'append')
|
||||||
|
|
||||||
|
await metaconfig.property.read_only()
|
||||||
|
await metaconfig.permissive.add('basic')
|
||||||
|
await metaconfig.permissive.add('normal')
|
||||||
|
await metaconfig.permissive.add('expert')
|
||||||
|
|
||||||
|
# set informtion and owner
|
||||||
|
await metaconfig.owner.set('v_{}'.format(servermodel_name))
|
||||||
|
await metaconfig.information.set('servermodel_id', servermodel_id)
|
||||||
|
await metaconfig.information.set('servermodel_name', servermodel_name)
|
||||||
|
|
||||||
|
# return configuration
|
||||||
|
return metaconfig
|
||||||
|
|
||||||
|
async def servermodel_legacy(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
servermodel_name: str,
|
||||||
|
servermodel_id: int,
|
||||||
|
servermodel_parent_id: int) -> None:
|
||||||
|
""" Make link between parent and children
|
||||||
|
"""
|
||||||
|
if servermodel_parent_id is None:
|
||||||
|
return
|
||||||
|
if not self.servermodel.get(servermodel_parent_id):
|
||||||
|
msg = _(f'Servermodel with id {servermodel_parent_id} not loaded, skipping legacy for servermodel {servermodel_name} ({servermodel_id})')
|
||||||
|
await log.error_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
msg)
|
||||||
|
return
|
||||||
|
servermodel_parent = self.servermodel[servermodel_parent_id]
|
||||||
|
servermodel_parent_name = await servermodel_parent.information.get('servermodel_name')
|
||||||
|
msg = _(f'Create legacy of servermodel {servermodel_name} ({servermodel_id}) with parent {servermodel_parent_name} ({servermodel_parent_id})')
|
||||||
|
await log.info_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
msg)
|
||||||
|
|
||||||
|
# do link
|
||||||
|
mix = await servermodel_parent.config.get('m_v_' + str(servermodel_parent_id))
|
||||||
|
try:
|
||||||
|
await mix.config.add(self.servermodel[servermodel_id])
|
||||||
|
except Exception as err:
|
||||||
|
await log.error_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
str(err))
|
||||||
|
|
||||||
|
async def load_servers(self,
|
||||||
|
risotto_context: Context) -> None:
|
||||||
|
""" load all available servers
|
||||||
|
"""
|
||||||
|
await log.info_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
f'Load servers')
|
||||||
|
# get all servers
|
||||||
|
servers = await self.call('v1.server.list',
|
||||||
|
risotto_context)
|
||||||
|
# loads servers
|
||||||
|
for server in servers:
|
||||||
|
try:
|
||||||
|
await self.load_server(risotto_context,
|
||||||
|
server['server_id'],
|
||||||
|
server['server_name'],
|
||||||
|
server['server_servermodel_id'])
|
||||||
|
except Exception as err:
|
||||||
|
if get_config().get('global').get('debug'):
|
||||||
|
print_exc()
|
||||||
|
server_name = server['server_name']
|
||||||
|
server_id = server['server_id']
|
||||||
|
msg = _(f'unable to load server {server_name} ({server_id}): {err}')
|
||||||
|
await log.error_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
msg)
|
||||||
|
|
||||||
|
async def load_server(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
server_id: int,
|
||||||
|
server_name: str,
|
||||||
|
server_servermodel_id: int) -> None:
|
||||||
|
""" Loads a server
|
||||||
|
"""
|
||||||
|
if server_id in self.server:
|
||||||
|
return
|
||||||
|
await log.info_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
f'Load server {server_name} ({server_id})')
|
||||||
|
if not server_servermodel_id in self.servermodel:
|
||||||
|
msg = f'unable to find servermodel with id {server_servermodel_id}'
|
||||||
|
await log.error_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
msg)
|
||||||
|
raise CallError(msg)
|
||||||
|
|
||||||
|
# check if server was already created
|
||||||
|
session_id = f's_{server_id}'
|
||||||
|
|
||||||
|
# get the servermodel's metaconfig
|
||||||
|
metaconfig = self.servermodel[server_servermodel_id]
|
||||||
|
|
||||||
|
# create server configuration and server 'to deploy' configuration and store it
|
||||||
|
self.server[server_id] = {'server': await self.build_config(session_id,
|
||||||
|
server_id,
|
||||||
|
server_name,
|
||||||
|
metaconfig),
|
||||||
|
'server_to_deploy': await self.build_config(f'std_{server_id}',
|
||||||
|
server_id,
|
||||||
|
server_name,
|
||||||
|
metaconfig),
|
||||||
|
'funcs_file': self.get_funcs_filename(server_servermodel_id)}
|
||||||
|
|
||||||
|
async def build_config(self,
|
||||||
|
session_id: str,
|
||||||
|
server_id: int,
|
||||||
|
server_name: str,
|
||||||
|
metaconfig: MetaConfig) -> None:
|
||||||
|
""" build server's config
|
||||||
|
"""
|
||||||
|
config = await metaconfig.config.new(session_id,
|
||||||
|
storage=self.save_storage,
|
||||||
|
persistent=True)
|
||||||
|
await config.information.set('server_id', server_id)
|
||||||
|
await config.information.set('server_name', server_name)
|
||||||
|
await config.owner.set(server_name)
|
||||||
|
await config.property.read_only()
|
||||||
|
return config
|
||||||
|
|
||||||
|
@register('v1.server.created')
|
||||||
|
async def server_created(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
server_id: int,
|
||||||
|
server_name: str,
|
||||||
|
server_servermodel_id: int) -> None:
|
||||||
|
""" Loads server's configuration when a new server is created
|
||||||
|
"""
|
||||||
|
await self.load_server(risotto_context,
|
||||||
|
server_id,
|
||||||
|
server_name,
|
||||||
|
server_servermodel_id)
|
||||||
|
|
||||||
|
@register('v1.server.deleted')
|
||||||
|
async def server_deleted(self,
|
||||||
|
server_id: int) -> None:
|
||||||
|
# delete config to it's parents
|
||||||
|
for server_type in ['server', 'server_to_deploy']:
|
||||||
|
config = self.server[server_id]['server']
|
||||||
|
for parent in await config.config.parents():
|
||||||
|
await parent.config.pop(await config.config.name())
|
||||||
|
delete_session(storage=self.save_storage,
|
||||||
|
session_id=await config.config.name())
|
||||||
|
# delete metaconfig
|
||||||
|
del self.server[server_id]
|
||||||
|
|
||||||
|
@register('v1.servermodel.created')
|
||||||
|
async def servermodel_created(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
servermodel_id: int,
|
||||||
|
servermodel_name: str,
|
||||||
|
servermodel_parents_id: List[int]) -> None:
|
||||||
|
""" when servermodels are created, load it and do link
|
||||||
|
"""
|
||||||
|
await self.load_and_link_servermodel(risotto_context,
|
||||||
|
servermodel_id,
|
||||||
|
servermodel_name,
|
||||||
|
servermodel_parents_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def load_and_link_servermodel(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
servermodel_id: int,
|
||||||
|
servermodel_name: str,
|
||||||
|
servermodel_parents_id: List[int]) -> None:
|
||||||
|
await self.load_servermodel(risotto_context,
|
||||||
|
servermodel_id,
|
||||||
|
servermodel_name)
|
||||||
|
if servermodel_parents_id is not None:
|
||||||
|
for servermodelparentid in servermodel_parents_id:
|
||||||
|
await self.servermodel_legacy(risotto_context,
|
||||||
|
servermodel_name,
|
||||||
|
servermodel_id,
|
||||||
|
servermodelparentid)
|
||||||
|
|
||||||
|
async def servermodel_delete(self,
|
||||||
|
servermodel_id: int) -> List[MetaConfig]:
|
||||||
|
metaconfig = self.servermodel.pop(servermodel_id)
|
||||||
|
mixconfig = await metaconfig.config.list()[0]
|
||||||
|
children = []
|
||||||
|
for child in await mixconfig.config.list():
|
||||||
|
children.append(child)
|
||||||
|
await mixconfig.config.pop(await child.config.name())
|
||||||
|
await metaconfig.config.pop(await mixconfig.config.name())
|
||||||
|
delete_session(storage=self.save_storage,
|
||||||
|
session_id=await mixconfig.config.name())
|
||||||
|
del mixconfig
|
||||||
|
for parent in await metaconfig.config.parents():
|
||||||
|
await parent.config.pop(await metaconfig.config.name())
|
||||||
|
delete_session(storage=self.save_storage,
|
||||||
|
session_id=await metaconfig.config.name())
|
||||||
|
return children
|
||||||
|
#
|
||||||
|
# @register('v1.servermodel.updated')
|
||||||
|
# async def servermodel_updated(self,
|
||||||
|
# risotto_context: Context,
|
||||||
|
# servermodel_id: int,
|
||||||
|
# servermodel_name: str,
|
||||||
|
# servermodel_parents_id: List[int]) -> None:
|
||||||
|
# log.info_msg(risotto_context,
|
||||||
|
# None,
|
||||||
|
# f'Reload servermodel {servermodel_name} ({servermodel_id})')
|
||||||
|
# # unlink cache to force download new aggregated file
|
||||||
|
# cache_file = join(self.cache_root_path, str(servermodel_id)+".xml")
|
||||||
|
# if isfile(cache_file):
|
||||||
|
# unlink(cache_file)
|
||||||
|
#
|
||||||
|
# # store all informations
|
||||||
|
# if servermodel_id in self.servermodel:
|
||||||
|
# old_values = await self.servermodel[servermodel_id].value.exportation()
|
||||||
|
# old_permissives = await self.servermodel[servermodel_id].permissive.exportation()
|
||||||
|
# old_properties = await self.servermodel[servermodel_id].property.exportation()
|
||||||
|
# children = await self.servermodel_delete(servermodel_id)
|
||||||
|
# else:
|
||||||
|
# old_values = None
|
||||||
|
#
|
||||||
|
# # create new one
|
||||||
|
# await self.load_and_link_servermodel(risotto_context,
|
||||||
|
# servermodel_id,
|
||||||
|
# servermodel_name,
|
||||||
|
# servermodel_parents_id)
|
||||||
|
#
|
||||||
|
# # migrates informations
|
||||||
|
# if old_values is not None:
|
||||||
|
# await self.servermodel[servermodel_id].value.importation(old_values)
|
||||||
|
# await self.servermodel[servermodel_id].permissive.importation(old_permissives)
|
||||||
|
# await self.servermodel[servermodel_id].property.importation(old_properties)
|
||||||
|
# for child in children:
|
||||||
|
# await self.servermodel_legacy(risotto_context,
|
||||||
|
# await child.information.get('servermodel_name'),
|
||||||
|
# await child.information.get('servermodel_id'),
|
||||||
|
# servermodel_id)
|
||||||
|
|
||||||
|
@register('v1.config.configuration.server.get')
|
||||||
|
async def get_configuration(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
server_name: str,
|
||||||
|
deployed: bool) -> bytes:
|
||||||
|
server = await self.call('v1.server.describe',
|
||||||
|
risotto_context,
|
||||||
|
server_name=server_name)
|
||||||
|
server_id = server['server_id']
|
||||||
|
if server_id not in self.server:
|
||||||
|
msg = _(f'cannot find server with id {server_id}')
|
||||||
|
await log.error_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
msg)
|
||||||
|
raise CallError(msg)
|
||||||
|
|
||||||
|
if deployed:
|
||||||
|
server = self.server[server_id]['server']
|
||||||
|
else:
|
||||||
|
server = self.server[server_id]['server_to_deploy']
|
||||||
|
|
||||||
|
await server.property.read_only()
|
||||||
|
try:
|
||||||
|
configuration = await server.value.dict(fullpath=True,
|
||||||
|
leader_to_list=True)
|
||||||
|
except:
|
||||||
|
if deployed:
|
||||||
|
msg = _(f'No configuration available for server {server_id}')
|
||||||
|
else:
|
||||||
|
msg = _(f'No undeployed configuration available for server {server_id}')
|
||||||
|
await log.error_msg(risotto_context,
|
||||||
|
None,
|
||||||
|
msg)
|
||||||
|
raise CallError(msg)
|
||||||
|
return {'server_name': server_name,
|
||||||
|
'deployed': deployed,
|
||||||
|
'configuration': configuration}
|
||||||
|
|
||||||
|
@register('v1.config.configuration.server.deploy', 'v1.config.configuration.server.updated')
|
||||||
|
async def deploy_configuration(self,
|
||||||
|
risotto_context: Context,
|
||||||
|
server_name: str) -> Dict:
|
||||||
|
"""Copy values, permissions, permissives from config 'to deploy' to active config
|
||||||
|
"""
|
||||||
|
server = await self.call('v1.server.describe',
|
||||||
|
risotto_context,
|
||||||
|
server_name=server_name)
|
||||||
|
server_id = server['server_id']
|
||||||
|
# FIXME is server_to_deploy working?
|
||||||
|
config = self.server[server_id]['server']
|
||||||
|
config_std = self.server[server_id]['server_to_deploy']
|
||||||
|
|
||||||
|
# when deploy, calculate force_store_value
|
||||||
|
ro = await config_std.property.getdefault('read_only', 'append')
|
||||||
|
if 'force_store_value' not in ro:
|
||||||
|
ro = frozenset(list(ro) + ['force_store_value'])
|
||||||
|
await config_std.property.setdefault(ro, 'read_only', 'append')
|
||||||
|
rw = await config_std.property.getdefault('read_write', 'append')
|
||||||
|
rw = frozenset(list(rw) + ['force_store_value'])
|
||||||
|
await config_std.property.setdefault(rw, 'read_write', 'append')
|
||||||
|
await config_std.property.add('force_store_value')
|
||||||
|
|
||||||
|
# copy informations from server 'to deploy' configuration to server configuration
|
||||||
|
await config.value.importation(await config_std.value.exportation())
|
||||||
|
await config.permissive.importation(await config_std.permissive.exportation())
|
||||||
|
await config.property.importation(await config_std.property.exportation())
|
||||||
|
|
||||||
|
return {'server_id': server_id,
|
||||||
|
'server_name': server_name,
|
||||||
|
'deployed': True}
|
||||||
|
1
src/risotto/services/server/__init__.py
Normal file
1
src/risotto/services/server/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .server import Risotto
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user