Compare commits

..

No commits in common. "dist/risotto/risotto-2.8.0/develop" and "master" have entirely different histories.

17 changed files with 216 additions and 723 deletions

5
debian/changelog vendored Normal file
View File

@ -0,0 +1,5 @@
cucchiaiata (0.1) unstable; urgency=low
* first version
-- Cadoles <contact@cadoles.com> Tue, 07 Apr 2020 15:25:29 +0200

10
debian/control vendored
View File

@ -2,19 +2,13 @@ Source: cucchiaiata
Section: admin Section: admin
Priority: extra Priority: extra
Maintainer: Cadoles <contact@cadoles.com> Maintainer: Cadoles <contact@cadoles.com>
Build-depends: debhelper (>=11), python3-all, python3-setuptools, dh-python Build-depends: debhelper (>=11), python3-all, python3-setuptools
Standards-Version: 3.9.4 Standards-Version: 3.9.4
Homepage: https://forge.cadoles.com/Infra/risotto Homepage: https://forge.cadoles.com/Infra/risotto
Package: python3-cucchiaiata
Architecture: any
Pre-Depends: dpkg, python3, ${misc:Pre-Depends}
Depends: ${python:Depends}, ${misc:Depends}, python3-tiramisu-api, python3-tiramisu-cmdline-parser
Description: configuration manager cli
Package: cucchiaiata Package: cucchiaiata
Architecture: any Architecture: any
Pre-Depends: dpkg, python3, ${misc:Pre-Depends} Pre-Depends: dpkg, python3, ${misc:Pre-Depends}
Depends: ${python:Depends}, ${misc:Depends}, python3-cucchiaiata Depends: ${python:Depends}, ${misc:Depends}
Description: configuration manager cli Description: configuration manager cli

View File

@ -1 +0,0 @@
scripts/* usr/bin/

View File

@ -1,81 +0,0 @@
zones:
- name: internet
settings:
configuration:
network: 192.168.0.0/24
host:
start: 192.168.0.150
stop: 192.168.0.200
dns: [192.168.0.1]
vlan_id: 0
gateway: 192.168.0.1
clusters:
- name: hapy.ac-test.fr
zone: internet
zones: [internet]
settings:
configuration:
virtual_ip: 192.168.1.1
opennebula:
one_user: eoleone
one_password: eole
nodes:
- name: node1.cadoles.com
servermodels:
- name: unbound
applicationservices: [unbound]
settings:
configuration:
serveur_dns:
unbound_local_zones: cadoles.com
unbound_allowed_client_cidr: 192.168.1.0/24
reseau:
unbound_route_address: 192.168.1.254
- name: aca
applicationservices: [openssh-server]
settings:
configuration:
ssh_server:
ssh_allow_cidr: 192.168.0.0/24
children:
- name: etb1
children:
- name: unbound_etab1
other_parents: [unbound]
settings:
configuration:
unbound:
unbound_zone_cadoles_com:
hostname_cadoles_com:
hostname_cadoles_com:
- toto
- titi
ip_cadoles_com:
- index: 0
value: 192.168.1.25
type_cadoles_com:
- index: 1
value: CNAME
cname_cadoles_com:
- index: 1
value: toto.cadoles.com
organizations:
- name: cadoles.com
sites:
- name: etab1
zones: [internet]
servers:
- name: dns.cadoles.com
cluster: hapy.ac-test.fr
zones: [internet]
servermodel: unbound_etab1
settings:
opennebula:
cpu: 0.2
vcpu: 2
memory: 2048
- name: etab2
zones: [internet]

View File

@ -1,170 +1,135 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#
# + = = = = = = = = = = = = = = = = = = = = = = = = = = = +
# " SITE cluster "
# " +---------------------+ "
# " | NEBULACLUSTER | "
# " | cluster.cadoles.com | "
# " +---------------------+ "
# " | "
# " +-------------------+ "
# " | NODE | "-----------+
# " | node1.cadoles.com | " |
# " +-------------------+ " |
# " | " +~~~~~~~~+
# + = = = = = = = = = = = = = | = = = = = = = = = = = = = + ! ZONE !
# | ! pedago !
# + = = = = = = = = = = = = = | = = = = = = = = = = = = = + +~~~~~~~~+
# " SITE etab1 | " |
# " +-----------------+ " |
# " | VM DNS | "-----------+
# " | dns.cadoles.com | "
# " +-----------------+ "
# " "
# + = = = = = = = = = = = = = = = = = = = = = = = = = = = +
#
set -xe set -xe
function get_id() { verif() {
S=$($1) i=0
V=$(echo $S|jq -r .session_id) argv=("$@")
if [ $? = 0 ]; then for V in creole.reseau.unbound_route_address creole.reseau.unbound_domain_name creole.serveur_dns.unbound_local_zones creole.reseau.unbound_ip_address_cidr; do
echo $V i=$((i+1))
else R=$(cucchiaiata-cli setting.session.$1.get -s $S -n $V | jq -Mcr .content[])
echo $S [ "$R" = "${argv[i]}" ]
exit 1 done
fi
} }
#======================================================================================================= # Import EOLE
# Infrastructure cucchiaiata-cli setting.source.create -o cadoles -u http://localhost
#======================================================================================================= cucchiaiata-cli setting.source.release.create -o cadoles -r 2020.1.1 -e last
cucchiaiata-cli setting.applicationservice.dataset.updated -o cadoles -e last
cucchiaiata-cli setting.servermodel.dataset.updated -o cadoles -e last
# Create a new user and set role 'server_rw' for the server dns.cadoles.com # Create a new user and set role 'server_rw' for this server
cucchiaiata-cli v1.user.user.create --login gnunux \ cucchiaiata-cli user.user.create -u gnunux -n gnunux -s gnunux
--name gnunux \ cucchiaiata-cli user.role.create -u gnunux -o 'server_rw' -a 'Server.ServerName' -v dns.cadoles.com
--surname gnunux \
--email egarette@cadoles.com
cucchiaiata-cli v1.user.role.create --user_login gnunux \
--role_name 'server_rw' \
--role_attribute 'Server.ServerName' \
--role_attribute_value dns.cadoles.com
# Zone # A servermodel for node in cluster
# +~~~~~~~~~~+ cucchiaiata-cli setting.servermodel.create -m cluster -d "Node in the cluster" -p base -o cadoles -e last
# ! ZONE !
# ! internet !
# +~~~~~~~~~~+
cucchiaiata-cli v1.infra.zone.create --zone_name internet
# zone configuration
S=$(get_id "cucchiaiata-cli v1.setting.session.zone.start --zone_name internet")
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.network "192.168.0.0/24" \
--configuration.host.start "192.168.0.150" \
--configuration.host.stop "192.168.0.200" \
--configuration.dns 192.168.0.1 \
--configuration.vlan_id 0 \
--configuration.gateway 192.168.0.1
cucchiaiata-cli v1.setting.session.validate --session_id $S
cucchiaiata-cli v1.setting.session.stop --session_id "$S" --save
# Heritage
## ACA
cucchiaiata-cli setting.servermodel.create -m aca -d Aca -p base -o cadoles -e last
cucchiaiata-cli setting.servermodel.dependency.add -m aca -a unbound -o cadoles -e last
S=$(cucchiaiata-cli setting.session.servermodel.start -m aca -e last| jq -r .session_id)
verif servermodel null null [] null
cucchiaiata-cli setting.session.servermodel.configure -s $S --creole.reseau.unbound_route_address 192.168.1.2
cucchiaiata-cli setting.session.servermodel.configure -s $S --creole.serveur_dns.unbound_allowed_client_cidr 192.168.1.0/24
verif servermodel 192.168.1.2 null [] null
cucchiaiata-cli setting.session.servermodel.stop -s $S -a
# Create the cluster hapy.ac-test.fr ## etab1
# +---------------------------+ cucchiaiata-cli setting.servermodel.create -m etab1 -d "Etab 1" -p aca -o internal -e last
# | hapy.ac-test.fr | +~~~~~~~~~~+ S=$(cucchiaiata-cli setting.session.servermodel.start -m etab1 -e last| jq -r .session_id)
# | +--------------------+ |-------! ZONE ! cucchiaiata-cli setting.session.servermodel.configure -s $S --creole.reseau.unbound_domain_name dns.cadoles.com
# | | node1.cadoles.com |---|-------! internet ! verif servermodel 192.168.1.2 dns.cadoles.com [] null
# | +--------------------+ | +~~~~~~~~~~+ cucchiaiata-cli setting.session.servermodel.stop -s $S -a
# +---------------------------+
cucchiaiata-cli v1.infra.cluster.create --cluster_name hapy.ac-test.fr \
--zone_name internet \
--zones_name internet
cucchiaiata-cli v1.infra.cluster.node.create --node_name node1.cadoles.com \
--cluster_name hapy.ac-test.fr
# configuration ## unbound
S=$(get_id "cucchiaiata-cli v1.setting.session.cluster.start --cluster_name hapy.ac-test.fr") cucchiaiata-cli setting.servermodel.create -m unbound -d "generic unbound configuration" -p fedora-31 -o cadoles -e last
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.virtual_ip "192.168.0.115" cucchiaiata-cli setting.servermodel.dependency.add -m unbound -a unbound -o cadoles -e last
# FIXME endpoint == https://<cluster_name>/RPC2 S=$(cucchiaiata-cli setting.session.servermodel.start -m unbound -e last| jq -r .session_id)
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.opennebula.one_user "eoleone" cucchiaiata-cli setting.session.servermodel.configure -s $S --creole.serveur_dns.unbound_local_zones cadoles.com
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.opennebula.one_password "eole" cucchiaiata-cli setting.session.servermodel.filter -s $S -n unbound
cucchiaiata-cli v1.setting.session.validate --session_id $S cucchiaiata-cli setting.session.servermodel.configure -s $S --unbound.unbound_zone_cadoles_com.hostname_cadoles_com.hostname_cadoles_com toto titi
cucchiaiata-cli v1.setting.session.stop --session_id "$S" --save cucchiaiata-cli setting.session.servermodel.configure -s $S --unbound.unbound_zone_cadoles_com.hostname_cadoles_com.ip_cadoles_com 0 192.168.1.25
cucchiaiata-cli setting.session.servermodel.configure -s $S --unbound.unbound_zone_cadoles_com.hostname_cadoles_com.type_cadoles_com 1 CNAME
cucchiaiata-cli setting.session.servermodel.configure -s $S --unbound.unbound_zone_cadoles_com.hostname_cadoles_com.cname_cadoles_com 1 toto.cadoles.com
cucchiaiata-cli setting.session.servermodel.filter -s $S -n creole
verif servermodel null null '["cadoles.com"]' null
cucchiaiata-cli setting.session.servermodel.stop -s $S -a
# Organization, sites and zone ## unbound_etab1
# + = = = = = = = = = = = = = = = = = = = = + cucchiaiata-cli setting.servermodel.create -m unbound_etab1 -d "unbound configuration for etab1" -p etab1 unbound -o internal -e last
# " ORGANISATION cadoles.com " S=$(cucchiaiata-cli setting.session.servermodel.start -m unbound_etab1 -e last | jq -r .session_id)
# " + = = = = = = = = = = = + " verif servermodel 192.168.1.2 dns.cadoles.com '["cadoles.com"]' null
# " " SITE etab1 "----"----+ +~~~~~~~~~~+
# " + = = = = = = = = = = = + " |___! ZONE !
# " + = = = = = = = = = = = + " | ! internet !
# " " SITE etab2 "----"----+ +~~~~~~~~~~+
# " + = = = = = = = = = = = + "
# + = = = = = = = = = = = = = = = = = = = = +
cucchiaiata-cli v1.infra.organization.create --organization_name cadoles.com
cucchiaiata-cli v1.infra.site.create --site_name etab1 \
--organization_name cadoles.com \
--zones_name internet
cucchiaiata-cli v1.infra.site.create --site_name etab2 \
--organization_name cadoles.com \
--zones_name internet
# Servermodels # Create a site, a zone and servers
# -> base-fedora-32 (Cadoles) cucchiaiata-cli infra.site.create -i etab1 -d "Etab 1"
# '-> unbound (Cadoles) cucchiaiata-cli infra.site.create -i cluster -d "Cluster"
# '-> unbound (servermodel) ----, cucchiaiata-cli infra.zone.create -z pedago -d "Pedago" -s etab1 cluster -n "192.168.1.0/24" -t "192.168.1.10" -e "192.168.1.100" -q 192.168.1.1 -g 192.168.1.254
# |
# -> openssh-server (Cadoles) |--> unbound_etab1 (servermodel)
# '-> aca (servermodel) |
# '-> etab1 (servermodel) ------'
cucchiaiata-cli v1.setting.servermodel.create --servermodel_name unbound --applicationservices_name unbound
cucchiaiata-cli v1.setting.servermodel.create --servermodel_name aca --applicationservices_name openssh-server
cucchiaiata-cli v1.setting.servermodel.create --servermodel_name etab1 --parents_name aca
cucchiaiata-cli v1.setting.servermodel.create --servermodel_name unbound_etab1 --parents_name etab1 unbound
cucchiaiata-cli v1.setting.servermodel.create --servermodel_name nsd_etab1 --parents_name etab1 --applicationservices_name nsd
# configuration cucchiaiata-cli infra.server.create -s node1.cadoles.com -d node1 -m cluster -e last -i cluster -z pedago
cucchiaiata-cli infra.server.create -s dns.cadoles.com -d description -m unbound_etab1 -e last -i etab1 -z pedago
# Servermodel ACA # Create a cluster with one node and one VM
S=$(get_id "cucchiaiata-cli v1.setting.session.servermodel.start --servermodel_name aca") cucchiaiata-cli provider.factory.cluster.create -c cluster.cadoles.com -d toto -v 192.168.1.1 -z pedago
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.ssh_server.ssh_allow_cidr 192.168.0.0/24 cucchiaiata-cli provider.factory.cluster.join -c cluster.cadoles.com -n node1.cadoles.com
cucchiaiata-cli v1.setting.session.stop --session_id "$S" --save cucchiaiata-cli provider.factory.server.add -s dns.cadoles.com -c cluster.cadoles.com -p 0.2 -v 2 -m 2
# Servermodel unbound # Configure server
S=$(get_id "cucchiaiata-cli v1.setting.session.servermodel.start --servermodel_name unbound") S=$(cucchiaiata-cli setting.session.server.start -s dns.cadoles.com | jq -r .session_id)
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.dns_resolver.unbound_allowed_client_cidr 192.168.0.0/24 cucchiaiata-cli setting.session.server.configure -s $S --creole.reseau.unbound_ip_address_cidr 192.168.1.1/24
cucchiaiata-cli v1.setting.session.stop --session_id "$S" --save cucchiaiata-cli setting.session.server.validate -s $S
verif server 192.168.1.2 dns.cadoles.com '["cadoles.com"]' 192.168.1.1/24
# Servermodel unbound_etab1 cucchiaiata-cli setting.session.server.stop -s $S -a
S=$(get_id "cucchiaiata-cli v1.setting.session.servermodel.start --servermodel_name unbound_etab1")
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.dns_resolver.unbound_forward_zones.unbound_forward_zones cadoles.com \
--configuration.dns_resolver.unbound_forward_zones.unbound_forward_addresses 0 192.168.0.151
cucchiaiata-cli v1.setting.session.stop --session_id "$S" --save
# Server
# + = = = = = = = = = = = = = = = = = = = = = = = +
# " SITE etab1 +-------------------+ " +~~~~~~~~~~+
# " | dns.cadoles.com |----"------! ZONE !
# " +-------------------+ " ! internet !
# " " +~~~~~~~~~~+
# + = = = = = = = = = = = = = = = = = = = = = = = +
# -> unbound_etab1
# '-> dns.cadoles.com (server)
cucchiaiata-cli v1.infra.server.create --server_name dns.cadoles.com \
--site_name etab1 \
--cluster_name hapy.ac-test.fr \
--zones_name internet \
--servermodel_name unbound_etab1
cucchiaiata-cli v1.infra.server.create --server_name nsd.cadoles.com \
--site_name etab1 \
--cluster_name hapy.ac-test.fr \
--zones_name internet \
--servermodel_name nsd_etab1
# configuration
sleep 1
S=$(get_id "cucchiaiata-cli v1.setting.session.server.start --server nsd.cadoles.com")
#FIXME IP en dure ?
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.dns_server.nsd_allowed_client_cidr 192.168.0.150/32 \
--configuration.dns_zone.nsd_zones cadoles.com
cucchiaiata-cli v1.setting.session.filter --session_id "$S" --namespace nsd
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --nsd.nsd_zone_cadoles_com.hostname_cadoles_com.hostname_cadoles_com toto titi \
--nsd.nsd_zone_cadoles_com.hostname_cadoles_com.ip_cadoles_com 0 192.168.0.25 \
--nsd.nsd_zone_cadoles_com.hostname_cadoles_com.type_cadoles_com 1 CNAME \
--nsd.nsd_zone_cadoles_com.hostname_cadoles_com.cname_cadoles_com 1 toto.cadoles.com
cucchiaiata-cli v1.setting.session.validate --session_id $S
cucchiaiata-cli v1.setting.session.stop --session_id "$S" --save
S=$(get_id "cucchiaiata-cli v1.setting.session.server.start --server dns.cadoles.com")
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.dns_resolver.unbound_default_forwards 192.168.0.1
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --configuration.dns_resolver.unbound_local_authority "nsd.cadoles.com"
cucchiaiata-cli v1.setting.session.filter --session_id "$S" --namespace opennebula
cucchiaiata-cli v1.setting.session.configure --session_id "$S" --opennebula.cpu 0.2 \
--opennebula.vcpu 2
cucchiaiata-cli v1.setting.session.validate --session_id $S
cucchiaiata-cli v1.setting.session.stop --session_id "$S" --save
#=======================================================================================================
# Deploy
#=======================================================================================================
# Generate configuration # Generate configuration
cucchiaiata-cli v1.infra.cluster.deploy --cluster_name hapy.ac-test.fr cucchiaiata-cli setting.config.configuration.server.deploy -s dns.cadoles.com
cucchiaiata-cli v1.infra.zone.deploy --zone_name internet cucchiaiata-cli setting.template.generate -s dns.cadoles.com
cucchiaiata-cli v1.infra.server.deploy --server_name nsd.cadoles.com
cucchiaiata-cli v1.infra.server.deploy --server_name dns.cadoles.com # Add OpenSSH dependency
cucchiaiata-cli setting.servermodel.dependency.add -m aca -a openssh -o cadoles -e last
S=$(cucchiaiata-cli setting.session.server.start -s dns.cadoles.com | jq -r .session_id)
R=$(cucchiaiata-cli setting.session.server.get -s $S -n creole.serveur_ssh.ssh_allow_networks | jq -Mcr .content[])
[ "$R" = "[]" ]
cucchiaiata-cli setting.session.server.stop -s $S
S=$(cucchiaiata-cli setting.session.servermodel.start -m aca -e last| jq -r .session_id)
cucchiaiata-cli setting.session.servermodel.configure -s $S --creole.serveur_ssh.ssh_allow_networks admin.cadoles.com
cucchiaiata-cli setting.session.servermodel.stop -s $S -a
S=$(cucchiaiata-cli setting.session.server.start -s dns.cadoles.com | jq -r .session_id)
R=$(cucchiaiata-cli setting.session.server.get -s $S -n creole.serveur_ssh.ssh_allow_networks | jq -Mcr .content[])
[ "$R" = '["admin.cadoles.com"]' ]
cucchiaiata-cli setting.session.server.stop -s $S
# Regenerate configuration
cucchiaiata-cli setting.config.configuration.server.deploy -s dns.cadoles.com
cucchiaiata-cli setting.template.generate -s dns.cadoles.com
# Generate cluster's configurtion
cucchiaiata-cli provider.factory.configure -c cluster.cadoles.com
echo "fin" echo "fin"

31
script/cucchiaiata-cli Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/python3
"""Zephir-cmd-input script
"""
from sys import exit, argv
from json import dumps
from traceback import print_exc
from cucchiaiata import Parser, config, Configuration
from cucchiaiata.i18n import _
def main():
try:
if len(argv) > 2 and argv[1] in ['setting.session.server.configure',
'setting.session.servermodel.configure']:
Configuration().get()
else:
parser = Parser()
print(dumps(parser.get(),
indent=config.indent))
except Exception as err:
if config.debug:
print_exc()
print(_('Error: {}').format(err))
exit(1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass

View File

@ -1,48 +0,0 @@
#!/usr/bin/python3
"""Zephir-cmd-input script
"""
from os import environ
from sys import exit, argv
from traceback import print_exc
from json import dumps
from cucchiaiata import Parser, config, Configuration, JsonError
from cucchiaiata.i18n import _
from cucchiaiata.output.interactive import get as interactive_get
from cucchiaiata.output.json import get as json_get
def main():
dico = {'interactive': interactive_get,
'json': json_get,
}
default_outputs = ','.join(dico.keys())
outputs = [dico[output] for output in environ.get('RISOTTO_OUTPUT', default_outputs).split(',')]
try:
if len(argv) > 2 and argv[1] == 'v1.setting.session.configure':
Configuration().get()
else:
parser = Parser()
message = parser.remote_config.option('message').value.get()
for output in outputs:
func = output(message)
if func:
func(parser.get(),
config,
)
break
except KeyboardInterrupt:
pass
except JsonError as err:
print(dumps(err.message,
indent=config.indent),
)
exit(1)
except Exception as err:
if config.debug:
print_exc()
print(_('Error: {}').format(err))
exit(1)
if __name__ == "__main__":
main()

View File

@ -1,232 +0,0 @@
#!/usr/bin/python3
"""Zephir-cmd-input script
"""
from sys import exit, argv
from yaml import load, SafeLoader, YAMLError
from time import sleep
from os.path import isfile
from json import dumps
from cucchiaiata import Configuration, JsonError, config
from cucchiaiata.common import Common
from cucchiaiata.i18n import _
class Import(Common):
def __init__(self,
config_file: str,
) -> None:
super().__init__()
with open(config_file, 'r') as stream:
try:
self.config = load(stream,
Loader=SafeLoader,
)
except YAMLError as err:
raise Exception(_('unable to lead the YAML file {}').format(err))
self.remote_config = self.remote_json_to_config(self.cucchiaiata_config.remote_url)
def parse_zones(self) -> None:
for zone in self.config.get('zones', []):
self.configuration('infra',
'zone',
zone,
)
self.send('v1.infra.zone.deploy',
zone_name=zone['name'],
)
def parse_clusters(self) -> None:
for cluster in self.config.get('clusters', []):
self.configuration('infra',
'cluster',
cluster,
zone_name=cluster['zone'],
zones_name=cluster['zones'],
)
for node in cluster.get('nodes', []):
self.configuration('infra',
'cluster.node',
node,
cluster_name=cluster['name'],
)
self.send('v1.infra.cluster.deploy',
cluster_name=cluster['name'],
)
def parse_servermodels(self) -> None:
for servermodel in self.config.get('servermodels', []):
self.add_servermodel(servermodel)
def parse_organizations(self) -> None:
for organization in self.config.get('organizations', []):
self.configuration('infra',
'organization',
organization,
)
for site in organization.get('sites', []):
self.configuration('infra',
'site',
site,
organization_name=organization['name'],
zones_name=site['zones'],
)
for server in site.get('servers', []):
self.configuration('infra',
'server',
server,
site_name=site['name'],
cluster_name=server['cluster'],
zones_name=server['zones'],
servermodel_name=server['servermodel'],
)
self.send('v1.infra.server.deploy',
server_name=server['name'],
)
def configuration(self,
domain: str,
element: str,
dico: dict,
**kwargs,
) -> None:
self.upset_element(domain,
element,
dico['name'],
kwargs,
)
if 'settings' in dico:
#FIXME
sleep(1)
try:
self.apply_settings(element,
dico,
)
except JsonError as err:
raise Exception(f'unable to configure {element} "{dico["name"]}": {err.message["reason"]}')
def apply_settings(self,
element: str,
dico: dict,
) -> None:
session_id = self.send(f'v1.setting.session.{element}.start',
**{f'{element}_name': dico['name']},
)['session_id']
config = Configuration()
config.message = 'v1.setting.session.configure'
config.session_id = session_id
tiramisu = None
tiramisu_namespace = None
for key, value in self.settings_paths(dico['settings']):
current_namespace = key.split('.', 1)[0]
if tiramisu_namespace != current_namespace:
if tiramisu is not None:
self.send_configuration(tiramisu,
session_id,
)
tiramisu_namespace = current_namespace
self.send('v1.setting.session.filter',
session_id=session_id,
namespace=tiramisu_namespace,
)
tiramisu = config.configure_server()
try:
if tiramisu.option(key).option.isfollower():
for val in value:
tiramisu.option(key, val['index']).value.set(val['value'])
else:
if tiramisu.option(key).option.ismulti() and \
not isinstance(value, list):
value = [value]
tiramisu.option(key).value.set(value)
except ValueError as err:
print(_(f'error when setting "{element}" "{dico["name"]}": "{key}" with value "{value}": {err}'))
exit(1)
except Exception as err:
print(_(f'unexpected error when setting "{element}" "{dico["name"]}": "{key}" with value "{value}": {err}'))
exit(1)
self.send_configuration(tiramisu,
session_id,
)
self.send('v1.setting.session.stop',
session_id=session_id,
save=True,
)
def upset_element(self,
domain: str,
element: str,
name: str,
kwargs: dict,
) -> None:
message_name = element
if '.' in message_name:
message_name = message_name.rsplit('.', 1)[-1]
try:
self.send(f'v1.{domain}.{element}.describe',
**{f'{message_name}_name': name},
)
except:
# not exists
print(f'add "{element}" "{name}"')
kwargs[f'{message_name}_name'] = name
self.send(f'v1.{domain}.{element}.create',
**kwargs,
)
def settings_paths(self,
dico: dict,
subpath: str=None,
) -> list:
ret = []
for key, value in dico.items():
if subpath:
key_path = f'{subpath}.{key}'
else:
key_path = key
if isinstance(value, dict):
ret.extend(self.settings_paths(value, key_path))
else:
ret.append((key_path, value))
return ret
def add_servermodel(self,
servermodel: dict,
parents: list=[],
) -> None:
if 'other_parents' in servermodel:
parents = parents.copy()
parents.extend(servermodel['other_parents'])
self.configuration('setting',
'servermodel',
servermodel,
parents_name=parents,
applicationservices_name=servermodel.get('applicationservices', []),
)
if 'children' in servermodel:
child_parents = parents.copy()
child_parents.append(servermodel['name'])
for child in servermodel['children']:
self.add_servermodel(child,
child_parents,
)
if __name__ == "__main__":
if len(argv) != 2 or not isfile(argv[1]):
print(_(f'usage: {argv[0]} filename.yaml'))
exit(1)
imp = Import(argv[1])
try:
imp.parse_zones()
imp.parse_clusters()
imp.parse_servermodels()
imp.parse_organizations()
except JsonError as err:
print(err.message['reason'])
exit(1)
except Exception as err:
print(err)
exit(1)

View File

@ -3,6 +3,7 @@ from setuptools import setup, find_packages
setup( setup(
name='cucchiaiata', name='cucchiaiata',
version='0.1', version='0.1',
packages=['cucchiaiata', 'cucchiaiata.output'], packages=['cucchiaiata' ],
scripts=['script/cucchiaiata-cli'],
package_dir={"": "src"}, package_dir={"": "src"},
) )

View File

@ -1,7 +1,6 @@
from .parser import Parser from .parser import Parser
from .configuration import Configuration from .configuration import Configuration
from .config import config from .config import config
from .common import JsonError
__all__ = ('Parser', 'config', 'Configuration', 'JsonError') __all__ = ('Parser', 'config')
__version__ = "0.0.1" __version__ = "0.0.1"

View File

@ -1,13 +1,10 @@
from os import environ
from os.path import isfile from os.path import isfile
from requests import get, post from requests import get, post
from json import dumps from json import dumps
from typing import Dict from typing import Dict
from tiramisu_api import Config
from .config import config from .config import config
from .i18n import _ from tiramisu_api import Config
if config.allow_insecure_https: if config.allow_insecure_https:
@ -16,14 +13,15 @@ if config.allow_insecure_https:
warnings.simplefilter('ignore', InsecureRequestWarning) warnings.simplefilter('ignore', InsecureRequestWarning)
class JsonError(Exception):
pass
class Common: class Common:
def __init__(self): def __init__(self):
self.cucchiaiata_config = config self.cucchiaiata_config = config
def get_token(self):
if isfile(self.cucchiaiata_config.token_file):
return open(self.cucchiaiata_config.token_file).read()
return ''
def get_error_from_http(self, def get_error_from_http(self,
req): req):
try: try:
@ -34,87 +32,34 @@ class Common:
return err return err
def remote_json_to_config(self, def remote_json_to_config(self,
url, url=None,
config_type=Config, config_type=Config):
):
"retrieves the remote config from the distant api description" "retrieves the remote config from the distant api description"
headers = get_headers() if url is None:
url = self.cucchiaiata_config.remote_url
token = self.get_token()
headers = {'Authorization':'Bearer {}'.format(token)}
req = get(url, req = get(url,
headers=headers, headers=headers,
verify=config.allow_insecure_https, verify=config.allow_insecure_https)
)
code = req.status_code code = req.status_code
if code != 200: if code != 200:
raise Exception(self.get_error_from_http(req)) raise Exception(self.get_error_from_http(req))
json = req.json() json = req.json()
return config_type(json) return config_type(json)
def get_payload(self,
message: str):
# remove symlinkoption and default value from payload
payload = {}
for option in self.remote_config.option(message).list():
if not option.owner.isdefault() and not option.option.issymlinkoption():
payload[option.option.name()] = option.value.get()
return payload
def send(self, def send_data(message: str,
message: str, payload: Dict):
**kwargs, final_url = '{}/{}'.format(config.remote_url, message)
) -> None:
self.remote_config.option('message').value.set(message)
for key, value in kwargs.items():
self.remote_config.option(f'{message}.{key}').value.set(value)
payload = self.get_payload(message)
return send_data(message,
payload,
)
def send_configuration(self,
tiramisu: 'ConfigAPI',
session_id: str,
) -> None:
tiramisu.send()
try:
self.send('v1.setting.session.validate',
session_id=session_id,
)
except JsonError as err:
self.send('v1.setting.session.stop',
session_id=session_id,
)
raise err from err
def get_headers():
headers = {}
if isfile(config.token_file):
with open(config.token_file) as token_file:
token = token_file.read()
headers['Authorization'] = f'Bearer {token}'
if 'FORCE_RISOTTO_USER' in environ:
headers['username'] = environ['FORCE_RISOTTO_USER']
return headers
def send_data(uri: str,
payload: Dict,
):
version, message = uri.split('.', 1)
final_url = '{}/{}/{}'.format(config.remote_url,
version,
message,
)
ret = post(final_url, ret = post(final_url,
data=dumps(payload), data=dumps(payload),
headers=get_headers(),
verify=config.allow_insecure_https) verify=config.allow_insecure_https)
try: if ret.status_code != 200:
response = ret.json()
except:
raise Exception(ret.text) raise Exception(ret.text)
if response['type'] == 'error': response = ret.json()
err = JsonError() if 'error' in response:
err.message = response['response'] if 'reason' in response['error']['kwargs']:
raise err raise Exception("{}".format(response['error']['kwargs']['reason']))
raise Exception('erreur inconnue')
return response['response'] return response['response']

View File

@ -11,7 +11,11 @@ class Config:
if not isfile(config_file): if not isfile(config_file):
print(_('Attention, there is no configuration file')) print(_('Attention, there is no configuration file'))
url = input(_('Address to Risotto server: ')) url = input(_('Address to Risotto server: '))
yaml_template = f'url: {url}' version = input(_('Risotto API\'s version (default: "v1"): '))
if not version:
version = "v1"
yaml_template = f"""url: {url}
version: {version}"""
with open(config_file, 'w') as fh: with open(config_file, 'w') as fh:
fh.write(yaml_template) fh.write(yaml_template)
@ -24,8 +28,9 @@ class Config:
raise Exception(_('Error when creating the config file {}').format(err)) raise Exception(_('Error when creating the config file {}').format(err))
self.url = config['url'] self.url = config['url']
self.version = config['version']
self.debug = config.get('debug', False) self.debug = config.get('debug', False)
self.remote_url = f'http://{self.url}/api' self.remote_url = 'http://{}/api/{}'.format(self.url, self.version)
self.token_file = join(expanduser("~"), '.zephir-client.jwt.token') self.token_file = join(expanduser("~"), '.zephir-client.jwt.token')
self.indent = config.get('indent', 2) self.indent = config.get('indent', 2)
self.allow_insecure_https = config.get('allow_insecure_https', False) self.allow_insecure_https = config.get('allow_insecure_https', False)

View File

@ -3,16 +3,16 @@ from sys import argv, exit
from tiramisu_cmdline_parser import TiramisuCmdlineParser from tiramisu_cmdline_parser import TiramisuCmdlineParser
from tiramisu_api import Config from tiramisu_api import Config
from cucchiaiata.i18n import _
from .i18n import _
from .parser import Parser
from .common import send_data, Common from .common import send_data, Common
class ConfigAPI(Config): class ConfigAPI(Config):
def send_data(self, def send_data(self,
data): data):
for index, payload in enumerate(data['updates']): for index, payload in enumerate(data['updates']):
payload['session_id'] = self.session_id payload['session_id'] = self.session_id
if isinstance(payload['value'], list): if isinstance(payload['value'], list):
payload['value_multi'] = payload['value'] payload['value_multi'] = payload['value']
@ -25,14 +25,15 @@ class ConfigAPI(Config):
class Configuration(Common): class Configuration(Common):
def configure_server(self): def configure_server(self):
version = self.message.split('.', 1)[0] if self.message == 'setting.session.server.configure':
url = '{}/{}/setting/{}'.format(self.cucchiaiata_config.remote_url, type = 'server'
version, else:
self.session_id, type = 'servermodel'
) url = '{}/setting/{}/{}'.format(self.cucchiaiata_config.remote_url,
type,
self.session_id)
tconfig = self.remote_json_to_config(url, tconfig = self.remote_json_to_config(url,
ConfigAPI, ConfigAPI)
)
tconfig.message = self.message tconfig.message = self.message
tconfig.session_id = self.session_id tconfig.session_id = self.session_id
return tconfig return tconfig
@ -51,9 +52,14 @@ class Configuration(Common):
index = parameters.index('-s') index = parameters.index('-s')
except ValueError: except ValueError:
try: try:
index = parameters.index('--session_id') index = parameters.index('--sessionid')
except ValueError: except ValueError:
parser = Parser() # FIXME not working ...
tiramisu_config = self.remote_json_to_config(ConfigAPI)
parser = TiramisuCmdlineParser(tiramisu_config,
self.prog,
unrestraint=True,
fullpath=True)
parser.print_help() parser.print_help()
exit(1) exit(1)
parameters.pop(index) parameters.pop(index)
@ -63,11 +69,8 @@ class Configuration(Common):
def get(self): def get(self):
parameters = self.get_parameters() parameters = self.get_parameters()
tiramisu_config = self.configure_server() tiramisu_config = self.configure_server()
option = next(tiramisu_config.option.list('all'))
namespace = option.option.path().split('.', 1)[0]
parser = TiramisuCmdlineParser(tiramisu_config, parser = TiramisuCmdlineParser(tiramisu_config,
self.prog, self.prog,
root=namespace,
unrestraint=True, unrestraint=True,
fullpath=True) fullpath=True)
parser.parse_args(parameters, parser.parse_args(parameters,

View File

@ -1,90 +0,0 @@
from paramiko.config import SSHConfig
from os.path import expandvars, isdir, isfile, join
from os import open as os_open, write, close, truncate, makedirs, O_WRONLY, O_CREAT
def setting_pki_openssh_client(dico, config):
config_dir = expandvars('$HOME/.ssh')
config_file = join(config_dir, 'config')
identityfile = join(expandvars('$HOME/.ssh'), f'risotto_{dico["organization_name"]}')
known_hosts = expandvars('$HOME/.ssh/known_hosts')
hostname = f'*.{dico["organization_name"]}'
new_data = {'identityfile': [identityfile],
'stricthostkeychecking': 'yes',
'hostname': hostname,
'user': dico['cn'],
}
ssh = SSHConfig()
if isfile(config_file):
ssh.parse(open(config_file))
if hostname not in ssh.get_hostnames():
print(f'\n\nIl faudrait ajouter dans le fichier "{config_file}" :')
print(f'Host {hostname}')
for key, value in new_data.items():
if key == 'hostname':
continue
print(f' {key} {value}')
print('\n')
else:
current_data = dict(ssh.lookup(hostname))
if current_data != new_data:
current = set(current_data)
new = set(new_data)
add = new - current
modify = [key for key in new if key in current and current_data[key] != new_data[key]]
if add or modify:
print(f'\n\nModifications suggérées de la section "Host {hostname}" du fichier "{config_file}" :')
for line in add:
value = new_data[line]
if isinstance(value, list):
value = ','.join(value)
print(f' - ajouter "{line} {value}"')
for line in modify:
value = new_data[line]
if isinstance(value, list):
value = ','.join(value)
print(f' - modifier "{line} {current_data[line]}" en "{line} {value}"')
print('\n')
else:
if not isdir(config_dir):
makedirs(config_dir, 0o700)
fh = os_open(config_file, O_WRONLY | O_CREAT, 0o400)
truncate(fh, 0)
write(fh, f'Host {hostname}\n'.encode())
for key, value in new_data.items():
if key == 'hostname':
continue
if isinstance(value, list):
value = ','.join(value)
write(fh, f' {key} {value}\n'.encode())
close(fh)
fh = os_open(f'{identityfile}.pub', O_WRONLY | O_CREAT, 0o400)
truncate(fh, 0)
write(fh, dico['certificate'].encode())
write(fh, b'\n')
close(fh)
if 'private_key' in dico:
fh = os_open(identityfile, O_WRONLY | O_CREAT, 0o400)
truncate(fh, 0)
write(fh, dico['private_key'].encode())
write(fh, b'\n')
close(fh, )
content = [f'@cert-authority *.cadoles.com {dico["chain"]}']
if isfile(known_hosts):
with open(known_hosts) as fh:
old = fh.read().strip()
for line in old.split('\n'):
if line.startswith(f'@cert-authority {hostname} '):
continue
content.append(line)
fh = os_open(known_hosts, O_WRONLY | O_CREAT, 0o400)
truncate(fh, 0)
for line in content:
write(fh, f'{line}\n'.encode())
close(fh)
print('Certificat mise à jour')
def get(message):
if message == 'v1.setting.pki.openssh.client.get':
return setting_pki_openssh_client

View File

@ -1,10 +0,0 @@
from json import dumps
def print_json(dico, config):
indent = config.indent
print(dumps(dico, indent = indent))
def get(message):
return print_json

View File

@ -19,14 +19,12 @@ class Parser(Common):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
# build a tiramisu parser and parse argument # build a tiramisu parser and parse argument
url = self.cucchiaiata_config.remote_url self.remote_config = self.remote_json_to_config()
self.remote_config = self.remote_json_to_config(url)
parser = CucchiaiataParser(self.remote_config, parser = CucchiaiataParser(self.remote_config,
fullpath=False, fullpath=False,
remove_empty_od=True, remove_empty_od=True,
display_modified_value=False, display_modified_value=False,
formatter_class=RawDescriptionHelpFormatter, formatter_class=RawDescriptionHelpFormatter)
)
parser.parse_args() parser.parse_args()
def get(self): def get(self):
@ -36,3 +34,12 @@ class Parser(Common):
# send message # send message
return send_data(message, return send_data(message,
payload) payload)
def get_payload(self,
message: str):
# remove symlinkoption and default value from payload
payload = {}
for option in self.remote_config.option(message).list():
if not option.owner.isdefault() and not option.option.issymlinkoption():
payload[option.option.name()] = option.value.get()
return payload