aboutsummaryrefslogtreecommitdiffstats
path: root/charms/trusty/neutron-contrail/hooks/neutron_contrail_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'charms/trusty/neutron-contrail/hooks/neutron_contrail_utils.py')
-rw-r--r--charms/trusty/neutron-contrail/hooks/neutron_contrail_utils.py478
1 files changed, 478 insertions, 0 deletions
diff --git a/charms/trusty/neutron-contrail/hooks/neutron_contrail_utils.py b/charms/trusty/neutron-contrail/hooks/neutron_contrail_utils.py
new file mode 100644
index 0000000..4bb8002
--- /dev/null
+++ b/charms/trusty/neutron-contrail/hooks/neutron_contrail_utils.py
@@ -0,0 +1,478 @@
+import functools
+import os
+import pwd
+import shutil
+from socket import gethostbyname, gethostname
+from subprocess import (
+ CalledProcessError,
+ check_call,
+ check_output
+)
+from time import sleep, time
+
+import apt_pkg
+import yaml
+
+import netaddr
+import netifaces
+
+from charmhelpers.core.hookenv import (
+ config,
+ log,
+ related_units,
+ relation_get,
+ relation_ids,
+ relation_type,
+ remote_unit
+)
+
+from charmhelpers.core.host import service_restart, service_start
+
+from charmhelpers.core.templating import render
+
+apt_pkg.init()
+
+def dpkg_version(pkg):
+ try:
+ return check_output(["dpkg-query", "-f", "${Version}\\n", "-W", pkg]).rstrip()
+ except CalledProcessError:
+ return None
+
+CONTRAIL_VERSION = dpkg_version("contrail-vrouter-agent")
+OPENSTACK_VERSION = dpkg_version("nova-compute")
+
+config = config()
+
+def retry(f=None, timeout=10, delay=2):
+ """Retry decorator.
+
+ Provides a decorator that can be used to retry a function if it raises
+ an exception.
+
+ :param timeout: timeout in seconds (default 10)
+ :param delay: retry delay in seconds (default 2)
+
+ Examples::
+
+ # retry fetch_url function
+ @retry
+ def fetch_url():
+ # fetch url
+
+ # retry fetch_url function for 60 secs
+ @retry(timeout=60)
+ def fetch_url():
+ # fetch url
+ """
+ if not f:
+ return functools.partial(retry, timeout=timeout, delay=delay)
+ @functools.wraps(f)
+ def func(*args, **kwargs):
+ start = time()
+ error = None
+ while True:
+ try:
+ return f(*args, **kwargs)
+ except Exception as e:
+ error = e
+ elapsed = time() - start
+ if elapsed >= timeout:
+ raise error
+ remaining = timeout - elapsed
+ if delay <= remaining:
+ sleep(delay)
+ else:
+ sleep(remaining)
+ raise error
+ return func
+
+def configure_vrouter():
+ # run external script to configure vrouter
+ args = ["./create-vrouter.sh"]
+ iface = config.get("vhost-interface")
+ if iface:
+ args.append(iface)
+ check_call(args, cwd="scripts")
+
+def contrail_api_ctx():
+ ip = config.get("contrail-api-ip")
+ if ip:
+ port = config.get("contrail-api-port")
+ return { "api_server": ip,
+ "api_port": port if port is not None else 8082 }
+
+ ctxs = [ { "api_server": gethostbyname(relation_get("private-address", unit, rid)),
+ "api_port": port }
+ for rid in relation_ids("contrail-api")
+ for unit, port in
+ ((unit, relation_get("port", unit, rid)) for unit in related_units(rid))
+ if port ]
+ return ctxs[0] if ctxs else {}
+
+def contrail_discovery_ctx():
+ ip = config.get("discovery-server-ip")
+ if ip:
+ return { "discovery_server": ip,
+ "discovery_port": 5998 }
+
+ ctxs = [ { "discovery_server": vip if vip \
+ else gethostbyname(relation_get("private-address", unit, rid)),
+ "discovery_port": port }
+ for rid in relation_ids("contrail-discovery")
+ for unit, port, vip in
+ ((unit, relation_get("port", unit, rid), relation_get("vip", unit, rid))
+ for unit in related_units(rid))
+ if port ]
+ return ctxs[0] if ctxs else {}
+
+@retry(timeout=300)
+def contrail_provision_linklocal(api_ip, api_port, service_name, service_ip,
+ service_port, fabric_ip, fabric_port, op,
+ user, password):
+ check_call(["contrail-provision-linklocal",
+ "--api_server_ip", api_ip,
+ "--api_server_port", str(api_port),
+ "--linklocal_service_name", service_name,
+ "--linklocal_service_ip", service_ip,
+ "--linklocal_service_port", str(service_port),
+ "--ipfabric_service_ip", fabric_ip,
+ "--ipfabric_service_port", str(fabric_port),
+ "--oper", op,
+ "--admin_user", user,
+ "--admin_password", password])
+
+@retry(timeout=300)
+def contrail_provision_vrouter(hostname, ip, api_ip, api_port, op,
+ user, password, tenant):
+ check_call(["contrail-provision-vrouter",
+ "--host_name", hostname,
+ "--host_ip", ip,
+ "--api_server_ip", api_ip,
+ "--api_server_port", str(api_port),
+ "--oper", op,
+ "--admin_user", user,
+ "--admin_password", password,
+ "--admin_tenant_name", tenant])
+
+def control_node_ctx():
+ return { "control_nodes": [ gethostbyname(relation_get("private-address", unit, rid))
+ for rid in relation_ids("control-node")
+ for unit in related_units(rid) ] }
+
+def disable_vrouter_vgw():
+ if os.path.exists("/etc/sysctl.d/60-vrouter-vgw.conf"):
+ # unset sysctl options
+ os.remove("/etc/sysctl.d/60-vrouter-vgw.conf")
+ check_call(["sysctl", "-qw", "net.ipv4.ip_forward=0"])
+
+def drop_caches():
+ """Clears OS pagecache"""
+ log("Clearing pagecache")
+ check_call(["sync"])
+ with open("/proc/sys/vm/drop_caches", "w") as f:
+ f.write("3\n")
+
+def enable_vrouter_vgw():
+ if not os.path.exists("/etc/sysctl.d/60-vrouter-vgw.conf"):
+ # set sysctl options
+ shutil.copy("files/60-vrouter-vgw.conf", "/etc/sysctl.d")
+ service_start("procps")
+
+def fix_nodemgr():
+ # add files missing from contrail-nodemgr package
+ shutil.copy("files/contrail-nodemgr-vrouter.ini",
+ "/etc/contrail/supervisord_vrouter_files")
+ pw = pwd.getpwnam("contrail")
+ os.chown("/etc/contrail/supervisord_vrouter_files/contrail-nodemgr-vrouter.ini",
+ pw.pw_uid, pw.pw_gid)
+ shutil.copy("files/contrail-vrouter.rules",
+ "/etc/contrail/supervisord_vrouter_files")
+ os.chown("/etc/contrail/supervisord_vrouter_files/contrail-vrouter.rules",
+ pw.pw_uid, pw.pw_gid)
+ shutil.copy("files/contrail-vrouter-nodemgr", "/etc/init.d")
+ os.chmod("/etc/init.d/contrail-vrouter-nodemgr", 0755)
+ service_restart("supervisor-vrouter")
+
+def fix_permissions():
+ os.chmod("/etc/contrail", 0755)
+ os.chown("/etc/contrail", 0, 0)
+
+def fix_vrouter_scripts():
+ # certain files need to be present for packages
+ if not os.path.exists("/opt/contrail/bin"):
+ os.makedirs("/opt/contrail/bin")
+ os.symlink("/bin/true", "/opt/contrail/bin/vrouter-pre-start.sh")
+ os.symlink("/bin/true", "/opt/contrail/bin/vrouter-post-start.sh")
+ os.symlink("/bin/true", "/opt/contrail/bin/vrouter-pre-stop.sh")
+
+def identity_admin_ctx():
+ ctxs = [ { "auth_host": gethostbyname(hostname),
+ "auth_port": relation_get("service_port", unit, rid),
+ "admin_user": relation_get("service_username", unit, rid),
+ "admin_password": relation_get("service_password", unit, rid),
+ "admin_tenant_name": relation_get("service_tenant_name", unit, rid),
+ "auth_region": relation_get("service_region", unit, rid) }
+ for rid in relation_ids("identity-admin")
+ for unit, hostname in
+ ((unit, relation_get("service_hostname", unit, rid)) for unit in related_units(rid))
+ if hostname ]
+ return ctxs[0] if ctxs else {}
+
+def ifdown(interfaces=None):
+ """ifdown an interface or all interfaces"""
+ log("Taking down {}".format(interfaces if interfaces else "interfaces"))
+ check_call(["ifdown"] + interfaces if interfaces else ["-a"])
+
+def ifup(interfaces=None):
+ """ifup an interface or all interfaces"""
+ log("Bringing up {}".format(interfaces if interfaces else "interfaces"))
+ check_call(["ifup"] + interfaces if interfaces else ["-a"])
+
+def lsmod(module):
+ """Check if a kernel module is loaded"""
+ with open("/proc/modules", "r") as modules:
+ for line in modules:
+ if line.split()[0] == module:
+ return True
+ return False
+
+def modprobe(module, auto_load=False, dkms_autoinstall=False):
+ """Load a kernel module.
+
+ Allows loading of a kernel module.
+
+ 'dkms_autoinstall' is useful for DKMS kernel modules. Juju often upgrades
+ units to newer kernels before charm install, which won't be used until the
+ machine is rebooted. In these cases, some modules may not be compiled for
+ the newer kernel. Setting this argument to True will ensure these modules
+ are compiled for newer kernels.
+
+ :param module: module to load
+ :param auto_load: load module on boot (default False)
+ :param dkms_autoinstall: invoke DKMS autoinstall for other kernels
+ (default False)
+ """
+ if not lsmod(module):
+ log("Loading kernel module {}".format(module))
+ check_call(["modprobe", module])
+ if auto_load:
+ with open("/etc/modules", "a") as modules:
+ modules.write(module)
+ modules.write("\n")
+ if dkms_autoinstall:
+ current = check_output(["uname", "-r"]).rstrip()
+ for kernel in os.listdir("/lib/modules"):
+ if kernel == current:
+ continue
+ log("DKMS auto installing for kernel {}".format(kernel))
+ check_call(["dkms", "autoinstall", "-k", kernel])
+
+def network_ctx():
+ iface = config.get("control-interface")
+ return { "control_network_ip": netifaces.ifaddresses(iface)[netifaces.AF_INET][0]["addr"] }
+
+def neutron_metadata_ctx():
+ if "local-metadata-secret" in config:
+ return { "metadata_secret": config["local-metadata-secret"] }
+
+ ctxs = [ { "metadata_secret": relation_get("shared-secret", unit, rid) }
+ for rid in relation_ids("neutron-metadata")
+ for unit in related_units(rid) ]
+ return ctxs[0] if ctxs else {}
+
+def provision_local_metadata():
+ api_port = None
+ api_ip = config.get("contrail-api-ip")
+ if api_ip:
+ api_port = config.get("contrail-api-port")
+ if api_port is None:
+ api_port = 8082
+ else:
+ api_ip, api_port = [ (gethostbyname(relation_get("private-address", unit, rid)),
+ port)
+ for rid in relation_ids("contrail-api")
+ for unit, port in
+ ((unit, relation_get("port", unit, rid)) for unit in related_units(rid))
+ if port ][0]
+ user, password = [ (relation_get("service_username", unit, rid),
+ relation_get("service_password", unit, rid))
+ for rid in relation_ids("identity-admin")
+ for unit in related_units(rid)
+ if relation_get("service_hostname", unit, rid) ][0]
+ log("Provisioning local metadata service 127.0.0.1:8775")
+ contrail_provision_linklocal(api_ip, api_port, "metadata",
+ "169.254.169.254", 80, "127.0.0.1", 8775,
+ "add", user, password)
+
+def provision_vrouter():
+ hostname = gethostname()
+ ip = netifaces.ifaddresses("vhost0")[netifaces.AF_INET][0]["addr"]
+ api_port = None
+ api_ip = config.get("contrail-api-ip")
+ if api_ip:
+ api_port = config.get("contrail-api-port")
+ if api_port is None:
+ api_port = 8082
+ else:
+ api_ip, api_port = [ (gethostbyname(relation_get("private-address", unit, rid)),
+ port)
+ for rid in relation_ids("contrail-api")
+ for unit, port in
+ ((unit, relation_get("port", unit, rid)) for unit in related_units(rid))
+ if port ][0]
+ user, password, tenant = [ (relation_get("service_username", unit, rid),
+ relation_get("service_password", unit, rid),
+ relation_get("service_tenant_name", unit, rid))
+ for rid in relation_ids("identity-admin")
+ for unit in related_units(rid)
+ if relation_get("service_hostname", unit, rid) ][0]
+ log("Provisioning vrouter {}".format(ip))
+ contrail_provision_vrouter(hostname, ip, api_ip, api_port, "add",
+ user, password, tenant)
+
+def remove_juju_bridge():
+ # run external script to remove bridge
+ check_call(["./remove-juju-bridge.sh"], cwd="scripts")
+
+def units(relation):
+ """Return a list of units for the specified relation"""
+ return [ unit for rid in relation_ids(relation)
+ for unit in related_units(rid) ]
+
+def unprovision_local_metadata():
+ relation = relation_type()
+ if relation and not remote_unit():
+ return
+ api_ip = config.previous("contrail-api-ip")
+ api_port = None
+ if api_ip:
+ api_port = config.previous("contrail-api-port")
+ if api_port is None:
+ api_port = 8082
+ elif relation == "contrail-api":
+ api_ip = gethostbyname(relation_get("private-address"))
+ api_port = relation_get("port")
+ else:
+ api_ip, api_port = [ (gethostbyname(relation_get("private-address", unit, rid)),
+ relation_get("port", unit, rid))
+ for rid in relation_ids("contrail-api")
+ for unit in related_units(rid) ][0]
+ user = None
+ password = None
+ if relation == "identity-admin":
+ user = relation_get("service_username")
+ password = relation_get("service_password")
+ else:
+ user, password = [ (relation_get("service_username", unit, rid),
+ relation_get("service_password", unit, rid))
+ for rid in relation_ids("identity-admin")
+ for unit in related_units(rid) ][0]
+ log("Unprovisioning local metadata service 127.0.0.1:8775")
+ contrail_provision_linklocal(api_ip, api_port, "metadata",
+ "169.254.169.254", 80, "127.0.0.1", 8775,
+ "del", user, password)
+
+def unprovision_vrouter():
+ relation = relation_type()
+ if relation and not remote_unit():
+ return
+ hostname = gethostname()
+ ip = netifaces.ifaddresses("vhost0")[netifaces.AF_INET][0]["addr"]
+ api_ip = config.previous("contrail-api-ip")
+ api_port = None
+ if api_ip:
+ api_port = config.previous("contrail-api-port")
+ if api_port is None:
+ api_port = 8082
+ elif relation == "contrail-api":
+ api_ip = gethostbyname(relation_get("private-address"))
+ api_port = relation_get("port")
+ else:
+ api_ip, api_port = [ (gethostbyname(relation_get("private-address", unit, rid)),
+ relation_get("port", unit, rid))
+ for rid in relation_ids("contrail-api")
+ for unit in related_units(rid) ][0]
+ user = None
+ password = None
+ tenant = None
+ if relation == "identity-admin":
+ user = relation_get("service_username")
+ password = relation_get("service_password")
+ tenant = relation_get("service_tenant_name")
+ else:
+ user, password, tenant = [ (relation_get("service_username", unit, rid),
+ relation_get("service_password", unit, rid),
+ relation_get("service_tenant_name", unit, rid))
+ for rid in relation_ids("identity-admin")
+ for unit in related_units(rid) ][0]
+ log("Unprovisioning vrouter {}".format(ip))
+ contrail_provision_vrouter(hostname, ip, api_ip, api_port, "del",
+ user, password, tenant)
+
+def vhost_gateway():
+ # determine vhost gateway
+ gateway = config.get("vhost-gateway")
+ if gateway == "auto":
+ for line in check_output(["route", "-n"]).splitlines()[2:]:
+ l = line.split()
+ if "G" in l[3] and l[7] == "vhost0":
+ return l[1]
+ gateway = None
+ return gateway
+
+def vhost_ip(iface):
+ # return a vhost formatted address and mask - x.x.x.x/xx
+ addr = netifaces.ifaddresses(iface)[netifaces.AF_INET][0]
+ ip = addr["addr"]
+ cidr = netaddr.IPNetwork(ip + "/" + addr["netmask"]).prefixlen
+ return ip + "/" + str(cidr)
+
+def vhost_phys():
+ # run external script to determine physical interface of vhost0
+ return check_output(["scripts/vhost-phys.sh"]).rstrip()
+
+def vrouter_ctx():
+ return { "vhost_ip": vhost_ip("vhost0"),
+ "vhost_gateway": vhost_gateway(),
+ "vhost_physical": vhost_phys() }
+
+def vrouter_vgw_ctx():
+ ctx = {}
+ vgws = config.get("virtual-gateways")
+ if vgws:
+ vgws = yaml.safe_load(vgws)
+ map(lambda item: item.update(domain="default-domain"), vgws)
+ ctx["vgws"] = vgws
+ return ctx
+
+def write_barbican_auth_config():
+ ctx = identity_admin_ctx()
+ render("contrail-barbican-auth.conf",
+ "/etc/contrail/contrail-barbican-auth.conf", ctx, "root", "contrail",
+ 0440)
+
+def write_nodemgr_config():
+ ctx = contrail_discovery_ctx()
+ render("contrail-vrouter-nodemgr.conf",
+ "/etc/contrail/contrail-vrouter-nodemgr.conf", ctx)
+
+def write_vnc_api_config():
+ ctx = {}
+ ctx.update(contrail_api_ctx())
+ ctx.update(identity_admin_ctx())
+ render("vnc_api_lib.ini", "/etc/contrail/vnc_api_lib.ini", ctx)
+
+def write_vrouter_config():
+ ctx = {}
+ ctx.update(control_node_ctx())
+ ctx.update(contrail_discovery_ctx())
+ ctx.update(neutron_metadata_ctx())
+ ctx.update(network_ctx())
+ ctx.update(vrouter_ctx())
+ ctx.update(vrouter_vgw_ctx())
+ render("contrail-vrouter-agent.conf",
+ "/etc/contrail/contrail-vrouter-agent.conf", ctx, perms=0440)
+
+def write_vrouter_vgw_interfaces():
+ ctx = vrouter_vgw_ctx()
+ render("vrouter-vgw.cfg", "/etc/network/interfaces.d/vrouter-vgw.cfg", ctx)