import ipaddress import jinja2 import os import subprocess class Shell(): @staticmethod def run_shell_command(cmd): p = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={}) out, err = p.communicate() if p.returncode != 0: raise Exception("Error running shell command:\n%s\n%s" % (out, err)) return out, err class Jinja(): @staticmethod def render(template_name, **kwargs): cwd = os.path.dirname(os.path.abspath(__file__)) loader = jinja2.FileSystemLoader([".", "/"]) extensions = ["jinja2.ext.with_", "jinja2.ext.loopcontrols"] environment = jinja2.Environment(trim_blocks = True, lstrip_blocks = True, extensions = extensions, loader = loader) template_names = ["%s/jinja/%s" % (cwd, template_name)] template = environment.select_template(template_names) return template.render(kwargs) class RawQuery(): @staticmethod def dict_fetchall(cursor): rows = cursor.fetchall() if len(rows) == 0: return None else: columns = [col[0] for col in cursor.description] return [dict(zip(columns, row)) for row in rows] @staticmethod def query_single_value(cursor, query, column_name): cursor.execute(query) rows = RawQuery.dict_fetchall(cursor) if rows == {}: return None else: return rows[0][column_name] class Address(): @staticmethod def coalesce(ip, default): if ip is None: return default else: return ip @staticmethod def get_quads(addr): return list(filter(lambda x: x, str(addr).split("/")[0].split(":"))) @staticmethod def address_add(ip, index): return ipaddress.IPv6Address(ip) + index """ Assumes a 5-quad 'subnet' as import input. Adds the 6th quad which is always 40a. """ @staticmethod def add_40a(subnet): return Address.address_add(subnet, 0x040a00000000) @staticmethod def next_address(ip, subnet, block_begin_hex): beginning_address = Address.coalesce(ip, Address.address_add(subnet, block_begin_hex)) if ip is None: return beginning_address else: return Address.address_add(beginning_address, 0x1) @staticmethod def add_cidr(subnet, cidr = 64): subnet_quads = Address.get_quads(subnet) return "%s::/%s" % (":".join(subnet_quads), cidr) @staticmethod def remove_cidr(subnet): return ipaddress.IPv6Address(str(subnet).split("/")[0]) @staticmethod def gateway_to_subnet(gateway, numquads = 5): # The subnet is always the first 5 quads of the gateway. # TODO: make sure this holds true in MTCE and labs as well as production. quads = Address.get_quads(gateway) return ipaddress.IPv6Address("%s::" % ":".join(quads[:numquads])) @staticmethod def gateway_to_subnet_new(gateway): quads = Address.get_quads(gateway) return ipaddress.IPv6Address("%s:40a::" % ":".join(quads[:5])) @staticmethod def subnet_to_4_quads(subnet): quads = Address.get_quads(gateway) return ipaddress.IPv6Address("%s::" % ":".join(quads[:4])) @staticmethod def strip_brackets(ip): return ip.replace("[", "").replace("]", "") class Hostname(): @staticmethod def make_oam_hostname(ilo_hostname): return ilo_hostname.replace("-pe0", "-pe2") """ Note: this method does not deal with the index at the end of the hostname. Doing so would require database access. This is the responsibility of the service classes. See dnsservice.py """ @staticmethod def make_vip_hostname(ilo_hostname, cluster_name): index = int(cluster_name[-3:]) return "%s-%s" % (ilo_hostname.replace("-pe0", "-pe1")[:-4], f"{index + 0:03}") @staticmethod def increment_hostname(hostname): index = int(hostname[-3:]) return "%s-%s" % (hostname[:-4], f"{index + 1:03}")