diff --git a/generate-conf.py b/generate-conf.py index 4ac7c64..f47752f 100644 --- a/generate-conf.py +++ b/generate-conf.py @@ -1,10 +1,12 @@ import argparse import difflib +import getpass import logging from pprint import pprint import colorlog +import requests import termcolor import yaml @@ -142,8 +144,17 @@ def gen_interfaces(switch_config): interfaces.sort(key=lambda x: x["number"]) return interfaces, vlans, mac_based_ports, ra_guard_ports, dhcp_snooping_vlans -def gen_conf(master_config, switch_config, old_config): +def get_header(old_config): header = "\n".join(old_config.split("\n")[:2]) + return header + +def conf_from_dict(config_dict): + with open("configs/config.j2", "r") as template_file: + template = Template(template_file.read()) + configuration = template.render(config_dict) + return configuration + +def gen_conf(master_config, switch_config, header): interfaces, vlans, mac_based_ports, ra_guard_ports, dhcp_snooping_vlans = gen_interfaces(switch_config) config_dict = { "header": header, @@ -164,13 +175,49 @@ def gen_conf(master_config, switch_config, old_config): "unauth_redirect": master_config.get("unauth_redirect"), "dhcp_snooping_vlans": syntax_from_range(dhcp_snooping_vlans, vlan_syntax=True), } - with open("configs/config.j2", "r") as template_file: - template = Template(template_file.read()) - configuration = template.render(config_dict) - return configuration + return conf_from_dict(config_dict) +def gen_conf_re2o(re2o_config, header): + print(re2o_config.keys()) + raise RuntimeError() + config_dict = { + "header": header + } + return conf_from_dict(config_dict) +def connect_as_self(base_url, username=None): + """Get a re2o token for the current unix user """ + if username is None: + username = getpass.getuser() + r = requests.post( + base_url + "token-auth", + data={ + "username": username, + "password": getpass.getpass("Login to {} as {} with password :".format(base_url, username)) + } + ) + if r.status_code != 200: + logger.critical("Wrong login/password") + exit(1) + token = r.json().get("token") + return token +def get_switch_from_results(results, switch_name): + for s in results: + if s.get("short_name"): + return s + return None + +def get_switch_from_re2o(re2o_instance, switch_name, re2o_user): + base_url = "{}/api/".format(re2o_instance) + token = connect_as_self(base_url, re2o_user) + headers = {"Authorization": "Token " + token} + r = requests.get(base_url + "switchs/ports-config", headers=headers) + sw_config = get_switch_from_results(r.json()["results"], switch_name) + while r.json().get("next") and s is None: + r = requests.get(r.json().get("next"), headers=headers) + sw_config = get_switch_from_results(r.json()["results"], switch_name) + return sw_config if __name__ == "__main__": format_string = "%(asctime)s - %(levelname)s - %(message)s" @@ -186,26 +233,52 @@ if __name__ == "__main__": parser.add_argument("-w", "--whole", action="store_true", help="Affiche la configuration en entier au lieu du diff") parser.add_argument("switch_name", type=str, help="Génère le template de ce switch") parser.add_argument("-H", "--host", type=str, required=False, help="Host sur lequel de déployer la configuration au lieu de l'adresse dans le template") + parser.add_argument("-r", "--re2o", type=str, required=False, help="Si renseigné, la configuration sera récupérée depuis l'instance de re2o indiquée au lieu d'utiliser les fichiers yaml") + parser.add_argument("--re2o-user", type=str, required=False, help="Permet de choisir l'user pour se connecter à l'api re2o, par défaut on prend l'user unix courant") + parser.add_argument("-4", "--force-ipv4", action="store_true", help="Force le provisionning en ipv4") + args = parser.parse_args() - logger.debug("Loading master config") - master_config = yaml.load(open("configs/master.yml", "r"), yaml.Loader) - logger.debug("Loading config for {}".format(args.switch_name)) - switch_config = yaml.load(open("configs/switches/{}.yml".format(args.switch_name), "r"), yaml.Loader) + if args.re2o: + logger.debug("Loading master config") + master_config = yaml.load(open("configs/master.yml", "r"), yaml.Loader) + logger.debug("Loading config from re2o") + re2o_config = get_switch_from_re2o(args.re2o, args.switch_name, args.re2o_user) + else: + logger.debug("Loading master config") + master_config = yaml.load(open("configs/master.yml", "r"), yaml.Loader) + logger.debug("Loading config for {}".format(args.switch_name)) + switch_config = yaml.load(open("configs/switches/{}.yml".format(args.switch_name), "r"), yaml.Loader) if args.host: switch_address = args.host + elif args.re2o: + if args.force_ipv4: + switch_address = re2o_config.get("ipv4") + else: + switch_address = re2o_config.get("ipv6") or re2o_config.get("ipv4") else: - switch_address = switch_config.get("ipv6-addr") or switch_config.get("ipv4-addr") + if args.force_ipv4: + switch_address = switch_config.get("ipv4-addr") + else: + switch_address = switch_config.get("ipv6-addr") or switch_config.get("ipv4-addr") + if switch_address is None: - switch_address = "{}.switches.crans.org".format(args.switch_name) + switch_address = "{}.switches.crans.org".format(args.switch_name) #TODO: crans logger.info("Connecting to {} with address {}".format(args.switch_name, switch_address)) - session = connect_to_switch(switch_address, user="root", key=master_config.get("ssh_private_key")) + session = connect_to_switch(switch_address, user="root", key=master_config.get("ssh_private_key")) #TODO: spécifier chemin clef old_config = sftp_read_file(session, "cfg/running-config").decode("utf-8") + header = get_header(old_config) + # génération de la conf logging.info("Generating configuration for {}".format(args.switch_name)) - configuration = gen_conf(master_config, switch_config, old_config) + if args.re2o: + configuration = gen_conf_re2o(re2o_config, header) + else: + configuration = gen_conf(master_config, switch_config, header) + + # génération du diff for line in difflib.unified_diff(old_config.split("\n"), configuration.split("\n"), fromfile='origin', tofile='new', lineterm=""): if line.startswith("-"): termcolor.cprint(line, "red")