From 8362b816a74ac167e9a399a427b38e77e03eb377 Mon Sep 17 00:00:00 2001 From: Denis Meltsaykin Date: Wed, 22 Apr 2015 16:50:15 +0000 Subject: [PATCH] First version of jenkins' job to deploy cluster As for now this script can make VMs, Volumes and Networks in libvirt. Also it manages subnets in order to achieve zero crossing between them. Fuel master node gets dynamic network parameters from script by injection during bootloader stage. Public network being injected in Fuel-master node, so it can be accessible from network. Cluster's network is configured with apropriate values. Included changes in nailgun network & settings configuration. All data is visible in the summary message. Thanks devops team for scancodes.py Change-Id: Ia8cb28c8197560216b2f7c2d76689286821666b4 --- jenkins/build_cluster.py | 732 +++++++++++++++++++++++++++++++++++++++ jenkins/config.xml | 111 ++++++ jenkins/readme.txt | 78 +++++ jenkins/scancodes.py | 183 ++++++++++ 4 files changed, 1104 insertions(+) create mode 100755 jenkins/build_cluster.py create mode 100644 jenkins/config.xml create mode 100644 jenkins/readme.txt create mode 100644 jenkins/scancodes.py diff --git a/jenkins/build_cluster.py b/jenkins/build_cluster.py new file mode 100755 index 0000000..7420174 --- /dev/null +++ b/jenkins/build_cluster.py @@ -0,0 +1,732 @@ +#!/usr/bin/env python + +import os +import re +import sqlite3 +import subprocess +import sys +import time + +import libvirt +import netaddr + +import scancodes + +cfg = dict() +# required vars +cfg["ENV_NAME"] = os.getenv("ENV_NAME") +cfg["ISO_URL"] = os.getenv("ISO_URL") + +# networks defenition +cfg["ADMIN_NET"] = os.getenv("ADMIN_NET", "10.88.0.0/16") +cfg["PUBLIC_NET"] = os.getenv("PUBLIC_NET", "172.16.59.0/24") +cfg["PUB_SUBNET_SIZE"] = int(os.getenv("PUB_SUBNET_SIZE", 28)) +cfg["ADM_SUBNET_SIZE"] = int(os.getenv("ADM_SUBNET_SIZE", 28)) + +#DB +cfg["DB_FILE"] = os.getenv("DB_FILE", "build_cluster.db") + +#fuel node credentials +cfg["FUEL_SSH_USERNAME"] = os.getenv("FUEL_SSH_USERNAME", "root") +cfg["FUEL_SSH_PASSWORD"] = os.getenv("FUEL_SSH_PASSWORD", "r00tme") +cfg["KEYSTONE_USERNAME"] = os.getenv("KEYSTONE_USERNAME", "admin") +cfg["KEYSTONE_PASSWORD"] = os.getenv("KEYSTONE_PASSWORD", "admin") +cfg["KEYSTONE_TENANT"] = os.getenv("KEYSTONE_TENANT", "admin") + +#nodes settings +cfg["ADMIN_RAM"] = int(os.getenv("ADMIN_RAM", 4096)) +cfg["ADMIN_CPU"] = int(os.getenv("ADMIN_CPU", 2)) +cfg["SLAVE_RAM"] = int(os.getenv("SLAVE_RAM", 3072)) +cfg["SLAVE_CPU"] = int(os.getenv("SLAVE_CPU", 1)) +cfg["NODES_COUNT"] = int(os.getenv("NODES_COUNT", 5)) +cfg["NODES_DISK_SIZE"] = int(os.getenv("NODES_DISK_SIZE", 50)) + +cfg["STORAGE_POOL"] = os.getenv("STORAGE_POOL", "default") + +cfg["ISO_DIR"] = os.getenv("PWD") + "/" + os.getenv("ISO_DIR", "iso") + "/" +if cfg["ISO_URL"]: + cfg["ISO_PATH"] = cfg["ISO_DIR"] + cfg["ISO_URL"] \ + .split("/")[-1].split(".torrent")[0] + +cfg["PREPARE_CLUSTER"] = os.getenv("PREPARE_CLUSTER") +cfg["RELEASE"] = os.getenv("RELEASE") +cfg["HA"] = os.getenv("HA") +cfg["NETWORK_TYPE"] = os.getenv("NETWORK_TYPE") + +db = None + +try: + vconn = libvirt.open("qemu:///system") +except: + print ("\nERRROR: libvirt is inaccessible!") + sys.exit(10) + + +def initialize_database(): + """ This functions initializes DB + either by creating it or just opening + """ + global db + + db = sqlite3.Connection(cfg["DB_FILE"]) + cursor = db.cursor() + try: + cursor.execute( + "CREATE TABLE IF NOT EXISTS nets (" + "net TEXT, " + "env TEXT, " + "interface TEXT);" + ) + cursor.execute( + "CREATE TABLE IF NOT EXISTS envs (" + "env TEXT, " + "owner TEXT, " + "nodes_count INT, " + "admin_ram INT, " + "admin_cpu INT, " + "slave_ram INT, " + "slave_cpu INT, " + "deploy_type INT);" + ) + cursor.execute( + "CREATE TABLE IF NOT EXISTS disks (" + "env TEXT, " + "node TEXT, " + "filename TEXT);" + ) + except: + print ("Unable to open/create database {0}".format(cfg["DB_FILE"])) + sys.exit(5) + + +def pprint_dict(subj): + if not isinstance(subj, dict): + return False + for k, v in sorted(subj.items()): + print (" {0:20}: {1}".format(k, v)) + + +def env_is_available(): + cursor = db.cursor() + cursor.execute( + "SELECT * FROM nets WHERE env='{0}';".format(cfg["ENV_NAME"]) + ) + + if cursor.fetchone() is None: + return True + else: + return False + + +def get_free_subnet_from_libvirt(): + occupied_nets = set() + for net in vconn.listAllNetworks(): + res = re.findall("", + net.XMLDesc()) + try: + occupied_nets.add(netaddr.IPNetwork( + "{0}/{1}".format(res[0][0], res[0][1]))) + except IndexError: + pass + + admin_subnets = set( + x for x in netaddr.IPNetwork(cfg["ADMIN_NET"]) + .subnet(cfg["ADM_SUBNET_SIZE"]) + if x not in occupied_nets + ) + public_subnets = set( + x for x in netaddr.IPNetwork(cfg["PUBLIC_NET"]) + .subnet(cfg["PUB_SUBNET_SIZE"]) + if x not in occupied_nets + ) + + if not admin_subnets or not public_subnets: + print ("\nERROR: No more NETWORKS to associate!") + return False + + cfg["ADMIN_SUBNET"] = sorted(admin_subnets)[0] + cfg["PUBLIC_SUBNET"] = sorted(public_subnets)[0] + print ( + "Following subnets will be used:\n" + " ADMIN_SUBNET: {0}\n" + " PUBLIC_SUBNET: {1}\n".format(cfg["ADMIN_SUBNET"], + cfg["PUBLIC_SUBNET"]) + ) + sql_query = [ + (str(cfg["ADMIN_SUBNET"]), str(cfg["ENV_NAME"]), + str(cfg["ENV_NAME"] + "_adm")), + (str(cfg["PUBLIC_SUBNET"]), str(cfg["ENV_NAME"]), + str(cfg["ENV_NAME"] + "_pub")) + ] + print sql_query + cursor = db.cursor() + cursor.executemany("INSERT INTO nets VALUES (?,?,?)", sql_query) + db.commit() + return True + + +def download_iso(): + try: + os.makedirs(cfg["ISO_DIR"]) + except os.error as err: + if err.args[0] != 17: + print ("Error during creating directory {0}: {1}".format( + cfg["ISO_DIR"], err.args[1])) + sys.exit(15) + + cmd = ["aria2c", "-d", cfg["ISO_DIR"], "--seed-time=0", + "--allow-overwrite=true", "--force-save=true", + "--auto-file-renaming=false", "--allow-piece-length-change=true", + "--log-level=error", cfg["ISO_URL"]] + + proc = subprocess.Popen( + cmd, + stdin=None, + stdout=None, + stderr=None, + bufsize=1 + ) + proc.wait() + + if proc.returncode == 0: + print("\nISO successfuly downloaded") + else: + print("\nERROR: Cannot download ISO") + sys.exit(20) + + +def register_env(): + cursor = db.cursor() + cursor.execute( + "INSERT INTO envs VALUES ('{0}','{1}',{2},{3},{4},{5},{6},{7});" + .format(cfg["ENV_NAME"], "nobody", cfg["NODES_COUNT"], + cfg["ADMIN_RAM"], cfg["ADMIN_CPU"], cfg["SLAVE_RAM"], + cfg["SLAVE_CPU"], 1) + ) + db.commit() + + +def define_nets(): + network_xml_template = \ + "\n" \ + " {net_name}\n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" + net_name = cfg["ENV_NAME"]+"_adm" + ip_addr = str(cfg["ADMIN_SUBNET"].ip + 1) + subnet = cfg["ADMIN_SUBNET"].prefixlen + + net_xml = network_xml_template.format(net_name=net_name, + ip_addr=ip_addr, + subnet=subnet) + + print ("Prepared admin_net xml:\n\n{0}".format(net_xml)) + + try: + cfg["ADM_SUBNET_OBJ"] = vconn.networkCreateXML(net_xml) + except: + print ("\nERROR: Unable to create admin subnet in libvirt!") + sys.exit(11) + + net_name = cfg["ENV_NAME"]+"_pub" + ip_addr = str(cfg["PUBLIC_SUBNET"].ip + 1) + subnet = cfg["PUBLIC_SUBNET"].prefixlen + + net_xml = network_xml_template.format(net_name=net_name, + ip_addr=ip_addr, + subnet=subnet) + + print ("Prepared public_net xml:\n\n{0}".format(net_xml)) + + try: + cfg["PUB_SUBNET_OBJ"] = vconn.networkCreateXML(net_xml) + except: + print ("\nERROR: Unable to create public subnet in libvirt!") + sys.exit(11) + + print ("Networks have been successfuly created.") + + +def volume_create(name): + vol_template = \ + "\n" \ + " {vol_name}.img\n" \ + " 0\n" \ + " {vol_size}\n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" + try: + pool = vconn.storagePoolLookupByName(cfg["STORAGE_POOL"]) + except: + print("\nERROR: libvirt`s storage pool '{0}' is not accessible!" + .format(cfg["STORAGE_POOL"])) + sys.exit(12) + + volume = vol_template.format(vol_name=name, + vol_size=cfg["NODES_DISK_SIZE"]) + + try: + vol_object = pool.createXML(volume) + except: + print("\nERROR: unable to create volume '{0}'!" + .format(name)) + sys.exit(13) + print("Created volume from XML:\n\n{0}".format(volume)) + return vol_object + + +def define_nodes(): + pass + + +def start_node(name, admin=False): + vol_obj = volume_create(name) + + node_template_xml = """ + + {name} + {memory} + {memory} + {vcpu} + + hvm + + + + + + + + + + + + + + + destroy + restart + destroy + + + + + + +{iso} + + +
+ + + + + + +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +