From 3cd01f1099af94b9350b4b48e6dc008f90c6ead8 Mon Sep 17 00:00:00 2001 From: plumgrid Date: Tue, 9 Aug 2016 01:07:49 -0400 Subject: [PATCH] Solutions API changes Signed-off-by: plumgrid --- actions.yaml | 8 ++ actions/actions.py | 65 +++++++++++++ actions/post-ips | 1 + actions/post-license | 1 + actions/post-zone-info | 1 + actions/restart-pg | 1 + config.yaml | 11 +++ hooks/pg_dir_context.py | 24 +++++ hooks/pg_dir_hooks.py | 30 +++++- hooks/pg_dir_utils.py | 154 ++++++++++++++++++++++++++++++- hooks/plumgrid-relation-changed | 1 + hooks/plumgrid-relation-departed | 1 + unit_tests/test_pg_dir_hooks.py | 3 +- 13 files changed, 293 insertions(+), 8 deletions(-) create mode 100644 actions.yaml create mode 100755 actions/actions.py create mode 120000 actions/post-ips create mode 120000 actions/post-license create mode 120000 actions/post-zone-info create mode 120000 actions/restart-pg create mode 120000 hooks/plumgrid-relation-changed create mode 120000 hooks/plumgrid-relation-departed diff --git a/actions.yaml b/actions.yaml new file mode 100644 index 0000000..c33671a --- /dev/null +++ b/actions.yaml @@ -0,0 +1,8 @@ +restart-pg: + description: Restart the plumgrid-director unit's service. +post-ips: + description: Post PLUMgrid nodes IPs to Solutions API server. +post-zone-info: + description: Post Zone info to Solutions API server. +post-license: + description: Post PLUMgrid License to Solutions API server. diff --git a/actions/actions.py b/actions/actions.py new file mode 100755 index 0000000..f1163d5 --- /dev/null +++ b/actions/actions.py @@ -0,0 +1,65 @@ +#!/usr/bin/python + +import os +import sys + +sys.path.append('hooks/') + +from charmhelpers.core.hookenv import action_fail +from pg_dir_utils import ( + restart_pg, + sapi_post_zone_info, + sapi_post_license, + sapi_post_ips +) + + +def restart_pg(args): + """ + Restart PLUMgrid services. + """ + restart_pg() + + +def post_ips(args): + """ + POST plumgrid nodes IPs to solutions api server. + """ + sapi_post_ips() + + +def post_zone_info(args): + """ + POST zone information to solutions api server + """ + sapi_post_zone_info() + + +def post_license(args): + """ + POST PLUMgrid License to solutions api server + """ + sapi_post_license() + + +# A dictionary of all the defined actions to callables (which take +# parsed arguments). +ACTIONS = {"restart-pg": restart_pg, "post-ips": post_ips, "post-zone-info": post_zone_info, + "post-license": post_license} + + +def main(args): + action_name = os.path.basename(args[0]) + try: + action = ACTIONS[action_name] + except KeyError: + return "Action %s undefined" % action_name + else: + try: + action(args) + except Exception as e: + action_fail(str(e)) + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/actions/post-ips b/actions/post-ips new file mode 120000 index 0000000..405a394 --- /dev/null +++ b/actions/post-ips @@ -0,0 +1 @@ +actions.py \ No newline at end of file diff --git a/actions/post-license b/actions/post-license new file mode 120000 index 0000000..405a394 --- /dev/null +++ b/actions/post-license @@ -0,0 +1 @@ +actions.py \ No newline at end of file diff --git a/actions/post-zone-info b/actions/post-zone-info new file mode 120000 index 0000000..405a394 --- /dev/null +++ b/actions/post-zone-info @@ -0,0 +1 @@ +actions.py \ No newline at end of file diff --git a/actions/restart-pg b/actions/restart-pg new file mode 120000 index 0000000..405a394 --- /dev/null +++ b/actions/restart-pg @@ -0,0 +1 @@ +actions.py \ No newline at end of file diff --git a/config.yaml b/config.yaml index 44de013..9b9dea1 100644 --- a/config.yaml +++ b/config.yaml @@ -56,3 +56,14 @@ options: default: 127.0.0.1 type: string description: IP address of the PLUMgrid Operations VM Management interface. + lcm-ip: + type: string + description: IP used by Solutions API to get/post cloud information. + sapi-port: + default: 8099 + type: int + description: Port used by Solutions API to get/post cloud information. + sapi-zone: + default: pgzone + type: string + description: Zone name used by Solutions API to get/post cloud information. diff --git a/hooks/pg_dir_context.py b/hooks/pg_dir_context.py index 1a03fe9..58453fb 100644 --- a/hooks/pg_dir_context.py +++ b/hooks/pg_dir_context.py @@ -26,6 +26,30 @@ from socket import ( ) +def _pg_edge_ips(): + ''' + Inspects edge-peer relation and returns the + ips of the edge nodes + ''' + return [get_host_ip(rdata['private-address']) + for rid in relation_ids("plumgrid") + for rdata in + (relation_get(rid=rid, unit=unit) for unit in related_units(rid)) + if 'edge-peer' in rdata] + + +def _pg_gateway_ips(): + ''' + Inspects gateway-peer relation and returns the + ips of the gateway nodes + ''' + return [get_host_ip(rdata['private-address']) + for rid in relation_ids("plumgrid") + for rdata in + (relation_get(rid=rid, unit=unit) for unit in related_units(rid)) + if 'gateway-peer' in rdata] + + def _pg_dir_ips(): ''' Inspects plumgrid-director peer relation and returns the diff --git a/hooks/pg_dir_hooks.py b/hooks/pg_dir_hooks.py index 20d9f39..809334d 100755 --- a/hooks/pg_dir_hooks.py +++ b/hooks/pg_dir_hooks.py @@ -9,7 +9,6 @@ import sys import time from charmhelpers.core.host import service_running from charmhelpers.contrib.network.ip import is_ip - from charmhelpers.core.hookenv import ( Hooks, UnregisteredHookError, @@ -42,7 +41,10 @@ from pg_dir_utils import ( restart_on_change, director_cluster_ready, configure_pg_sources, - configure_analyst_opsvm + configure_analyst_opsvm, + sapi_post_ips, + sapi_post_license, + sapi_post_zone_info ) hooks = Hooks() @@ -67,6 +69,7 @@ def install(): @hooks.hook('director-relation-joined') +@hooks.hook('director-relation-changed') @restart_on_change(restart_map()) def dir_joined(): ''' @@ -77,16 +80,24 @@ def dir_joined(): CONFIGS.write_all() -@hooks.hook('plumgrid-relation-joined') +@hooks.hook('plumgrid-relation-joined', + 'plumgrid-relation-changed', + 'plumgrid-relation-departed') def plumgrid_joined(relation_id=None): ''' This hook is run when relation with edge or gateway is created. ''' opsvm_ip = config('opsvm-ip') if not is_ip(opsvm_ip): - raise ValueError('Incorrect OPSVM IP specified') + raise ValueError('Invalid OPSVM IP specified!') else: relation_set(relation_id=relation_id, opsvm_ip=opsvm_ip) + if is_leader(): + if is_ip(config('lcm-ip')): + sapi_post_ips() + sapi_post_zone_info() + else: + raise ValueError('Invalid LCM IP specified!') @hooks.hook('plumgrid-configs-relation-joined') @@ -117,6 +128,8 @@ def config_changed(): if charm_config.changed('plumgrid-license-key'): if is_leader() and post_pg_license(): log("PLUMgrid License Posted") + # Post PG license to Sol-API + sapi_post_license() if charm_config.changed('fabric-interfaces'): if not fabric_interface_changed(): log("Fabric interface already set") @@ -124,6 +137,8 @@ def config_changed(): stop_pg() if charm_config.changed('plumgrid-virtual-ip'): CONFIGS.write_all() + for rid in relation_ids('plumgrid'): + plumgrid_joined(rid) stop_pg() for rid in relation_ids('plumgrid-configs'): plumgrid_configs_joined(rid) @@ -149,6 +164,12 @@ def config_changed(): for rid in relation_ids('plumgrid'): plumgrid_joined(rid) stop_pg() + # TODO + if (charm_config.changed('sapi-port') or + charm_config.changed('lcm-ip') or + charm_config.changed('sapi-zone')): + for rid in relation_ids('plumgrid'): + plumgrid_joined(rid) ensure_mtu() CONFIGS.write_all() if not service_running('plumgrid'): @@ -161,6 +182,7 @@ def start(): This hook is run when the charm is started. ''' configure_analyst_opsvm() + sapi_post_zone_info() if config('plumgrid-license-key') is not None: count = 0 while (count < 10): diff --git a/hooks/pg_dir_utils.py b/hooks/pg_dir_utils.py index 2be390a..4aa9fc1 100644 --- a/hooks/pg_dir_utils.py +++ b/hooks/pg_dir_utils.py @@ -24,6 +24,8 @@ from charmhelpers.contrib.network.ip import ( get_bridges, get_bridge_nics, is_ip, + get_iface_addr, + get_host_ip ) from charmhelpers.core.host import ( service_start, @@ -39,6 +41,11 @@ from charmhelpers.fetch import ( from charmhelpers.contrib.openstack.utils import ( os_release, ) +from pg_dir_context import ( + _pg_dir_ips, + _pg_edge_ips, + _pg_gateway_ips +) SOURCES_LIST = '/etc/apt/sources.list' LXC_CONF = '/etc/libvirt/lxc.conf' @@ -109,8 +116,8 @@ def configure_analyst_opsvm(): ''' if not service_running('plumgrid'): restart_pg() - NS_ENTER = ('/opt/local/bin/nsenter -t $(ps ho pid --ppid ' - '$(cat /var/run/libvirt/lxc/plumgrid.pid)) -m -n -u -i -p ') + NS_ENTER = ('/opt/local/bin/nsenter -t $(ps ho pid --ppid $(cat ' + '/var/run/libvirt/lxc/plumgrid.pid)) -m -n -u -i -p ') sigmund_stop = NS_ENTER + '/usr/bin/service plumgrid-sigmund stop' sigmund_status = NS_ENTER \ + '/usr/bin/service plumgrid-sigmund status' @@ -248,7 +255,13 @@ def get_mgmt_interface(): ''' mgmt_interface = config('mgmt-interface') if not mgmt_interface: - return get_iface_from_addr(unit_get('private-address')) + try: + return get_iface_from_addr(unit_get('private-address')) + except: + for bridge_interface in get_bridges(): + if (get_host_ip(unit_get('private-address')) + in get_iface_addr(bridge_interface)): + return bridge_interface elif interface_exists(mgmt_interface): return mgmt_interface else: @@ -398,6 +411,141 @@ def post_pg_license(): return 1 +def sapi_post_ips(): + pg_edge_ips = _pg_edge_ips() + pg_dir_ips = _pg_dir_ips() + pg_gateway_ips = _pg_gateway_ips() + pg_dir_ips.append(get_host_ip(unit_get('private-address'))) + pg_edge_ips = '"edge_ips"' + ':' \ + + '"{}"'.format(','.join(str(i) for i in pg_edge_ips)) + pg_dir_ips = '"director_ips"' + ':' \ + + '"{}"'.format(','.join(str(i) for i in pg_dir_ips)) + pg_gateway_ips = '"gateway_ips"' + ':' \ + + '"{}"'.format(','.join(str(i) for i in pg_gateway_ips)) + opsvm_ip = '"opsvm_ip"' + ':' + '"{}"'.format(config('opsvm-ip')) + virtual_ip = '"virtual_ip"' + ':' \ + + '"{}"'.format(config('plumgrid-virtual-ip')) + JSON_IPS = ','.join([pg_dir_ips, pg_edge_ips, pg_gateway_ips, + opsvm_ip, virtual_ip]) + status = ( + 'curl -H \'Content-Type: application/json\' -X ' + 'PUT -d \'{{{0}}}\' http://{1}' + ':' + '{2}/v1/zones/{3}/allIps' + ).format(JSON_IPS, config('lcm-ip'), config('sapi-port'), + config('sapi-zone')) + print "POST_IPS {}".format(status) + if 'success' in _exec_cmd_output( + status, + 'No response from specified LCM IP!'): + log('Successfully posted Zone IPs to Solutions API server!') + + +def _exec_cmd_output(cmd=None, error_msg='Command exited with ERRORs', + fatal=False): + ''' + Function to get output from bash command executed on the node. + ''' + if cmd is None: + log("No command specified") + else: + if fatal: + return subprocess.check_output(cmd, shell=True) + else: + try: + return subprocess.check_output(cmd, shell=True) + except subprocess.CalledProcessError: + log(error_msg) + return None + + +def sapi_post_license(): + username = '"user_name":' + '"{}"'.format(config('plumgrid-username')) + password = '"password":' + '"{}"'.format(config('plumgrid-password')) + license = '"license":' + '"{}"'.format(config('plumgrid-license-key')) + JSON_LICENSE = ','.join([username, password, license]) + status = ( + 'curl -H \'Content-Type: application/json\' -X ' + 'PUT -d \'{{{0}}}\' http://{1}' + ':' + '{2}/v1/zones/{3}/pgLicense' + ).format(JSON_LICENSE, config('lcm-ip'), config('sapi-port'), + config('sapi-zone')) + print "POST_LICENSE status: {}".format(status) + if 'success' in _exec_cmd_output( + status, + 'No response from specified LCM IP!'): + log('Successfully posted license file for zone "{}"!' + .format(config('sapi-zone'))) + + +def sapi_post_zone_info(): + sol_name = '"solution_name":"Ubuntu OpenStack"' + sol_version = '"solution_version":"12"' + pg_ons_version = _exec_cmd_output( + 'dpkg -l | grep plumgrid | awk \'{print $3}\' | ' + 'sed \'s/-/./\' | cut -f1 -d"-"', + 'Unable to obtain PG ONS version' + ).replace('\n', '') + pg_ons_version = \ + '"pg_ons_version":"{}"'.format(pg_ons_version) + hypervisor = '"hypervisor":"Ubuntu"' + hypervisor_version = \ + _exec_cmd_output('lsb_release -r | awk \'{print $2}\'', + 'Unable to obtain solution version' + ).replace('\n', '') + hypervisor_version = '"hypervisor_version":"{}"' \ + .format(hypervisor_version) + kernel_version = _exec_cmd_output( + 'uname -r', + 'Unable to obtain kernal version').replace('\n', '') + kernel_version = \ + '"kernel_version":"{}"'.format(kernel_version) + cloudapex_path = '/var/lib/libvirt/filesystems/plumgrid/' \ + 'opt/pg/web/cloudApex/modules/appCloudApex' \ + '/appCloudApex.js' + if os.path.isfile(cloudapex_path): + pg_cloudapex_version = 'cat ' \ + + '{}'.format(cloudapex_path) \ + + ' | grep -i appversion | awk \'{print $2}\'' + pg_cloudapex_version = \ + _exec_cmd_output(pg_cloudapex_version, + 'Unable to retrieve CloudApex version' + ).replace('\n', '') + else: + log('CloudApex not installed!') + pg_cloudapex_version = '' + pg_cloudapex_version = \ + '"pg_cloudapex_version":"{}"'.format(pg_cloudapex_version) + JSON_ZONE_INFO = ','.join([ + sol_name, + sol_version, + pg_ons_version, + hypervisor, + hypervisor_version, + kernel_version, + pg_cloudapex_version, + ]) + status = ( + 'curl -H \'Content-Type: application/json\' -X ' + 'PUT -d \'{{{0}}}\' http://{1}:{2}/v1/zones/{3}/zoneinfo' + ).format(JSON_ZONE_INFO, config('lcm-ip'), config('sapi-port'), + config('sapi-zone')) + print "ZONE_INFO status = {}".format(status) + if 'success' in _exec_cmd_output( + status, + 'No response from specified LCM IP!'): + log('Successfully posted Zone IPs to Solutions API server!') + + +def is_zone_info_available(): + zone_info = 'curl -H "Content-Type: application/json" -X GET '\ + + 'http://{}:8099/v1/zones/pgzone/zoneInfo'.format(config('lcm-ip')) + try: + status = subprocess.check_output(zone_info, shell=True) + if "success" in status: + return True + except: + log('No response from specified LCM IP') + return False + + def load_iptables(): ''' Loads iptables rules to allow all PLUMgrid communication. diff --git a/hooks/plumgrid-relation-changed b/hooks/plumgrid-relation-changed new file mode 120000 index 0000000..6530c5a --- /dev/null +++ b/hooks/plumgrid-relation-changed @@ -0,0 +1 @@ +pg_dir_hooks.py \ No newline at end of file diff --git a/hooks/plumgrid-relation-departed b/hooks/plumgrid-relation-departed new file mode 120000 index 0000000..6530c5a --- /dev/null +++ b/hooks/plumgrid-relation-departed @@ -0,0 +1 @@ +pg_dir_hooks.py \ No newline at end of file diff --git a/unit_tests/test_pg_dir_hooks.py b/unit_tests/test_pg_dir_hooks.py index 45146ed..5d7de5d 100644 --- a/unit_tests/test_pg_dir_hooks.py +++ b/unit_tests/test_pg_dir_hooks.py @@ -33,7 +33,8 @@ TO_PATCH = [ 'config', 'load_iptables', 'status_set', - 'configure_analyst_opsvm' + 'configure_analyst_opsvm', + 'sapi_post_zone_info' ] NEUTRON_CONF_DIR = "/etc/neutron"