forked from stove/risotto
first commit
This commit is contained in:
parent
d8bb3528f8
commit
946506f27c
6 changed files with 1160 additions and 0 deletions
233
funcs.py
Normal file
233
funcs.py
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
from tiramisu import valid_network_netmask, valid_ip_netmask, valid_broadcast, valid_in_network, valid_not_equal as valid_differ, valid_not_equal, calc_value
|
||||||
|
from ipaddress import ip_address
|
||||||
|
from os.path import dirname, abspath, join as _join, isdir as _isdir, isfile as _isfile
|
||||||
|
from typing import List
|
||||||
|
from json import load
|
||||||
|
from secrets import token_urlsafe as _token_urlsafe
|
||||||
|
|
||||||
|
from rougail.utils import normalize_family
|
||||||
|
|
||||||
|
from utils import multi_function, CONFIGS
|
||||||
|
from x509 import gen_cert as _x509_gen_cert, gen_ca as _x509_gen_ca, gen_pub as _x509_gen_pub, has_pub as _x509_has_pub
|
||||||
|
# =============================================================
|
||||||
|
# fork of risotto-setting/src/risotto_setting/config/config.py
|
||||||
|
|
||||||
|
with open('servers.json', 'r') as server_fh:
|
||||||
|
ZONES_SERVER = load(server_fh)
|
||||||
|
|
||||||
|
|
||||||
|
ZONES = None
|
||||||
|
DOMAINS = None
|
||||||
|
HERE = dirname(abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def load_zones():
|
||||||
|
global ZONES
|
||||||
|
if ZONES is not None:
|
||||||
|
return
|
||||||
|
ZONES = ZONES_SERVER['zones']
|
||||||
|
for server_name, server in ZONES_SERVER['servers'].items():
|
||||||
|
if 'informations' not in server:
|
||||||
|
continue
|
||||||
|
server_zones = server['informations']['zones_name']
|
||||||
|
server_extra_domainnames = server['informations'].get('extra_domainnames', [])
|
||||||
|
if len(server_zones) > 1 and len(server_zones) != len(server_extra_domainnames) + 1:
|
||||||
|
raise Exception(f'the server "{server_name}" has more that one zone, please set correct number of extra_domainnames ({len(server_zones) - 1} instead of {len(server_extra_domainnames)})')
|
||||||
|
|
||||||
|
for idx, zone_name in enumerate(server_zones):
|
||||||
|
zone_domain_name = ZONES[zone_name]['domain_name']
|
||||||
|
if idx == 0:
|
||||||
|
zone_server_name = server_name
|
||||||
|
else:
|
||||||
|
zone_server_name = server_extra_domainnames[idx - 1]
|
||||||
|
server_domain_name = zone_server_name.split('.', 1)[1]
|
||||||
|
if zone_domain_name and zone_domain_name != server_domain_name:
|
||||||
|
raise Exception(f'wrong server_name "{zone_server_name}" in zone "{zone_name}" should ends with "{zone_domain_name}"')
|
||||||
|
ZONES[zone_name].setdefault('hosts', []).append(server_name)
|
||||||
|
|
||||||
|
|
||||||
|
def load_domains():
|
||||||
|
load_zones()
|
||||||
|
global DOMAINS
|
||||||
|
if DOMAINS is not None:
|
||||||
|
return
|
||||||
|
DOMAINS = {}
|
||||||
|
for zone_name, zone in ZONES_SERVER['zones'].items():
|
||||||
|
if 'domain_name' in zone:
|
||||||
|
hosts = []
|
||||||
|
ips = []
|
||||||
|
for host in ZONES[zone_name].get('hosts', []):
|
||||||
|
hosts.append(host.split('.', 1)[0])
|
||||||
|
ips.append(get_ip(host, [zone_name], 0))
|
||||||
|
DOMAINS[zone['domain_name']] = (tuple(hosts), tuple(ips))
|
||||||
|
|
||||||
|
|
||||||
|
def get_ip(server_name: str,
|
||||||
|
zones_name: List[str],
|
||||||
|
index: str,
|
||||||
|
) -> str:
|
||||||
|
if server_name is None:
|
||||||
|
return
|
||||||
|
load_zones()
|
||||||
|
index = int(index)
|
||||||
|
zone_name = zones_name[index]
|
||||||
|
if zone_name not in ZONES:
|
||||||
|
raise ValueError(f"cannot set IP in unknown zone '{zone_name}'")
|
||||||
|
zone = ZONES[zone_name]
|
||||||
|
if server_name not in zone['hosts']:
|
||||||
|
raise ValueError(f"cannot set IP in unknown server '{server_name}'")
|
||||||
|
server_index = zone['hosts'].index(server_name)
|
||||||
|
# print(server_name, zones_name, index, str(ip_address(zone['start_ip']) + server_index))
|
||||||
|
return str(ip_address(zone['start_ip']) + server_index)
|
||||||
|
|
||||||
|
|
||||||
|
@multi_function
|
||||||
|
def get_chain(authority_cn,
|
||||||
|
authority_name,
|
||||||
|
):
|
||||||
|
if not authority_name or authority_name is None:
|
||||||
|
if isinstance(authority_name, list):
|
||||||
|
return []
|
||||||
|
return
|
||||||
|
if not isinstance(authority_cn, list):
|
||||||
|
is_list = False
|
||||||
|
authority_cn = [authority_cn]
|
||||||
|
else:
|
||||||
|
is_list = True
|
||||||
|
authorities = []
|
||||||
|
|
||||||
|
for auth_cn in authority_cn:
|
||||||
|
ret = _x509_gen_ca(auth_cn,
|
||||||
|
authority_name,
|
||||||
|
HERE,
|
||||||
|
)
|
||||||
|
if not is_list:
|
||||||
|
return ret
|
||||||
|
authorities.append(ret)
|
||||||
|
return authorities
|
||||||
|
|
||||||
|
|
||||||
|
@multi_function
|
||||||
|
def get_certificate(cn,
|
||||||
|
authority_name,
|
||||||
|
authority_cn=None,
|
||||||
|
extra_domainnames=[],
|
||||||
|
type='server',
|
||||||
|
):
|
||||||
|
if isinstance(cn, list) and extra_domainnames:
|
||||||
|
raise Exception('cn cannot be a list with extra_domainnames set')
|
||||||
|
if not cn or authority_name is None:
|
||||||
|
if isinstance(cn, list):
|
||||||
|
return []
|
||||||
|
return
|
||||||
|
return _x509_gen_cert(cn,
|
||||||
|
extra_domainnames,
|
||||||
|
authority_cn,
|
||||||
|
authority_name,
|
||||||
|
type,
|
||||||
|
'crt',
|
||||||
|
HERE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@multi_function
|
||||||
|
def get_private_key(cn,
|
||||||
|
authority_name=None,
|
||||||
|
authority_cn=None,
|
||||||
|
type='server',
|
||||||
|
):
|
||||||
|
if not cn:
|
||||||
|
if isinstance(cn, list):
|
||||||
|
return []
|
||||||
|
return
|
||||||
|
if authority_name is None:
|
||||||
|
if _x509_has_pub(cn, HERE):
|
||||||
|
return _x509_gen_pub(cn,
|
||||||
|
'key',
|
||||||
|
HERE,
|
||||||
|
)
|
||||||
|
if isinstance(cn, list):
|
||||||
|
return []
|
||||||
|
return
|
||||||
|
return _x509_gen_cert(cn,
|
||||||
|
[],
|
||||||
|
authority_cn,
|
||||||
|
authority_name,
|
||||||
|
type,
|
||||||
|
'key',
|
||||||
|
HERE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_public_key(cn):
|
||||||
|
if not cn:
|
||||||
|
return
|
||||||
|
return _x509_gen_pub(cn,
|
||||||
|
'pub',
|
||||||
|
HERE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def zone_information(zone_name: str,
|
||||||
|
type: str,
|
||||||
|
multi: bool=False,
|
||||||
|
index: int=None,
|
||||||
|
) -> str:
|
||||||
|
if not zone_name:
|
||||||
|
return
|
||||||
|
if type == 'gateway' and index != 0:
|
||||||
|
return
|
||||||
|
load_zones()
|
||||||
|
if zone_name not in ZONES:
|
||||||
|
raise ValueError(f"cannot get zone informations in unknown zone '{zone_name}'")
|
||||||
|
zone = ZONES[zone_name]
|
||||||
|
if type not in zone:
|
||||||
|
raise ValueError(f"unknown type '{type}' in zone '{zone_name}'")
|
||||||
|
value = zone[type]
|
||||||
|
if multi:
|
||||||
|
value = [value]
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def get_internal_zones() -> List[str]:
|
||||||
|
load_domains()
|
||||||
|
return list(DOMAINS.keys())
|
||||||
|
|
||||||
|
|
||||||
|
@multi_function
|
||||||
|
def get_zones_info(type: str) -> str:
|
||||||
|
ret = []
|
||||||
|
for data in ZONES_SERVER['zones'].values():
|
||||||
|
ret.append(data[type])
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@multi_function
|
||||||
|
def get_internal_zone_names() -> List[str]:
|
||||||
|
load_zones()
|
||||||
|
return list(ZONES.keys())
|
||||||
|
|
||||||
|
|
||||||
|
def get_internal_zone_information(zone: str,
|
||||||
|
info: str,
|
||||||
|
) -> str:
|
||||||
|
load_domains()
|
||||||
|
if info == 'cidr':
|
||||||
|
return ZONES[zone]['gateway'] + '/' + ZONES[zone]['network'].split('/')[-1]
|
||||||
|
return ZONES[zone][info]
|
||||||
|
|
||||||
|
|
||||||
|
def get_internal_info_in_zone(zone: str,
|
||||||
|
auto: bool,
|
||||||
|
type: str,
|
||||||
|
index: int=None,
|
||||||
|
) -> List[str]:
|
||||||
|
if not auto:
|
||||||
|
return
|
||||||
|
for domain_name, domain in DOMAINS.items():
|
||||||
|
if zone == domain_name:
|
||||||
|
if type == 'host':
|
||||||
|
return list(domain[0])
|
||||||
|
else:
|
||||||
|
return domain[1][index]
|
||||||
|
|
||||||
|
# =============================================================
|
191
servers.json
Normal file
191
servers.json
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
{"zones": {"external": {"network": "192.168.45.0/24",
|
||||||
|
"gateway": "192.168.45.1",
|
||||||
|
"start_ip": "192.168.45.10",
|
||||||
|
"domain_name": "in.silique.fr"
|
||||||
|
},
|
||||||
|
"list": {"network": "192.168.47.0/24",
|
||||||
|
"gateway": "192.168.47.1",
|
||||||
|
"start_ip": "192.168.47.10",
|
||||||
|
"domain_name": "list.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"modules": {"host": {"applicationservices": ["host-systemd-machined"]},
|
||||||
|
"unbound": {"applicationservices": ["unbound", "provider-systemd-machined"]},
|
||||||
|
"nsd": {"applicationservices": ["nsd", "provider-systemd-machined"]},
|
||||||
|
"revprox": {"applicationservices": ["nginx-reverse-proxy-server", "provider-systemd-machined"]},
|
||||||
|
"postgresql": {"applicationservices": ["postgresql-server", "provider-systemd-machined"]},
|
||||||
|
"redis": {"applicationservices": ["redis-server", "provider-systemd-machined"]},
|
||||||
|
"ldap": {"applicationservices": ["openldap-server", "provider-systemd-machined"]},
|
||||||
|
"lemonldap": {"applicationservices": ["lemonldap", "provider-systemd-machined"]},
|
||||||
|
"nextcloud": {"applicationservices": ["nextcloud", "provider-systemd-machined"]},
|
||||||
|
"mail": {"applicationservices": ["postfix-relay", "provider-systemd-machined"]},
|
||||||
|
"dovecot": {"applicationservices": ["dovecot", "provider-systemd-machined"]},
|
||||||
|
"mailman": {"applicationservices": ["mailman", "provider-systemd-machined"]},
|
||||||
|
"gitea": {"applicationservices": ["gitea", "provider-systemd-machined"]},
|
||||||
|
"roundcube": {"applicationservices": ["roundcube", "provider-systemd-machined"]},
|
||||||
|
"vaultwarden": {"applicationservices": ["vaultwarden", "provider-systemd-machined"]}
|
||||||
|
},
|
||||||
|
"servers": {"cloud": {"module": "host",
|
||||||
|
"values": {"rougail.host_install_dir": "/root/installations",
|
||||||
|
"rougail.host_dhcp_interface": ["enp3s0"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unbound.in.silique.fr": {"module": "unbound",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns_resolver.unbound_default_forwards": ["8.8.8.8"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nsd.in.silique.fr": {"module": "nsd",
|
||||||
|
"informations": {"zones_name": ["external", "list"],
|
||||||
|
"extra_domainnames": ["nsd.list.silique.fr"]
|
||||||
|
},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns_server.nsd_resolver": "unbound.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revprox.in.silique.fr": {"module": "revprox",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr",
|
||||||
|
"rougail.nginx.nginx_default": "cloud.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mail.in.silique.fr": {"module": "mail",
|
||||||
|
"informations": {"zones_name": ["external", "list"],
|
||||||
|
"extra_domainnames": ["mail.list.silique.fr"]
|
||||||
|
},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "unbound.in.silique.fr",
|
||||||
|
"rougail.postfix.postfix_mail_hostname": "mail.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dovecot.in.silique.fr": {"module": "dovecot",
|
||||||
|
"informations": {"zones_name": ["external"]
|
||||||
|
},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr",
|
||||||
|
"rougail.postfix.postfix_my_domains": ["cloud.silique.fr"],
|
||||||
|
"rougail.smtp.smtp_relay_address": "mail.in.silique.fr",
|
||||||
|
"rougail.annuaire.ldap_server_address": "ldap.in.silique.fr",
|
||||||
|
"rougail.dovecot.revprox_server_domainname": "revprox.in.silique.fr",
|
||||||
|
"rougail.oauth2_client.oauth2_client_server_domainname": "lemonldap.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis-rc.in.silique.fr": {"module": "redis",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis-nc.in.silique.fr": {"module": "redis",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis-gi.in.silique.fr": {"module": "redis",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ldap.in.silique.fr": {"module": "ldap",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr",
|
||||||
|
"accounts.users.ldap_user_mail": ["gnunux@silique.fr", "bbohard@silique.fr", "ddtddt@silique.fr"],
|
||||||
|
"accounts.users.ldap_user_uid": {"0": "gnunux", "1": "bbohard", "2": "ddtddt"},
|
||||||
|
"accounts.users.ldap_user_sn": {"0": "Emmanuel", "1": "Benjamin", "2": "Damien"},
|
||||||
|
"accounts.users.ldap_user_gn": {"0": "Garette", "1": "Bohard", "2": "Thomas"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lemonldap.in.silique.fr": {"module": "lemonldap",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr",
|
||||||
|
"rougail.annuaire.ldap_server_address": "ldap.in.silique.fr",
|
||||||
|
"rougail.smtp.smtp_relay_address": "mail.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_server_domainname": "revprox.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_external_domainname": "auth.silique.fr",
|
||||||
|
"rougail.lemonldap.lemon_domain": "cloud.silique.fr",
|
||||||
|
"rougail.lemonldap.lemon_mail_admin": "gnunux@silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nextcloud.in.silique.fr": {"module": "nextcloud",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr",
|
||||||
|
"rougail.nextcloud.nextcloud_mail_admin": "gnunux@silique.fr",
|
||||||
|
"rougail.postgresql.pg_client_server_domainname": "postgresql.in.silique.fr",
|
||||||
|
"rougail.annuaire.ldap_server_address": "ldap.in.silique.fr",
|
||||||
|
"rougail.redis.redis_client_server_domainname": "redis-nc.in.silique.fr",
|
||||||
|
"rougail.smtp.smtp_relay_address": "mail.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_server_domainname": "revprox.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_external_domainname": "cloud.silique.fr",
|
||||||
|
"rougail.oauth2_client.oauth2_client_server_domainname": "lemonldap.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"roundcube.in.silique.fr": {"module": "roundcube",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr",
|
||||||
|
"rougail.postgresql.pg_client_server_domainname": "postgresql.in.silique.fr",
|
||||||
|
"rougail.annuaire.ldap_server_address": "ldap.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_server_domainname": "revprox.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_external_domainname": "cloud.silique.fr",
|
||||||
|
"rougail.redis.redis_client_server_domainname": "redis-rc.in.silique.fr",
|
||||||
|
"rougail.imap.imap_address": "dovecot.in.silique.fr",
|
||||||
|
"rougail.oauth2_client.oauth2_client_server_domainname": "lemonldap.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postgresql.in.silique.fr": {"module": "postgresql",
|
||||||
|
"informations": {"zones_name": ["external", "list"],
|
||||||
|
"extra_domainnames": ["postgresql.list.silique.fr"]
|
||||||
|
},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mailman.list.silique.fr": {"module": "mailman",
|
||||||
|
"informations": {"zones_name": ["list"]
|
||||||
|
},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.list.silique.fr",
|
||||||
|
"rougail.smtp.smtp_relay_address": "mail.list.silique.fr",
|
||||||
|
"rougail.postgresql.pg_client_server_domainname": "postgresql.list.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_server_domainname": "revprox.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_external_domainname": "cloud.silique.fr",
|
||||||
|
"rougail.mailman.mailman_mail_owner": "admin@silique.fr",
|
||||||
|
"rougail.mailman.mailman_domains": ["lists.silique.fr"],
|
||||||
|
"rougail.oauth2_client.oauth2_client_server_domainname": "lemonldap.in.silique.fr",
|
||||||
|
"mailman.list_lists_silique_fr.name_lists_silique_fr": ["list1", "list2"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vaultwarden.in.silique.fr": {"module": "vaultwarden",
|
||||||
|
"informations": {"zones_name": ["external"]},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr",
|
||||||
|
"rougail.vaultwarden.vaultwarden_admin_email": "gnunux@silique.fr",
|
||||||
|
"rougail.postgresql.pg_client_server_domainname": "postgresql.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_server_domainname": "revprox.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_external_domainname": "cloud.silique.fr",
|
||||||
|
"rougail.smtp.smtp_relay_address": "mail.in.silique.fr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitea.in.silique.fr": {"module": "gitea",
|
||||||
|
"informations": {"zones_name": ["external"]
|
||||||
|
},
|
||||||
|
"values": {"rougail.host": "cloud",
|
||||||
|
"rougail.dns.dns_client_address": "nsd.in.silique.fr",
|
||||||
|
"rougail.smtp.smtp_relay_address": "mail.in.silique.fr",
|
||||||
|
"rougail.gitea.gitea_mail_sender": "gitea@silique.fr",
|
||||||
|
"rougail.postgresql.pg_client_server_domainname": "postgresql.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_server_domainname": "revprox.in.silique.fr",
|
||||||
|
"rougail.redis.redis_client_server_domainname": "redis-gi.in.silique.fr",
|
||||||
|
"rougail.oauth2_client.oauth2_client_server_domainname": "lemonldap.in.silique.fr",
|
||||||
|
"rougail.nginx.revprox_client_external_domainname": "cloud.silique.fr"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
10
src/utils.py
Normal file
10
src/utils.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
MULTI_FUNCTIONS = []
|
||||||
|
CONFIGS = {}
|
||||||
|
|
||||||
|
|
||||||
|
def multi_function(function):
|
||||||
|
global MULTI_FUNCTIONS
|
||||||
|
name = function.__name__
|
||||||
|
if name not in MULTI_FUNCTIONS:
|
||||||
|
MULTI_FUNCTIONS.append(name)
|
||||||
|
return function
|
254
src/x509.py
Normal file
254
src/x509.py
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
from OpenSSL.crypto import load_certificate, load_privatekey, dump_certificate, dump_privatekey, dump_publickey, PKey, X509, X509Extension, TYPE_RSA, FILETYPE_PEM
|
||||||
|
from os import makedirs, symlink
|
||||||
|
from os.path import join, isdir, isfile, exists
|
||||||
|
#from shutil import rmtree
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
PKI_DIR = 'pki/x509'
|
||||||
|
#FIXME
|
||||||
|
EMAIL = 'gnunux@gnunux.info'
|
||||||
|
COUNTRY = 'FR'
|
||||||
|
LOCALITY = 'Dijon'
|
||||||
|
STATE = 'France'
|
||||||
|
ORG_NAME = 'Cadoles'
|
||||||
|
ORG_UNIT_NAME = 'CSS'
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_key_pair():
|
||||||
|
key = PKey()
|
||||||
|
key.generate_key(TYPE_RSA, 4096)
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_cert(is_ca,
|
||||||
|
common_names,
|
||||||
|
serial_number,
|
||||||
|
validity_end_in_seconds,
|
||||||
|
key_file,
|
||||||
|
cert_file,
|
||||||
|
type=None,
|
||||||
|
ca_cert=None,
|
||||||
|
ca_key=None,
|
||||||
|
email_address=None,
|
||||||
|
country_name=None,
|
||||||
|
locality_name=None,
|
||||||
|
state_or_province_name=None,
|
||||||
|
organization_name=None,
|
||||||
|
organization_unit_name=None,
|
||||||
|
):
|
||||||
|
#can look at generated file using openssl:
|
||||||
|
#openssl x509 -inform pem -in selfsigned.crt -noout -text
|
||||||
|
# create a key pair
|
||||||
|
if isfile(key_file):
|
||||||
|
with open(key_file) as fh:
|
||||||
|
filecontent = bytes(fh.read(), 'utf-8')
|
||||||
|
key = load_privatekey(FILETYPE_PEM, filecontent)
|
||||||
|
else:
|
||||||
|
key = _gen_key_pair()
|
||||||
|
cert = X509()
|
||||||
|
cert.set_version(2)
|
||||||
|
cert.get_subject().C = country_name
|
||||||
|
cert.get_subject().ST = state_or_province_name
|
||||||
|
cert.get_subject().L = locality_name
|
||||||
|
cert.get_subject().O = organization_name
|
||||||
|
cert.get_subject().OU = organization_unit_name
|
||||||
|
cert.get_subject().CN = common_names[0]
|
||||||
|
cert.get_subject().emailAddress = email_address
|
||||||
|
cert_ext = []
|
||||||
|
if not is_ca:
|
||||||
|
cert_ext.append(X509Extension(b'basicConstraints', False, b'CA:FALSE'))
|
||||||
|
cert_ext.append(X509Extension(b'keyUsage', True, b'digitalSignature, keyEncipherment'))
|
||||||
|
cert_ext.append(X509Extension(b'subjectAltName', False, ", ".join([f'DNS:{common_name}' for common_name in common_names]).encode('ascii')))
|
||||||
|
if type == 'server':
|
||||||
|
cert_ext.append(X509Extension(b'extendedKeyUsage', True, b'serverAuth'))
|
||||||
|
else:
|
||||||
|
cert_ext.append(X509Extension(b'extendedKeyUsage', True, b'clientAuth'))
|
||||||
|
else:
|
||||||
|
cert_ext.append(X509Extension(b'basicConstraints', False, b'CA:TRUE'))
|
||||||
|
cert_ext.append(X509Extension(b"keyUsage", True, b'keyCertSign, cRLSign'))
|
||||||
|
cert_ext.append(X509Extension(b'subjectAltName', False, f'email:{email_address}'.encode()))
|
||||||
|
cert_ext.append(X509Extension(b'subjectKeyIdentifier', False, b"hash", subject=cert))
|
||||||
|
cert.add_extensions(cert_ext)
|
||||||
|
cert.set_serial_number(serial_number)
|
||||||
|
cert.gmtime_adj_notBefore(0)
|
||||||
|
cert.gmtime_adj_notAfter(validity_end_in_seconds)
|
||||||
|
if is_ca:
|
||||||
|
ca_cert = cert
|
||||||
|
ca_key = key
|
||||||
|
else:
|
||||||
|
with open(ca_cert) as fh:
|
||||||
|
filecontent = bytes(fh.read(), 'utf-8')
|
||||||
|
ca_cert = load_certificate(FILETYPE_PEM, filecontent)
|
||||||
|
with open(ca_key) as fh:
|
||||||
|
filecontent = bytes(fh.read(), 'utf-8')
|
||||||
|
ca_key = load_privatekey(FILETYPE_PEM, filecontent)
|
||||||
|
cert.set_issuer(ca_cert.get_subject())
|
||||||
|
cert.add_extensions([X509Extension(b"authorityKeyIdentifier", False, b'keyid:always', issuer=ca_cert)])
|
||||||
|
cert.set_pubkey(key)
|
||||||
|
cert.sign(ca_key, "sha512")
|
||||||
|
|
||||||
|
with open(cert_file, "wt") as f:
|
||||||
|
f.write(dump_certificate(FILETYPE_PEM, cert).decode("utf-8"))
|
||||||
|
with open(key_file, "wt") as f:
|
||||||
|
f.write(dump_privatekey(FILETYPE_PEM, key).decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def gen_ca(authority_dns,
|
||||||
|
authority_name,
|
||||||
|
base_dir,
|
||||||
|
):
|
||||||
|
authority_cn = authority_name + '+' + authority_dns
|
||||||
|
week_number = datetime.now().isocalendar().week
|
||||||
|
root_dir_name = join(base_dir, PKI_DIR, authority_cn)
|
||||||
|
ca_dir_name = join(root_dir_name, 'ca')
|
||||||
|
sn_ca_name = join(ca_dir_name, 'serial_number')
|
||||||
|
key_ca_name = join(ca_dir_name, 'private.key')
|
||||||
|
cert_ca_name = join(ca_dir_name, f'certificate_{week_number}.crt')
|
||||||
|
if not isfile(cert_ca_name):
|
||||||
|
if not isdir(ca_dir_name):
|
||||||
|
# rmtree(ca_dir_name)
|
||||||
|
makedirs(ca_dir_name)
|
||||||
|
if isfile(sn_ca_name):
|
||||||
|
with open(sn_ca_name, 'r') as fh:
|
||||||
|
serial_number = int(fh.read().strip()) + 1
|
||||||
|
else:
|
||||||
|
serial_number = 0
|
||||||
|
_gen_cert(True,
|
||||||
|
[authority_cn],
|
||||||
|
serial_number,
|
||||||
|
10*24*60*60,
|
||||||
|
key_ca_name,
|
||||||
|
cert_ca_name,
|
||||||
|
email_address=EMAIL,
|
||||||
|
country_name=COUNTRY,
|
||||||
|
locality_name=LOCALITY,
|
||||||
|
state_or_province_name=STATE,
|
||||||
|
organization_name=ORG_NAME,
|
||||||
|
organization_unit_name=ORG_UNIT_NAME,
|
||||||
|
)
|
||||||
|
with open(sn_ca_name, 'w') as fh:
|
||||||
|
fh.write(str(serial_number))
|
||||||
|
with open(cert_ca_name, 'r') as fh:
|
||||||
|
return fh.read().strip()
|
||||||
|
|
||||||
|
|
||||||
|
def gen_cert_iter(cn,
|
||||||
|
extra_domainnames,
|
||||||
|
authority_cn,
|
||||||
|
authority_name,
|
||||||
|
type,
|
||||||
|
base_dir,
|
||||||
|
dir_name,
|
||||||
|
):
|
||||||
|
week_number = datetime.now().isocalendar().week
|
||||||
|
root_dir_name = join(base_dir, PKI_DIR, authority_cn)
|
||||||
|
ca_dir_name = join(root_dir_name, 'ca')
|
||||||
|
key_ca_name = join(ca_dir_name, 'private.key')
|
||||||
|
cert_ca_name = join(ca_dir_name, f'certificate_{week_number}.crt')
|
||||||
|
sn_name = join(dir_name, f'serial_number')
|
||||||
|
key_name = join(dir_name, f'private.key')
|
||||||
|
cert_name = join(dir_name, f'certificate_{week_number}.crt')
|
||||||
|
if not isfile(cert_ca_name):
|
||||||
|
raise Exception(f'cannot find CA file "{cert_ca_name}"')
|
||||||
|
if not isfile(cert_name):
|
||||||
|
if not isdir(dir_name):
|
||||||
|
makedirs(dir_name)
|
||||||
|
if isfile(sn_name):
|
||||||
|
with open(sn_name, 'r') as fh:
|
||||||
|
serial_number = int(fh.read().strip()) + 1
|
||||||
|
else:
|
||||||
|
serial_number = 0
|
||||||
|
common_names = [cn]
|
||||||
|
common_names.extend(extra_domainnames)
|
||||||
|
_gen_cert(False,
|
||||||
|
common_names,
|
||||||
|
serial_number,
|
||||||
|
10*24*60*60,
|
||||||
|
key_name,
|
||||||
|
cert_name,
|
||||||
|
ca_cert=cert_ca_name,
|
||||||
|
ca_key=key_ca_name,
|
||||||
|
type=type,
|
||||||
|
email_address=EMAIL,
|
||||||
|
country_name=COUNTRY,
|
||||||
|
locality_name=LOCALITY,
|
||||||
|
state_or_province_name=STATE,
|
||||||
|
organization_name=ORG_NAME,
|
||||||
|
organization_unit_name=ORG_UNIT_NAME,
|
||||||
|
)
|
||||||
|
with open(sn_name, 'w') as fh:
|
||||||
|
fh.write(str(serial_number))
|
||||||
|
for extra in extra_domainnames:
|
||||||
|
extra_dir_name = join(base_dir, PKI_DIR, authority_name + '+' + extra)
|
||||||
|
if not exists(extra_dir_name):
|
||||||
|
symlink(root_dir_name, extra_dir_name)
|
||||||
|
for extra in extra_domainnames:
|
||||||
|
extra_dir_name = join(base_dir, PKI_DIR, authority_name + '+' + extra)
|
||||||
|
if not exists(extra_dir_name):
|
||||||
|
raise Exception(f'file {extra_dir_name} not already exists that means subjectAltName is not set in certificat, please remove {cert_name}')
|
||||||
|
return cert_name
|
||||||
|
|
||||||
|
|
||||||
|
def gen_cert(cn,
|
||||||
|
extra_domainnames,
|
||||||
|
authority_cn,
|
||||||
|
authority_name,
|
||||||
|
type,
|
||||||
|
file_type,
|
||||||
|
base_dir,
|
||||||
|
):
|
||||||
|
if '.' in authority_name:
|
||||||
|
raise Exception(f'dot is not allowed in authority_name "{authority_name}"')
|
||||||
|
if type == 'server' and authority_cn is None:
|
||||||
|
authority_cn = cn
|
||||||
|
if authority_cn is None:
|
||||||
|
raise Exception(f'authority_cn is mandatory when authority type is client')
|
||||||
|
if extra_domainnames is None:
|
||||||
|
extra_domainnames = []
|
||||||
|
auth_cn = authority_name + '+' + authority_cn
|
||||||
|
dir_name = join(base_dir, PKI_DIR, auth_cn, 'certificats', cn, type)
|
||||||
|
if file_type == 'crt':
|
||||||
|
filename = gen_cert_iter(cn,
|
||||||
|
extra_domainnames,
|
||||||
|
auth_cn,
|
||||||
|
authority_name,
|
||||||
|
type,
|
||||||
|
base_dir,
|
||||||
|
dir_name,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
filename = join(dir_name, f'private.key')
|
||||||
|
with open(filename, 'r') as fh:
|
||||||
|
return fh.read().strip()
|
||||||
|
|
||||||
|
|
||||||
|
def has_pub(cn,
|
||||||
|
base_dir,
|
||||||
|
):
|
||||||
|
dir_name = join(base_dir, PKI_DIR, 'public', cn)
|
||||||
|
cert_name = join(dir_name, f'public.pub')
|
||||||
|
return isfile(cert_name)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_pub(cn,
|
||||||
|
file_type,
|
||||||
|
base_dir,
|
||||||
|
):
|
||||||
|
dir_name = join(base_dir, PKI_DIR, 'public', cn)
|
||||||
|
key_name = join(dir_name, f'private.key')
|
||||||
|
if file_type == 'pub':
|
||||||
|
pub_name = join(dir_name, f'public.pub')
|
||||||
|
if not isfile(pub_name):
|
||||||
|
if not isdir(dir_name):
|
||||||
|
makedirs(dir_name)
|
||||||
|
key = _gen_key_pair()
|
||||||
|
with open(pub_name, "wt") as f:
|
||||||
|
f.write(dump_publickey(FILETYPE_PEM, key).decode("utf-8"))
|
||||||
|
with open(key_name, "wt") as f:
|
||||||
|
f.write(dump_privatekey(FILETYPE_PEM, key).decode("utf-8"))
|
||||||
|
filename = pub_name
|
||||||
|
else:
|
||||||
|
filename = key_name
|
||||||
|
with open(filename, 'r') as fh:
|
||||||
|
return fh.read().strip()
|
472
test.py
Executable file
472
test.py
Executable file
|
@ -0,0 +1,472 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from asyncio import run
|
||||||
|
from os import listdir, link, makedirs
|
||||||
|
from os.path import isdir, isfile, join
|
||||||
|
from shutil import rmtree, copy2, copytree
|
||||||
|
from json import load as json_load
|
||||||
|
from yaml import load, SafeLoader
|
||||||
|
from pprint import pprint
|
||||||
|
from typing import Any
|
||||||
|
from warnings import warn_explicit
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from tiramisu import Config
|
||||||
|
from tiramisu.error import ValueWarning
|
||||||
|
from rougail import RougailConfig, RougailConvert, RougailSystemdTemplate
|
||||||
|
from rougail.utils import normalize_family
|
||||||
|
#from rougail.error import TemplateError
|
||||||
|
|
||||||
|
from utils import MULTI_FUNCTIONS, CONFIGS
|
||||||
|
|
||||||
|
DATASET_DIRECTORY = '/home/gnunux/git/risotto_cadoles/risotto-dataset/seed'
|
||||||
|
FUNCTIONS = 'funcs.py'
|
||||||
|
CONFIG_DEST_DIR = 'configurations'
|
||||||
|
SRV_DEST_DIR = 'srv'
|
||||||
|
INSTALL_DIR = 'installations'
|
||||||
|
# "netbox.in.gnunux.info": {"applicationservices": ["netbox", "provider-systemd-machined"],
|
||||||
|
# "informations": {"zones_name": ["gnunux"]},
|
||||||
|
# "values": {"rougail.postgresql.pg_client_server_domainname": "postgresql.in.gnunux.info",
|
||||||
|
# "rougail.redis.redis_client_server_domainname": "redis.in.gnunux.info",
|
||||||
|
# "rougail.nginx.revprox_client_server_domainname": "revprox.in.gnunux.info",
|
||||||
|
# "rougail.nginx.revprox_client_external_domainname": "in.gnunux.info"
|
||||||
|
# }
|
||||||
|
# },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
with open('servers.json', 'r') as server_fh:
|
||||||
|
jsonfile = json_load(server_fh)
|
||||||
|
SERVERS = jsonfile['servers']
|
||||||
|
MODULES = jsonfile['modules']
|
||||||
|
|
||||||
|
|
||||||
|
async def set_linked(linked_server: str,
|
||||||
|
linked_provider: str,
|
||||||
|
linked_value: str,
|
||||||
|
linked_returns: str=None,
|
||||||
|
dynamic: str=None,
|
||||||
|
):
|
||||||
|
if None in (linked_server, linked_provider, linked_value):
|
||||||
|
return
|
||||||
|
if linked_server not in CONFIGS:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
config = CONFIGS[linked_server][0]
|
||||||
|
path = await config.information.get('provider:' + linked_provider, None)
|
||||||
|
if not path:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find provider "{linked_provider}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
await config.property.read_write()
|
||||||
|
try:
|
||||||
|
option = config.forcepermissive.option(path)
|
||||||
|
if await option.option.ismulti():
|
||||||
|
values = await option.value.get()
|
||||||
|
if linked_value not in values:
|
||||||
|
values.append(linked_value)
|
||||||
|
await option.value.set(values)
|
||||||
|
else:
|
||||||
|
await option.value.set(linked_value)
|
||||||
|
except Exception as err:
|
||||||
|
await config.property.read_only()
|
||||||
|
raise err from err
|
||||||
|
await config.property.read_only()
|
||||||
|
if linked_returns is not None:
|
||||||
|
linked_variable = await config.information.get('provider:' + linked_returns, None)
|
||||||
|
if not linked_variable:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked variable "{linked_returns}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
linked_variable = None
|
||||||
|
if linked_variable is not None:
|
||||||
|
if dynamic:
|
||||||
|
linked_variable = linked_variable.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
elif '{suffix}' in linked_variable:
|
||||||
|
idx = CONFIGS[linked_server][3]
|
||||||
|
linked_variable = linked_variable.replace('{suffix}', str(idx))
|
||||||
|
ret = await config.forcepermissive.option(linked_variable).value.get()
|
||||||
|
else:
|
||||||
|
ret = normalize_family(linked_value)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
async def get_linked_configuration(linked_server: str,
|
||||||
|
linked_provider: str,
|
||||||
|
dynamic: str=None,
|
||||||
|
):
|
||||||
|
if linked_server not in CONFIGS:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
config = CONFIGS[linked_server][0]
|
||||||
|
path = await config.information.get('provider:' + linked_provider, None)
|
||||||
|
if not path:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find variable "{path}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if dynamic:
|
||||||
|
path = path.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
try:
|
||||||
|
return await config.forcepermissive.option(path).value.get()
|
||||||
|
except AttributeError as err:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find get value of "{path}" in linked server "{linked_server}": {err}'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Empty:
|
||||||
|
pass
|
||||||
|
empty = Empty()
|
||||||
|
|
||||||
|
|
||||||
|
async def set_linked_configuration(_linked_value: Any,
|
||||||
|
linked_server: str,
|
||||||
|
linked_provider: str,
|
||||||
|
linked_value: Any=empty,
|
||||||
|
dynamic: str=None,
|
||||||
|
leader_provider: str=None,
|
||||||
|
leader_value: Any=None,
|
||||||
|
):
|
||||||
|
if linked_value is not empty:
|
||||||
|
_linked_value = linked_value
|
||||||
|
linked_value = _linked_value
|
||||||
|
if linked_server is None:
|
||||||
|
return
|
||||||
|
if linked_value is None or linked_server not in CONFIGS:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
config = CONFIGS[linked_server][0]
|
||||||
|
path = await config.information.get('provider:' + linked_provider, None)
|
||||||
|
if not path:
|
||||||
|
warn_explicit(ValueWarning(f'cannot find variable "{path}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if dynamic:
|
||||||
|
path = path.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
await config.property.read_write()
|
||||||
|
try:
|
||||||
|
if leader_provider is not None:
|
||||||
|
leader_path = await config.information.get('provider:' + leader_provider, None)
|
||||||
|
if not leader_path:
|
||||||
|
await config.property.read_only()
|
||||||
|
warn_explicit(ValueWarning(f'cannot find leader variable "{path}" in linked server "{linked_server}"'),
|
||||||
|
ValueWarning,
|
||||||
|
__file__,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if dynamic:
|
||||||
|
leader_path = leader_path.replace('{suffix}', normalize_family(dynamic))
|
||||||
|
values = await config.forcepermissive.option(leader_path).value.get()
|
||||||
|
if leader_value in values:
|
||||||
|
slave_idx = values.index(leader_value)
|
||||||
|
slave_option = config.forcepermissive.option(path, slave_idx)
|
||||||
|
if await slave_option.option.issubmulti():
|
||||||
|
slave_values = await slave_option.value.get()
|
||||||
|
if linked_value not in slave_values:
|
||||||
|
slave_values.append(linked_value)
|
||||||
|
await slave_option.value.set(slave_values)
|
||||||
|
|
||||||
|
else:
|
||||||
|
await slave_option.value.set(linked_value)
|
||||||
|
else:
|
||||||
|
option = config.forcepermissive.option(path)
|
||||||
|
if await option.option.ismulti() and not isinstance(linked_value, list):
|
||||||
|
values = await option.value.get()
|
||||||
|
if linked_value not in values:
|
||||||
|
values.append(linked_value)
|
||||||
|
await option.value.set(values)
|
||||||
|
else:
|
||||||
|
await option.value.set(linked_value)
|
||||||
|
except AttributeError as err:
|
||||||
|
#raise ValueError(str(err)) from err
|
||||||
|
pass
|
||||||
|
except Exception as err:
|
||||||
|
await config.property.read_only()
|
||||||
|
raise err from err
|
||||||
|
await config.property.read_only()
|
||||||
|
|
||||||
|
|
||||||
|
def tiramisu_display_name(kls,
|
||||||
|
dyn_name: 'Base'=None,
|
||||||
|
suffix: str=None,
|
||||||
|
) -> str:
|
||||||
|
if dyn_name is not None:
|
||||||
|
name = kls.impl_getpath() + suffix
|
||||||
|
else:
|
||||||
|
name = kls.impl_getpath()
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def load_applications():
|
||||||
|
applications = {}
|
||||||
|
for distrib in listdir(DATASET_DIRECTORY):
|
||||||
|
distrib_dir = join(DATASET_DIRECTORY, distrib, 'applicationservice')
|
||||||
|
if not isdir(distrib_dir):
|
||||||
|
continue
|
||||||
|
for release in listdir(distrib_dir):
|
||||||
|
release_dir = join(distrib_dir, release)
|
||||||
|
if not isdir(release_dir):
|
||||||
|
continue
|
||||||
|
for applicationservice in listdir(release_dir):
|
||||||
|
applicationservice_dir = join(release_dir, applicationservice)
|
||||||
|
if not isdir(applicationservice_dir):
|
||||||
|
continue
|
||||||
|
if applicationservice in applications:
|
||||||
|
raise Exception(f'multi applicationservice: {applicationservice} ({applicationservice_dir} <=> {applications[applicationservice]})')
|
||||||
|
applications[applicationservice] = applicationservice_dir
|
||||||
|
return applications
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleCfg():
|
||||||
|
def __init__(self):
|
||||||
|
self.dictionaries_dir = []
|
||||||
|
self.modules = []
|
||||||
|
self.functions_file = [FUNCTIONS]
|
||||||
|
self.templates_dir = []
|
||||||
|
self.extra_dictionaries = {}
|
||||||
|
self.servers = []
|
||||||
|
|
||||||
|
|
||||||
|
def build_module(module_name, datas, module_infos):
|
||||||
|
install_dir = join(INSTALL_DIR, module_name)
|
||||||
|
makedirs(install_dir)
|
||||||
|
applications = load_applications()
|
||||||
|
cfg = ModuleCfg()
|
||||||
|
module_infos[module_name] = cfg
|
||||||
|
def calc_depends(appname, added):
|
||||||
|
if appname in added:
|
||||||
|
return
|
||||||
|
as_dir = applications[appname]
|
||||||
|
cfg.modules.append(appname)
|
||||||
|
dictionaries_dir = join(as_dir, 'dictionaries')
|
||||||
|
if isdir(dictionaries_dir):
|
||||||
|
cfg.dictionaries_dir.append(dictionaries_dir)
|
||||||
|
funcs_dir = join(as_dir, 'funcs')
|
||||||
|
if isdir(funcs_dir):
|
||||||
|
for f in listdir(funcs_dir):
|
||||||
|
if f.startswith('__'):
|
||||||
|
continue
|
||||||
|
cfg.functions_file.append(join(funcs_dir, f))
|
||||||
|
templates_dir = join(as_dir, 'templates')
|
||||||
|
if isdir(templates_dir):
|
||||||
|
cfg.templates_dir.append(templates_dir)
|
||||||
|
extras_dir = join(as_dir, 'extras')
|
||||||
|
if isdir(extras_dir):
|
||||||
|
for extra in listdir(extras_dir):
|
||||||
|
extra_dir = join(extras_dir, extra)
|
||||||
|
if isdir(extra_dir):
|
||||||
|
cfg.extra_dictionaries.setdefault(extra, []).append(extra_dir)
|
||||||
|
for type in ['image', 'install']:
|
||||||
|
manual_dir = join(as_dir, 'manual', type)
|
||||||
|
if isdir(manual_dir):
|
||||||
|
for filename in listdir(manual_dir):
|
||||||
|
src_file = join(manual_dir, filename)
|
||||||
|
if type == 'image':
|
||||||
|
dst_file = join(install_dir, filename)
|
||||||
|
verify = False
|
||||||
|
else:
|
||||||
|
dst_file= join(INSTALL_DIR, filename)
|
||||||
|
verify = True
|
||||||
|
if isdir(src_file):
|
||||||
|
if not isdir(dst_file):
|
||||||
|
makedirs(dst_file)
|
||||||
|
for subfilename in listdir(src_file):
|
||||||
|
if not verify or not isfile(dst_file):
|
||||||
|
src = join(src_file, subfilename)
|
||||||
|
dst = join(dst_file, subfilename)
|
||||||
|
if isfile(src):
|
||||||
|
copy2(src, dst)
|
||||||
|
else:
|
||||||
|
copytree(src, dst)
|
||||||
|
elif not verify or not isfile(dst_file):
|
||||||
|
src = join(manual_dir, filename)
|
||||||
|
dst = dst_file
|
||||||
|
if isfile(src):
|
||||||
|
copy2(src, dst)
|
||||||
|
else:
|
||||||
|
copytree(src, dst)
|
||||||
|
added.append(appname)
|
||||||
|
with open(join(as_dir, 'applicationservice.yml')) as yaml:
|
||||||
|
app = load(yaml, Loader=SafeLoader)
|
||||||
|
|
||||||
|
for xml in app.get('depends', []):
|
||||||
|
calc_depends(xml, added)
|
||||||
|
added = []
|
||||||
|
for applicationservice in datas['applicationservices']:
|
||||||
|
calc_depends(applicationservice, added)
|
||||||
|
|
||||||
|
|
||||||
|
async def build(server_name, datas, module_infos):
|
||||||
|
if server_name in CONFIGS:
|
||||||
|
raise Exception(f'server "{server_name}" is duplicate')
|
||||||
|
cfg = RougailConfig.copy()
|
||||||
|
module_info = module_infos[datas['module']]
|
||||||
|
module_info.servers.append(server_name)
|
||||||
|
if datas['module'] == 'host':
|
||||||
|
cfg['tmpfile_dest_dir'] = datas['values']['rougail.host_install_dir'] + '/host/configurations/' + server_name
|
||||||
|
cfg['templates_dir'] = module_info.templates_dir
|
||||||
|
cfg['dictionaries_dir'] = module_info.dictionaries_dir
|
||||||
|
cfg['functions_file'] = module_info.functions_file
|
||||||
|
cfg['multi_functions'] = MULTI_FUNCTIONS
|
||||||
|
cfg['extra_dictionaries'] = module_info.extra_dictionaries
|
||||||
|
cfg['extra_annotators'].append('risotto_setting.rougail')
|
||||||
|
optiondescription = {'set_linked': set_linked,
|
||||||
|
'get_linked_configuration': get_linked_configuration,
|
||||||
|
'set_linked_configuration': set_linked_configuration,
|
||||||
|
}
|
||||||
|
cfg['internal_functions'] = list(optiondescription.keys())
|
||||||
|
try:
|
||||||
|
eolobj = RougailConvert(cfg)
|
||||||
|
except Exception as err:
|
||||||
|
print(f'Try to load {module_info.modules}')
|
||||||
|
raise err from err
|
||||||
|
xml = eolobj.save(None)
|
||||||
|
#print(xml)
|
||||||
|
#cfg['patches_dir'] = join(test_dir, 'patches')
|
||||||
|
cfg['tmp_dir'] = 'tmp'
|
||||||
|
cfg['destinations_dir'] = join(INSTALL_DIR, datas['module'], CONFIG_DEST_DIR, server_name)
|
||||||
|
if isdir('tmp'):
|
||||||
|
rmtree('tmp')
|
||||||
|
makedirs('tmp')
|
||||||
|
makedirs(cfg['destinations_dir'])
|
||||||
|
try:
|
||||||
|
exec(xml, None, optiondescription)
|
||||||
|
except Exception as err:
|
||||||
|
print(xml)
|
||||||
|
raise Exception(f'unknown error when load tiramisu object {err}') from err
|
||||||
|
config = await Config(optiondescription['option_0'], display_name=tiramisu_display_name)
|
||||||
|
await config.property.read_write()
|
||||||
|
try:
|
||||||
|
if await config.option('machine.add_srv').value.get():
|
||||||
|
srv = join(INSTALL_DIR, SRV_DEST_DIR, server_name)
|
||||||
|
else:
|
||||||
|
srv = None
|
||||||
|
except AttributeError:
|
||||||
|
srv = None
|
||||||
|
await config.property.read_write()
|
||||||
|
CONFIGS[server_name] = (config, cfg, srv, 0)
|
||||||
|
|
||||||
|
|
||||||
|
async def value_pprint(dico, config):
|
||||||
|
pprint_dict = {}
|
||||||
|
for path, value in dico.items():
|
||||||
|
if await config.option(path).option.type() == 'password' and value:
|
||||||
|
value = 'X' * len(value)
|
||||||
|
pprint_dict[path] = value
|
||||||
|
pprint(pprint_dict)
|
||||||
|
|
||||||
|
|
||||||
|
async def set_values(server_name, config, datas):
|
||||||
|
if 'informations' in datas:
|
||||||
|
for information, value in datas['informations'].items():
|
||||||
|
await config.information.set(information, value)
|
||||||
|
if 'extra_domainnames' in datas['informations']:
|
||||||
|
for idx, extra_domainname in enumerate(datas['informations']['extra_domainnames']):
|
||||||
|
if extra_domainname in CONFIGS:
|
||||||
|
raise Exception(f'server "{server_name}" is duplicate')
|
||||||
|
value = list(CONFIGS[server_name])
|
||||||
|
value[3] = idx + 1
|
||||||
|
CONFIGS[extra_domainname] = tuple(value)
|
||||||
|
await config.information.set('server_name', server_name)
|
||||||
|
await config.property.read_write()
|
||||||
|
try:
|
||||||
|
if 'values' in datas:
|
||||||
|
for path, value in datas['values'].items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for idx, val in value.items():
|
||||||
|
await config.option(path, int(idx)).value.set(val)
|
||||||
|
else:
|
||||||
|
await config.option(path).value.set(value)
|
||||||
|
except Exception as err:
|
||||||
|
await value_pprint(await config.value.dict(), config)
|
||||||
|
error_msg = f'cannot configure server "{server_name}": {err}'
|
||||||
|
raise Exception(error_msg) from err
|
||||||
|
await config.property.read_only()
|
||||||
|
#await config.value.dict()
|
||||||
|
|
||||||
|
|
||||||
|
async def valid_mandatories(server_name, config):
|
||||||
|
mandatories = await config.value.mandatory()
|
||||||
|
if mandatories:
|
||||||
|
print()
|
||||||
|
print(f'=== Configuration: {server_name} ===')
|
||||||
|
await config.property.pop('mandatory')
|
||||||
|
await value_pprint(await config.value.dict(), config)
|
||||||
|
raise Exception(f'server "{server_name}" has mandatories variables without values "{", ".join(mandatories)}"')
|
||||||
|
|
||||||
|
|
||||||
|
async def templates(server_name, config, cfg, srv, int_idx):
|
||||||
|
values = await config.value.dict()
|
||||||
|
engine = RougailSystemdTemplate(config, cfg)
|
||||||
|
# if server_name == 'roundcube.in.gnunux.info':
|
||||||
|
# print()
|
||||||
|
# print(f'=== Configuration: {server_name} ===')
|
||||||
|
# pprint(values)
|
||||||
|
try:
|
||||||
|
await engine.instance_files()
|
||||||
|
except Exception as err:
|
||||||
|
print()
|
||||||
|
print(f'=== Configuration: {server_name} ===')
|
||||||
|
await value_pprint(values, config)
|
||||||
|
raise err from err
|
||||||
|
if srv:
|
||||||
|
makedirs(srv)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
if isdir(INSTALL_DIR):
|
||||||
|
rmtree(INSTALL_DIR)
|
||||||
|
makedirs(INSTALL_DIR)
|
||||||
|
module_infos = {}
|
||||||
|
for module_name, datas in MODULES.items():
|
||||||
|
build_module(module_name, datas, module_infos)
|
||||||
|
for server_name, datas in SERVERS.items():
|
||||||
|
await build(server_name, datas, module_infos)
|
||||||
|
for module_name, cfg in module_infos.items():
|
||||||
|
with open(join(INSTALL_DIR, module_name, 'install_machines'), 'w') as fh:
|
||||||
|
for server_name in cfg.servers:
|
||||||
|
fh.write(f'./install_machine {module_name} {server_name}\n')
|
||||||
|
for server_name, datas in SERVERS.items():
|
||||||
|
await set_values(server_name, CONFIGS[server_name][0], datas)
|
||||||
|
for server_name in SERVERS:
|
||||||
|
config = CONFIGS[server_name][0]
|
||||||
|
await config.property.pop('mandatory')
|
||||||
|
await config.value.dict()
|
||||||
|
await config.property.add('mandatory')
|
||||||
|
for server_name in SERVERS:
|
||||||
|
await valid_mandatories(server_name, CONFIGS[server_name][0])
|
||||||
|
# print(await CONFIGS['revprox.in.gnunux.info'][0].option('nginx.reverse_proxy_for_netbox_in_gnunux_info.reverse_proxy_netbox_in_gnunux_info.revprox_url_netbox_in_gnunux_info', 0).value.get())
|
||||||
|
for server_name in SERVERS:
|
||||||
|
await templates(server_name, *CONFIGS[server_name])
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
Loading…
Reference in a new issue