Merge lp:~junaidali/charms/trusty/plumgrid-director/oil-sapi-changes into lp:charms/trusty/plumgrid-director

Proposed by Junaid Ali
Status: Needs review
Proposed branch: lp:~junaidali/charms/trusty/plumgrid-director/oil-sapi-changes
Merge into: lp:charms/trusty/plumgrid-director
Diff against target: 1050 lines (+649/-94)
12 files modified
Makefile (+2/-3)
README.md (+3/-2)
actions.yaml (+8/-2)
actions/actions.py (+36/-7)
bin/charm_helpers_sync.py (+253/-0)
config.yaml (+24/-0)
hooks/install (+20/-0)
hooks/pg_dir_context.py (+24/-0)
hooks/pg_dir_hooks.py (+37/-17)
hooks/pg_dir_utils.py (+235/-62)
metadata.yaml (+3/-0)
unit_tests/test_pg_dir_hooks.py (+4/-1)
To merge this branch: bzr merge lp:~junaidali/charms/trusty/plumgrid-director/oil-sapi-changes
Reviewer Review Type Date Requested Status
James Page Pending
Review via email: mp+308654@code.launchpad.net

Description of the change

Added charm series in metadata.yaml file
Updated README file
Added Solutions API support for PG-ONS 6.X.X
Added actions
Wrapper to deal with Ubuntu versions don't have py2 installed
Edge and gateway relations will also be used to get node's IPs
Catalyst OPSVM changes

To post a comment you must log in.

Unmerged revisions

22. By Junaid Ali

Changes:
  Added charm series in metadata.yaml file
  Updated README file
  Added Solutions API support for PG-ONS 6.X.X
  Added actions
  Wrapper to deal with Ubuntu versions don't have py2 installed
  Edge and gateway relations will also be used to get node's IPs
  Catalyst OPSVM changes

21. By Junaid Ali

Changes:
  Added charm series in metadata.yaml file
  Updated README file
  Added Solutions API support for PG-ONS 6.X.X
  Added actions
  Wrapper to deal with Ubuntu versions don't have py2 installed
  Edge and gateway relations will also be used to get node's IPs
  Catalyst OPSVM changes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2016-05-25 16:30:35 +0000
3+++ Makefile 2016-10-17 17:28:49 +0000
4@@ -4,12 +4,11 @@
5 virtualenv:
6 virtualenv .venv
7 .venv/bin/pip install flake8 nose coverage mock pyyaml netifaces \
8- netaddr jinja2 pyflakes pep8 six pbr funcsigs psutil charm-tools \
9- simplejson
10+ netaddr jinja2 pyflakes pep8 six pbr funcsigs psutil
11
12 lint: virtualenv
13 .venv/bin/flake8 --exclude hooks/charmhelpers hooks unit_tests tests --ignore E402
14- @.venv/bin/charm-proof
15+ @charm proof
16
17 unit_test: virtualenv
18 @echo Starting tests...
19
20=== modified file 'README.md'
21--- README.md 2016-03-03 20:56:40 +0000
22+++ README.md 2016-10-17 17:28:49 +0000
23@@ -26,7 +26,7 @@
24
25 # Known Limitations and Issues
26
27-This is an early access version of the PLUMgrid Director charm and it is not meant for production deployments. The charm only supports Kilo Openstack Release.
28+This charm currently is in an initial phase in supporting Ubuntu 16.04.
29
30 # Configuration
31
32@@ -58,4 +58,5 @@
33 # Contact Information
34
35 Bilal Baqar <bbaqar@plumgrid.com>
36-Bilal Ahmad <bilal@plumgrid.com>
37+Javeria Khan <javeriak@plumgrid.com>
38+Junaid Ali <junaidali@plumgrid.com>
39
40=== modified file 'actions.yaml'
41--- actions.yaml 2016-07-27 09:39:41 +0000
42+++ actions.yaml 2016-10-17 17:28:49 +0000
43@@ -1,2 +1,8 @@
44-restart:
45- description: Restart the plumgrid-director unit. This action will restart related services.
46+restart-pg-service:
47+ description: Restart the plumgrid-director unit's service.
48+sapi-post-ips:
49+ description: Post PLUMgrid nodes IPs to Solutions API server.
50+sapi-post-zone-info:
51+ description: Post PLUMgrid Zone info to Solutions API server.
52+sapi-post-license:
53+ description: Post PLUMgrid License to Solutions API server.
54
55=== modified file 'actions/actions.py'
56--- actions/actions.py 2016-07-27 09:39:41 +0000
57+++ actions/actions.py 2016-10-17 17:28:49 +0000
58@@ -7,20 +7,49 @@
59
60 from charmhelpers.core.hookenv import action_fail
61 from pg_dir_utils import (
62- restart_pg
63+ restart_pg,
64+ sapi_post_zone_info,
65+ sapi_post_license,
66+ sapi_post_ips
67 )
68
69
70-def restart(args):
71- """Pause the Ceilometer services.
72- @raises Exception should the service fail to stop.
73- """
74- restart_pg('lxc')
75+def restart_pg_service(args):
76+ """
77+ Restart PLUMgrid services.
78+ """
79+ restart_pg()
80+
81+
82+def post_ips(args):
83+ """
84+ POST PLUMgrid nodes IPs to solutions api server.
85+ """
86+ sapi_post_ips()
87+
88+
89+def post_zone_info(args):
90+ """
91+ POST PLUMgrid zone information to solutions api server
92+ """
93+ sapi_post_zone_info()
94+
95+
96+def post_license(args):
97+ """
98+ POST PLUMgrid License key to solutions api server
99+ """
100+ sapi_post_license()
101
102
103 # A dictionary of all the defined actions to callables (which take
104 # parsed arguments).
105-ACTIONS = {"restart": restart}
106+ACTIONS = {
107+ "restart-pg-service": restart_pg_service,
108+ "sapi-post-ips": post_ips,
109+ "sapi-post-zone-info": post_zone_info,
110+ "sapi-post-license": post_license
111+}
112
113
114 def main(args):
115
116=== removed symlink 'actions/restart'
117=== target was u'actions.py'
118=== added symlink 'actions/restart-pg-service'
119=== target is u'actions.py'
120=== added symlink 'actions/sapi-post-ips'
121=== target is u'actions.py'
122=== added symlink 'actions/sapi-post-license'
123=== target is u'actions.py'
124=== added symlink 'actions/sapi-post-zone-info'
125=== target is u'actions.py'
126=== added directory 'bin'
127=== added file 'bin/charm_helpers_sync.py'
128--- bin/charm_helpers_sync.py 1970-01-01 00:00:00 +0000
129+++ bin/charm_helpers_sync.py 2016-10-17 17:28:49 +0000
130@@ -0,0 +1,253 @@
131+#!/usr/bin/python
132+
133+# Copyright 2014-2015 Canonical Limited.
134+#
135+# This file is part of charm-helpers.
136+#
137+# charm-helpers is free software: you can redistribute it and/or modify
138+# it under the terms of the GNU Lesser General Public License version 3 as
139+# published by the Free Software Foundation.
140+#
141+# charm-helpers is distributed in the hope that it will be useful,
142+# but WITHOUT ANY WARRANTY; without even the implied warranty of
143+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
144+# GNU Lesser General Public License for more details.
145+#
146+# You should have received a copy of the GNU Lesser General Public License
147+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
148+
149+# Authors:
150+# Adam Gandelman <adamg@ubuntu.com>
151+
152+import logging
153+import optparse
154+import os
155+import subprocess
156+import shutil
157+import sys
158+import tempfile
159+import yaml
160+from fnmatch import fnmatch
161+
162+import six
163+
164+CHARM_HELPERS_BRANCH = 'lp:charm-helpers'
165+
166+
167+def parse_config(conf_file):
168+ if not os.path.isfile(conf_file):
169+ logging.error('Invalid config file: %s.' % conf_file)
170+ return False
171+ return yaml.load(open(conf_file).read())
172+
173+
174+def clone_helpers(work_dir, branch):
175+ dest = os.path.join(work_dir, 'charm-helpers')
176+ logging.info('Checking out %s to %s.' % (branch, dest))
177+ cmd = ['bzr', 'checkout', '--lightweight', branch, dest]
178+ subprocess.check_call(cmd)
179+ return dest
180+
181+
182+def _module_path(module):
183+ return os.path.join(*module.split('.'))
184+
185+
186+def _src_path(src, module):
187+ return os.path.join(src, 'charmhelpers', _module_path(module))
188+
189+
190+def _dest_path(dest, module):
191+ return os.path.join(dest, _module_path(module))
192+
193+
194+def _is_pyfile(path):
195+ return os.path.isfile(path + '.py')
196+
197+
198+def ensure_init(path):
199+ '''
200+ ensure directories leading up to path are importable, omitting
201+ parent directory, eg path='/hooks/helpers/foo'/:
202+ hooks/
203+ hooks/helpers/__init__.py
204+ hooks/helpers/foo/__init__.py
205+ '''
206+ for d, dirs, files in os.walk(os.path.join(*path.split('/')[:2])):
207+ _i = os.path.join(d, '__init__.py')
208+ if not os.path.exists(_i):
209+ logging.info('Adding missing __init__.py: %s' % _i)
210+ open(_i, 'wb').close()
211+
212+
213+def sync_pyfile(src, dest):
214+ src = src + '.py'
215+ src_dir = os.path.dirname(src)
216+ logging.info('Syncing pyfile: %s -> %s.' % (src, dest))
217+ if not os.path.exists(dest):
218+ os.makedirs(dest)
219+ shutil.copy(src, dest)
220+ if os.path.isfile(os.path.join(src_dir, '__init__.py')):
221+ shutil.copy(os.path.join(src_dir, '__init__.py'),
222+ dest)
223+ ensure_init(dest)
224+
225+
226+def get_filter(opts=None):
227+ opts = opts or []
228+ if 'inc=*' in opts:
229+ # do not filter any files, include everything
230+ return None
231+
232+ def _filter(dir, ls):
233+ incs = [opt.split('=').pop() for opt in opts if 'inc=' in opt]
234+ _filter = []
235+ for f in ls:
236+ _f = os.path.join(dir, f)
237+
238+ if not os.path.isdir(_f) and not _f.endswith('.py') and incs:
239+ if True not in [fnmatch(_f, inc) for inc in incs]:
240+ logging.debug('Not syncing %s, does not match include '
241+ 'filters (%s)' % (_f, incs))
242+ _filter.append(f)
243+ else:
244+ logging.debug('Including file, which matches include '
245+ 'filters (%s): %s' % (incs, _f))
246+ elif (os.path.isfile(_f) and not _f.endswith('.py')):
247+ logging.debug('Not syncing file: %s' % f)
248+ _filter.append(f)
249+ elif (os.path.isdir(_f) and not
250+ os.path.isfile(os.path.join(_f, '__init__.py'))):
251+ logging.debug('Not syncing directory: %s' % f)
252+ _filter.append(f)
253+ return _filter
254+ return _filter
255+
256+
257+def sync_directory(src, dest, opts=None):
258+ if os.path.exists(dest):
259+ logging.debug('Removing existing directory: %s' % dest)
260+ shutil.rmtree(dest)
261+ logging.info('Syncing directory: %s -> %s.' % (src, dest))
262+
263+ shutil.copytree(src, dest, ignore=get_filter(opts))
264+ ensure_init(dest)
265+
266+
267+def sync(src, dest, module, opts=None):
268+
269+ # Sync charmhelpers/__init__.py for bootstrap code.
270+ sync_pyfile(_src_path(src, '__init__'), dest)
271+
272+ # Sync other __init__.py files in the path leading to module.
273+ m = []
274+ steps = module.split('.')[:-1]
275+ while steps:
276+ m.append(steps.pop(0))
277+ init = '.'.join(m + ['__init__'])
278+ sync_pyfile(_src_path(src, init),
279+ os.path.dirname(_dest_path(dest, init)))
280+
281+ # Sync the module, or maybe a .py file.
282+ if os.path.isdir(_src_path(src, module)):
283+ sync_directory(_src_path(src, module), _dest_path(dest, module), opts)
284+ elif _is_pyfile(_src_path(src, module)):
285+ sync_pyfile(_src_path(src, module),
286+ os.path.dirname(_dest_path(dest, module)))
287+ else:
288+ logging.warn('Could not sync: %s. Neither a pyfile or directory, '
289+ 'does it even exist?' % module)
290+
291+
292+def parse_sync_options(options):
293+ if not options:
294+ return []
295+ return options.split(',')
296+
297+
298+def extract_options(inc, global_options=None):
299+ global_options = global_options or []
300+ if global_options and isinstance(global_options, six.string_types):
301+ global_options = [global_options]
302+ if '|' not in inc:
303+ return (inc, global_options)
304+ inc, opts = inc.split('|')
305+ return (inc, parse_sync_options(opts) + global_options)
306+
307+
308+def sync_helpers(include, src, dest, options=None):
309+ if not os.path.isdir(dest):
310+ os.makedirs(dest)
311+
312+ global_options = parse_sync_options(options)
313+
314+ for inc in include:
315+ if isinstance(inc, str):
316+ inc, opts = extract_options(inc, global_options)
317+ sync(src, dest, inc, opts)
318+ elif isinstance(inc, dict):
319+ # could also do nested dicts here.
320+ for k, v in six.iteritems(inc):
321+ if isinstance(v, list):
322+ for m in v:
323+ inc, opts = extract_options(m, global_options)
324+ sync(src, dest, '%s.%s' % (k, inc), opts)
325+
326+if __name__ == '__main__':
327+ parser = optparse.OptionParser()
328+ parser.add_option('-c', '--config', action='store', dest='config',
329+ default=None, help='helper config file')
330+ parser.add_option('-D', '--debug', action='store_true', dest='debug',
331+ default=False, help='debug')
332+ parser.add_option('-b', '--branch', action='store', dest='branch',
333+ help='charm-helpers bzr branch (overrides config)')
334+ parser.add_option('-d', '--destination', action='store', dest='dest_dir',
335+ help='sync destination dir (overrides config)')
336+ (opts, args) = parser.parse_args()
337+
338+ if opts.debug:
339+ logging.basicConfig(level=logging.DEBUG)
340+ else:
341+ logging.basicConfig(level=logging.INFO)
342+
343+ if opts.config:
344+ logging.info('Loading charm helper config from %s.' % opts.config)
345+ config = parse_config(opts.config)
346+ if not config:
347+ logging.error('Could not parse config from %s.' % opts.config)
348+ sys.exit(1)
349+ else:
350+ config = {}
351+
352+ if 'branch' not in config:
353+ config['branch'] = CHARM_HELPERS_BRANCH
354+ if opts.branch:
355+ config['branch'] = opts.branch
356+ if opts.dest_dir:
357+ config['destination'] = opts.dest_dir
358+
359+ if 'destination' not in config:
360+ logging.error('No destination dir. specified as option or config.')
361+ sys.exit(1)
362+
363+ if 'include' not in config:
364+ if not args:
365+ logging.error('No modules to sync specified as option or config.')
366+ sys.exit(1)
367+ config['include'] = []
368+ [config['include'].append(a) for a in args]
369+
370+ sync_options = None
371+ if 'options' in config:
372+ sync_options = config['options']
373+ tmpd = tempfile.mkdtemp()
374+ try:
375+ checkout = clone_helpers(tmpd, config['branch'])
376+ sync_helpers(config['include'], checkout, config['destination'],
377+ options=sync_options)
378+ except Exception as e:
379+ logging.error("Could not sync: %s" % e)
380+ raise e
381+ finally:
382+ logging.debug('Cleaning up %s' % tmpd)
383+ shutil.rmtree(tmpd)
384
385=== modified file 'config.yaml'
386--- config.yaml 2016-07-23 14:21:58 +0000
387+++ config.yaml 2016-10-17 17:28:49 +0000
388@@ -17,6 +17,7 @@
389 description: Public SSH key of PLUMgrid LCM which is running PG-Tools.
390 mgmt-interface:
391 type: string
392+ default:
393 description: The interface connected to PLUMgrid Managment network.
394 fabric-interfaces:
395 default: 'MANAGEMENT'
396@@ -56,3 +57,26 @@
397 default: 127.0.0.1
398 type: string
399 description: IP address of the PLUMgrid Operations VM Management interface.
400+ lcm-ip:
401+ type: string
402+ default: 127.0.0.1
403+ description: IP used by Solutions API to get/post cloud information.
404+ sapi-port:
405+ default: 8099
406+ type: int
407+ description: Port used by Solutions API to get/post cloud information.
408+ sapi-zone:
409+ default: pgzone
410+ type: string
411+ description: Zone name used by Solutions API to get/post cloud information.
412+ openstack-release:
413+ default: kilo
414+ type: string
415+ description: |
416+ OpenStack release to determine solution version that will be posted to
417+ Solutions API server.
418+ enable-sapi:
419+ default: false
420+ type: boolean
421+ description: |
422+ Enable or disable Solutions API support.
423
424=== modified symlink 'hooks/install' (properties changed: -x to +x)
425=== target was u'pg_dir_hooks.py'
426--- hooks/install 1970-01-01 00:00:00 +0000
427+++ hooks/install 2016-10-17 17:28:49 +0000
428@@ -0,0 +1,20 @@
429+#!/bin/bash
430+# Wrapper to deal with newer Ubuntu versions that don't have py2 installed
431+# by default.
432+
433+declare -a DEPS=('apt' 'netaddr' 'netifaces' 'pip' 'yaml')
434+
435+check_and_install() {
436+ pkg="${1}-${2}"
437+ if ! dpkg -s ${pkg} 2>&1 > /dev/null; then
438+ apt-get -y install ${pkg}
439+ fi
440+}
441+
442+PYTHON="python"
443+
444+for dep in ${DEPS[@]}; do
445+ check_and_install ${PYTHON} ${dep}
446+done
447+
448+exec ./hooks/install.real
449
450=== added symlink 'hooks/install.real'
451=== target is u'pg_dir_hooks.py'
452=== modified file 'hooks/pg_dir_context.py'
453--- hooks/pg_dir_context.py 2016-03-26 22:04:58 +0000
454+++ hooks/pg_dir_context.py 2016-10-17 17:28:49 +0000
455@@ -26,6 +26,30 @@
456 )
457
458
459+def _pg_edge_ips():
460+ '''
461+ Inspects edge-peer relation and returns the
462+ ips of the edge nodes
463+ '''
464+ return [get_host_ip(rdata['private-address'])
465+ for rid in relation_ids("plumgrid")
466+ for rdata in
467+ (relation_get(rid=rid, unit=unit) for unit in related_units(rid))
468+ if 'edge-peer' in rdata]
469+
470+
471+def _pg_gateway_ips():
472+ '''
473+ Inspects gateway-peer relation and returns the
474+ ips of the gateway nodes
475+ '''
476+ return [get_host_ip(rdata['private-address'])
477+ for rid in relation_ids("plumgrid")
478+ for rdata in
479+ (relation_get(rid=rid, unit=unit) for unit in related_units(rid))
480+ if 'gateway-peer' in rdata]
481+
482+
483 def _pg_dir_ips():
484 '''
485 Inspects plumgrid-director peer relation and returns the
486
487=== modified file 'hooks/pg_dir_hooks.py'
488--- hooks/pg_dir_hooks.py 2016-07-28 19:27:47 +0000
489+++ hooks/pg_dir_hooks.py 2016-10-17 17:28:49 +0000
490@@ -9,7 +9,6 @@
491 import time
492 from charmhelpers.core.host import service_running
493 from charmhelpers.contrib.network.ip import is_ip
494-
495 from charmhelpers.core.hookenv import (
496 Hooks,
497 UnregisteredHookError,
498@@ -41,15 +40,19 @@
499 load_iptables,
500 restart_on_change,
501 director_cluster_ready,
502- disable_apparmor_libvirt,
503- configure_pg_sources
504+ configure_pg_sources,
505+ configure_analyst_opsvm,
506+ sapi_post_ips,
507+ sapi_post_license,
508+ sapi_post_zone_info,
509+ disable_apparmor_libvirt
510 )
511
512 hooks = Hooks()
513 CONFIGS = register_configs()
514
515
516-@hooks.hook()
517+@hooks.hook('install.real')
518 def install():
519 '''
520 Install hook is run when the charm is first deployed on a node.
521@@ -62,12 +65,13 @@
522 for pkg in pkgs:
523 apt_install(pkg, options=['--force-yes'], fatal=True)
524 load_iovisor()
525- ensure_mtu()
526 disable_apparmor_libvirt()
527+ ensure_mtu()
528 CONFIGS.write_all()
529
530
531 @hooks.hook('director-relation-joined')
532+@hooks.hook('director-relation-changed')
533 @restart_on_change(restart_map())
534 def dir_joined():
535 '''
536@@ -76,19 +80,22 @@
537 if director_cluster_ready():
538 ensure_mtu()
539 CONFIGS.write_all()
540- restart_pg('lxc')
541-
542-
543-@hooks.hook('plumgrid-relation-joined')
544+
545+
546+@hooks.hook('plumgrid-relation-joined',
547+ 'plumgrid-relation-changed',
548+ 'plumgrid-relation-departed')
549 def plumgrid_joined(relation_id=None):
550 '''
551 This hook is run when relation with edge or gateway is created.
552 '''
553 opsvm_ip = config('opsvm-ip')
554 if not is_ip(opsvm_ip):
555- raise ValueError('Incorrect OPSVM IP specified')
556+ raise ValueError('Invalid OPSVM IP specified!')
557 else:
558 relation_set(relation_id=relation_id, opsvm_ip=opsvm_ip)
559+ if is_leader():
560+ sapi_post_ips()
561
562
563 @hooks.hook('plumgrid-configs-relation-joined')
564@@ -119,6 +126,8 @@
565 if charm_config.changed('plumgrid-license-key'):
566 if is_leader() and post_pg_license():
567 log("PLUMgrid License Posted")
568+ # Post PG license to Sol-API
569+ sapi_post_license()
570 if charm_config.changed('fabric-interfaces'):
571 if not fabric_interface_changed():
572 log("Fabric interface already set")
573@@ -126,6 +135,8 @@
574 stop_pg()
575 if charm_config.changed('plumgrid-virtual-ip'):
576 CONFIGS.write_all()
577+ for rid in relation_ids('plumgrid'):
578+ plumgrid_joined(rid)
579 stop_pg()
580 for rid in relation_ids('plumgrid-configs'):
581 plumgrid_configs_joined(rid)
582@@ -151,10 +162,21 @@
583 for rid in relation_ids('plumgrid'):
584 plumgrid_joined(rid)
585 stop_pg()
586+ if (charm_config.changed('sapi-port') or
587+ charm_config.changed('lcm-ip') or
588+ charm_config.changed('sapi-zone') or
589+ charm_config.changed('enable-sapi')):
590+ if is_leader():
591+ if is_ip(config('lcm-ip')):
592+ sapi_post_zone_info()
593+ else:
594+ raise ValueError('Invalid LCM IP specified!')
595+ for rid in relation_ids('plumgrid'):
596+ plumgrid_joined(rid)
597 ensure_mtu()
598 CONFIGS.write_all()
599 if not service_running('plumgrid'):
600- restart_pg('lxc')
601+ restart_pg()
602
603
604 @hooks.hook('start')
605@@ -162,16 +184,15 @@
606 '''
607 This hook is run when the charm is started.
608 '''
609- restart_pg('lxc')
610- time.sleep(15)
611+ configure_analyst_opsvm()
612 if config('plumgrid-license-key') is not None:
613 count = 0
614- while (count < 15):
615+ while (count < 10):
616+ time.sleep(15)
617 if post_pg_license():
618 break
619 count += 1
620- time.sleep(15)
621- if count == 15:
622+ if count == 10:
623 raise ValueError("Error occurred while posting plumgrid license"
624 "key. Please check plumgrid services.")
625
626@@ -184,7 +205,6 @@
627 '''
628 ensure_mtu()
629 CONFIGS.write_all()
630- restart_pg('lxc')
631
632
633 @hooks.hook('stop')
634
635=== modified file 'hooks/pg_dir_utils.py'
636--- hooks/pg_dir_utils.py 2016-08-29 10:03:28 +0000
637+++ hooks/pg_dir_utils.py 2016-10-17 17:28:49 +0000
638@@ -7,7 +7,6 @@
639 import time
640 import os
641 import json
642-import shlex
643 from collections import OrderedDict
644 from socket import gethostname as get_unit_hostname
645 from copy import deepcopy
646@@ -25,14 +24,16 @@
647 get_bridges,
648 get_bridge_nics,
649 is_ip,
650+ get_iface_addr,
651+ get_host_ip
652 )
653 from charmhelpers.core.host import (
654 service_start,
655- service_restart,
656 service_stop,
657 service_running,
658 path_hash,
659- set_nic_mtu
660+ set_nic_mtu,
661+ service_restart
662 )
663 from charmhelpers.fetch import (
664 apt_cache,
665@@ -41,6 +42,11 @@
666 from charmhelpers.contrib.openstack.utils import (
667 os_release,
668 )
669+from pg_dir_context import (
670+ _pg_dir_ips,
671+ _pg_edge_ips,
672+ _pg_gateway_ips
673+)
674
675 SOURCES_LIST = '/etc/apt/sources.list'
676 LXC_CONF = '/etc/libvirt/lxc.conf'
677@@ -57,6 +63,14 @@
678 AUTH_KEY_PATH = '%s/root/.ssh/authorized_keys' % PG_LXC_DATA_PATH
679 TEMP_LICENSE_FILE = '/tmp/license'
680
681+# Constant values for OpenStack releases as Canonical-Ubuntu
682+# doesn't have any specific solution version associated
683+OPENSTACK_RELEASE_VERS = {
684+ 'kilo': '10',
685+ 'liberty': '11',
686+ 'mitaka': '12'
687+}
688+
689 BASE_RESOURCE_MAP = OrderedDict([
690 (PG_KA_CONF, {
691 'services': ['plumgrid'],
692@@ -105,6 +119,31 @@
693 log('Unable to update /etc/apt/sources.list')
694
695
696+def configure_analyst_opsvm():
697+ '''
698+ Configures Anaylyst for OPSVM
699+ '''
700+ if not service_running('plumgrid'):
701+ restart_pg()
702+ NS_ENTER = ('/opt/local/bin/nsenter -t $(ps ho pid --ppid $(cat '
703+ '/var/run/libvirt/lxc/plumgrid.pid)) -m -n -u -i -p ')
704+ sigmund_stop = NS_ENTER + '/usr/bin/service plumgrid-sigmund stop'
705+ sigmund_status = NS_ENTER \
706+ + '/usr/bin/service plumgrid-sigmund status'
707+ sigmund_autoboot = NS_ENTER \
708+ + '/usr/bin/sigmund-configure --ip {0} --start --autoboot' \
709+ .format(config('opsvm-ip'))
710+ try:
711+ status = subprocess.check_output(sigmund_status, shell=True)
712+ if 'start/running' in status:
713+ if subprocess.call(sigmund_stop, shell=True):
714+ log('plumgrid-sigmund couldn\'t be stopped!')
715+ return
716+ subprocess.check_call(sigmund_autoboot, shell=True)
717+ except:
718+ log('plumgrid-sigmund couldn\'t be started!')
719+
720+
721 def determine_packages():
722 '''
723 Returns list of packages required by PLUMgrid director as specified
724@@ -131,6 +170,36 @@
725 return pkgs
726
727
728+def disable_apparmor_libvirt():
729+ '''
730+ Disables Apparmor profile of libvirtd.
731+ '''
732+ apt_install('apparmor-utils')
733+ apt_install('cgroup-bin')
734+ _exec_cmd(['sudo', 'aa-disable', '/usr/sbin/libvirtd'],
735+ error_msg='Error disabling AppArmor profile of libvirtd')
736+ disable_apparmor()
737+ service_restart('libvirt-bin')
738+
739+
740+def disable_apparmor():
741+ '''
742+ Disables Apparmor security for lxc.
743+ '''
744+ try:
745+ f = open(LXC_CONF, 'r')
746+ except IOError:
747+ log('Libvirt not installed yet')
748+ return 0
749+ filedata = f.read()
750+ f.close()
751+ newdata = filedata.replace("security_driver = \"apparmor\"",
752+ "#security_driver = \"apparmor\"")
753+ f = open(LXC_CONF, 'w')
754+ f.write(newdata)
755+ f.close()
756+
757+
758 def register_configs(release=None):
759 '''
760 Returns an object of the Openstack Tempating Class which contains the
761@@ -161,31 +230,7 @@
762 return {cfg: rscs['services'] for cfg, rscs in resource_map().iteritems()}
763
764
765-def start_gateway():
766- '''
767- Brings up PE-gateway interface. Initial hack but will be solved when docker
768- plumgrid-director package will be used.
769- '''
770- count = 0
771- while (count < 7):
772- cmd = 'ps aux | grep launch_metadata_helper'
773- output = subprocess.check_output([cmd], shell=True)
774- roots = 0
775- v = shlex.split(output)
776- for i in v:
777- if i == 'root':
778- roots += 1
779- if roots < 3:
780- stop_pg()
781- time.sleep(3)
782- service_start('plumgrid')
783- else:
784- break
785- count += 1
786- time.sleep(20)
787-
788-
789-def restart_pg(gateway=None):
790+def restart_pg():
791 '''
792 Stops and Starts PLUMgrid service after flushing iptables.
793 '''
794@@ -203,8 +248,6 @@
795 raise ValueError("plumgrid service couldn't be started")
796 else:
797 raise ValueError("libvirt-bin service couldn't be started")
798- if gateway:
799- start_gateway()
800 status_set('active', 'Unit is ready')
801
802
803@@ -251,8 +294,14 @@
804 '''
805 mgmt_interface = config('mgmt-interface')
806 if not mgmt_interface:
807- return get_iface_from_addr(unit_get('private-address'))
808- elif mgmt_interface and interface_exists(mgmt_interface):
809+ try:
810+ return get_iface_from_addr(unit_get('private-address'))
811+ except:
812+ for bridge_interface in get_bridges():
813+ if (get_host_ip(unit_get('private-address'))
814+ in get_iface_addr(bridge_interface)):
815+ return bridge_interface
816+ elif interface_exists(mgmt_interface):
817 return mgmt_interface
818 else:
819 log('Provided managment interface %s does not exist'
820@@ -317,36 +366,6 @@
821 set_nic_mtu(fabric_interface, interface_mtu)
822
823
824-def disable_apparmor_libvirt():
825- '''
826- Disables Apparmor profile of libvirtd.
827- '''
828- apt_install('apparmor-utils')
829- apt_install('cgroup-bin')
830- _exec_cmd(['sudo', 'aa-disable', '/usr/sbin/libvirtd'],
831- error_msg='Error disabling AppArmor profile of libvirtd')
832- disable_apparmor()
833- service_restart('libvirt-bin')
834-
835-
836-def disable_apparmor():
837- '''
838- Disables Apparmor security for lxc.
839- '''
840- try:
841- f = open(LXC_CONF, 'r')
842- except IOError:
843- log('Libvirt not installed yet')
844- return 0
845- filedata = f.read()
846- f.close()
847- newdata = filedata.replace("security_driver = \"apparmor\"",
848- "#security_driver = \"apparmor\"")
849- f = open(LXC_CONF, 'w')
850- f.write(newdata)
851- f.close()
852-
853-
854 def _exec_cmd(cmd=None, error_msg='Command exited with ERRORs', fatal=False):
855 '''
856 Function to execute any bash command on the node.
857@@ -431,6 +450,160 @@
858 return 1
859
860
861+def sapi_post_ips():
862+ """
863+ Posts PLUMgrid nodes IPs to solutions api server.
864+ """
865+ if not config('enable-sapi'):
866+ log('Solutions API support is disabled!')
867+ return 1
868+ pg_edge_ips = _pg_edge_ips()
869+ pg_dir_ips = _pg_dir_ips()
870+ pg_gateway_ips = _pg_gateway_ips()
871+ pg_dir_ips.append(get_host_ip(unit_get('private-address')))
872+ pg_edge_ips = '"edge_ips"' + ':' \
873+ + '"{}"'.format(','.join(str(i) for i in pg_edge_ips))
874+ pg_dir_ips = '"director_ips"' + ':' \
875+ + '"{}"'.format(','.join(str(i) for i in pg_dir_ips))
876+ pg_gateway_ips = '"gateway_ips"' + ':' \
877+ + '"{}"'.format(','.join(str(i) for i in pg_gateway_ips))
878+ opsvm_ip = '"opsvm_ip"' + ':' + '"{}"'.format(config('opsvm-ip'))
879+ virtual_ip = '"virtual_ip"' + ':' \
880+ + '"{}"'.format(config('plumgrid-virtual-ip'))
881+ JSON_IPS = ','.join([pg_dir_ips, pg_edge_ips, pg_gateway_ips,
882+ opsvm_ip, virtual_ip])
883+ status = (
884+ 'curl -H \'Content-Type: application/json\' -X '
885+ 'PUT -d \'{{{0}}}\' http://{1}' + ':' + '{2}/v1/zones/{3}/allIps'
886+ ).format(JSON_IPS, config('lcm-ip'), config('sapi-port'),
887+ config('sapi-zone'))
888+ POST_ZONE_IPs = _exec_cmd_output(
889+ status,
890+ 'Posting Zone IPs to Solutions API server failed!')
891+ if POST_ZONE_IPs:
892+ if 'success' in POST_ZONE_IPs:
893+ log('Successfully posted Zone IPs to Solutions API server!')
894+ log(POST_ZONE_IPs)
895+
896+
897+def _exec_cmd_output(cmd=None, error_msg='Command exited with ERRORs',
898+ fatal=False):
899+ '''
900+ Function to get output from bash command executed on the node.
901+ '''
902+ if cmd is None:
903+ log("No command specified")
904+ else:
905+ if fatal:
906+ return subprocess.check_output(cmd, shell=True)
907+ else:
908+ try:
909+ return subprocess.check_output(cmd, shell=True)
910+ except subprocess.CalledProcessError:
911+ log(error_msg)
912+ return None
913+
914+
915+def sapi_post_license():
916+ '''
917+ Posts PLUMgrid License to solutions api server
918+ '''
919+ if not config('enable-sapi'):
920+ log('Solutions API support is disabled!')
921+ return 1
922+ username = '"user_name":' + '"{}"'.format(config('plumgrid-username'))
923+ password = '"password":' + '"{}"'.format(config('plumgrid-password'))
924+ license = '"license":' + '"{}"'.format(config('plumgrid-license-key'))
925+ JSON_LICENSE = ','.join([username, password, license])
926+ status = (
927+ 'curl -H \'Content-Type: application/json\' -X '
928+ 'PUT -d \'{{{0}}}\' http://{1}' + ':' + '{2}/v1/zones/{3}/pgLicense'
929+ ).format(JSON_LICENSE, config('lcm-ip'), config('sapi-port'),
930+ config('sapi-zone'))
931+ POST_LICENSE = _exec_cmd_output(
932+ status,
933+ 'Posting PLUMgrid License to Solutions API server failed!')
934+ if POST_LICENSE:
935+ if 'success' in POST_LICENSE:
936+ log('Successfully posted license file for zone "{}"!'
937+ .format(config('sapi-zone')))
938+ log(POST_LICENSE)
939+
940+
941+def sapi_post_zone_info():
942+ '''
943+ Posts zone information to solutions api server
944+ '''
945+ if not config('enable-sapi'):
946+ log('Solutions API support is disabled!')
947+ return 1
948+ sol_name = '"solution_name":"Ubuntu OpenStack"'
949+ release = config('openstack-release')
950+ for key, value in OPENSTACK_RELEASE_VERS.iteritems():
951+ if release == value:
952+ sol_version = value
953+ else:
954+ sol_version = 10
955+ sol_version = '"solution_version":"{}"'.format(sol_version)
956+ pg_ons_version = _exec_cmd_output(
957+ 'dpkg -l | grep plumgrid | awk \'{print $3}\' | '
958+ 'sed \'s/-/./\' | cut -f1 -d"-"',
959+ 'Unable to obtain PG ONS version'
960+ ).replace('\n', '')
961+ pg_ons_version = \
962+ '"pg_ons_version":"{}"'.format(pg_ons_version)
963+ hypervisor = '"hypervisor":"Ubuntu"'
964+ hypervisor_version = \
965+ _exec_cmd_output('lsb_release -r | awk \'{print $2}\'',
966+ 'Unable to obtain solution version'
967+ ).replace('\n', '')
968+ hypervisor_version = '"hypervisor_version":"{}"' \
969+ .format(hypervisor_version)
970+ kernel_version = _exec_cmd_output(
971+ 'uname -r',
972+ 'Unable to obtain kernal version').replace('\n', '')
973+ kernel_version = \
974+ '"kernel_version":"{}"'.format(kernel_version)
975+ cloudapex_path = '/var/lib/libvirt/filesystems/plumgrid/' \
976+ 'opt/pg/web/cloudApex/modules/appCloudApex' \
977+ '/appCloudApex.js'
978+ if os.path.isfile(cloudapex_path):
979+ pg_cloudapex_version = 'cat ' \
980+ + '{}'.format(cloudapex_path) \
981+ + ' | grep -i appversion | awk \'{print $2}\''
982+ pg_cloudapex_version = \
983+ _exec_cmd_output(pg_cloudapex_version,
984+ 'Unable to retrieve CloudApex version'
985+ ).replace('\n', '')
986+ else:
987+ log('CloudApex not installed!')
988+ pg_cloudapex_version = ''
989+ pg_cloudapex_version = \
990+ '"pg_cloudapex_version":"{}"'.format(pg_cloudapex_version)
991+ JSON_ZONE_INFO = ','.join([
992+ sol_name,
993+ sol_version,
994+ pg_ons_version,
995+ hypervisor,
996+ hypervisor_version,
997+ kernel_version,
998+ pg_cloudapex_version,
999+ ])
1000+ status = (
1001+ 'curl -H \'Content-Type: application/json\' -X '
1002+ 'PUT -d \'{{{0}}}\' http://{1}:{2}/v1/zones/{3}/zoneinfo'
1003+ ).format(JSON_ZONE_INFO, config('lcm-ip'), config('sapi-port'),
1004+ config('sapi-zone'))
1005+ POST_ZONE_INFO = _exec_cmd_output(
1006+ status,
1007+ 'Posting Zone Information to Solutions API server failed!')
1008+ if POST_ZONE_INFO:
1009+ if 'success' in POST_ZONE_INFO:
1010+ log('Successfully posted Zone information to Solutions API'
1011+ ' server!')
1012+ log(POST_ZONE_INFO)
1013+
1014+
1015 def load_iptables():
1016 '''
1017 Loads iptables rules to allow all PLUMgrid communication.
1018
1019=== added symlink 'hooks/plumgrid-relation-changed'
1020=== target is u'pg_dir_hooks.py'
1021=== added symlink 'hooks/plumgrid-relation-departed'
1022=== target is u'pg_dir_hooks.py'
1023=== modified file 'metadata.yaml'
1024--- metadata.yaml 2016-05-04 06:47:43 +0000
1025+++ metadata.yaml 2016-10-17 17:28:49 +0000
1026@@ -7,6 +7,9 @@
1027 The configuration of the virtual network infrastructure for tenants is
1028 done through the PLUMgrid Director. The PLUMgrid Director is typically
1029 co-located on the OpenStack controller nodes.
1030+series:
1031+ - xenial
1032+ - trusty
1033 tags:
1034 - openstack
1035 requires:
1036
1037=== modified file 'unit_tests/test_pg_dir_hooks.py'
1038--- unit_tests/test_pg_dir_hooks.py 2016-05-01 02:16:59 +0000
1039+++ unit_tests/test_pg_dir_hooks.py 2016-10-17 17:28:49 +0000
1040@@ -32,7 +32,10 @@
1041 'post_pg_license',
1042 'config',
1043 'load_iptables',
1044- 'status_set'
1045+ 'status_set',
1046+ 'configure_analyst_opsvm',
1047+ 'sapi_post_zone_info',
1048+ 'disable_apparmor_libvirt'
1049 ]
1050 NEUTRON_CONF_DIR = "/etc/neutron"
1051

Subscribers

People subscribed via source and target branches

to all changes: