PLUMgrid gateway initial charm
This commit is contained in:
commit
2e2935412a
17
.project
Normal file
17
.project
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>neutron-openvswitch</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.python.pydev.PyDevBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.python.pydev.pythonNature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
9
.pydevproject
Normal file
9
.pydevproject
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<?eclipse-pydev version="1.0"?><pydev_project>
|
||||||
|
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
|
||||||
|
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
||||||
|
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||||
|
<path>/neutron-openvswitch/hooks</path>
|
||||||
|
<path>/neutron-openvswitch/unit_tests</path>
|
||||||
|
</pydev_pathproperty>
|
||||||
|
</pydev_project>
|
24
Makefile
Normal file
24
Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/make
|
||||||
|
PYTHON := /usr/bin/env python
|
||||||
|
|
||||||
|
lint:
|
||||||
|
@flake8 --exclude hooks/charmhelpers hooks
|
||||||
|
@flake8 --exclude hooks/charmhelpers unit_tests
|
||||||
|
@charm proof
|
||||||
|
|
||||||
|
unit_test:
|
||||||
|
@echo Starting tests...
|
||||||
|
@$(PYTHON) /usr/bin/nosetests --nologcapture unit_tests
|
||||||
|
|
||||||
|
bin/charm_helpers_sync.py:
|
||||||
|
@mkdir -p bin
|
||||||
|
@bzr cat lp:charm-helpers/tools/charm_helpers_sync/charm_helpers_sync.py \
|
||||||
|
> bin/charm_helpers_sync.py
|
||||||
|
|
||||||
|
sync: bin/charm_helpers_sync.py
|
||||||
|
@$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-sync.yaml
|
||||||
|
patch -p0 < ~/profsvcs/canonical/charms/charmhelpers.patch
|
||||||
|
|
||||||
|
publish: lint unit_test
|
||||||
|
bzr push lp:charms/plumgrid-gateway
|
||||||
|
bzr push lp:charms/trusty/plumgrid-gateway
|
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Overview
|
||||||
|
|
||||||
|
This charm provides the PLUMgrid Gateway configuration for a node.
|
||||||
|
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
To deploy (partial deployment of linked charms only):
|
||||||
|
|
||||||
|
juju deploy neutron-api
|
||||||
|
juju deploy neutron-iovisor
|
||||||
|
juju deploy plumgrid-director
|
||||||
|
juju deploy plumgrid-gateway
|
||||||
|
juju add-relation plumgrid-gateway neutron-iovisor
|
||||||
|
juju add-relation plumgrid-gateway plumgrid-director
|
||||||
|
|
253
bin/charm_helpers_sync.py
Normal file
253
bin/charm_helpers_sync.py
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# Copyright 2014-2015 Canonical Limited.
|
||||||
|
#
|
||||||
|
# This file is part of charm-helpers.
|
||||||
|
#
|
||||||
|
# charm-helpers is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License version 3 as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# charm-helpers is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Authors:
|
||||||
|
# Adam Gandelman <adamg@ubuntu.com>
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import optparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import yaml
|
||||||
|
from fnmatch import fnmatch
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
CHARM_HELPERS_BRANCH = 'lp:charm-helpers'
|
||||||
|
|
||||||
|
|
||||||
|
def parse_config(conf_file):
|
||||||
|
if not os.path.isfile(conf_file):
|
||||||
|
logging.error('Invalid config file: %s.' % conf_file)
|
||||||
|
return False
|
||||||
|
return yaml.load(open(conf_file).read())
|
||||||
|
|
||||||
|
|
||||||
|
def clone_helpers(work_dir, branch):
|
||||||
|
dest = os.path.join(work_dir, 'charm-helpers')
|
||||||
|
logging.info('Checking out %s to %s.' % (branch, dest))
|
||||||
|
cmd = ['bzr', 'checkout', '--lightweight', branch, dest]
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
return dest
|
||||||
|
|
||||||
|
|
||||||
|
def _module_path(module):
|
||||||
|
return os.path.join(*module.split('.'))
|
||||||
|
|
||||||
|
|
||||||
|
def _src_path(src, module):
|
||||||
|
return os.path.join(src, 'charmhelpers', _module_path(module))
|
||||||
|
|
||||||
|
|
||||||
|
def _dest_path(dest, module):
|
||||||
|
return os.path.join(dest, _module_path(module))
|
||||||
|
|
||||||
|
|
||||||
|
def _is_pyfile(path):
|
||||||
|
return os.path.isfile(path + '.py')
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_init(path):
|
||||||
|
'''
|
||||||
|
ensure directories leading up to path are importable, omitting
|
||||||
|
parent directory, eg path='/hooks/helpers/foo'/:
|
||||||
|
hooks/
|
||||||
|
hooks/helpers/__init__.py
|
||||||
|
hooks/helpers/foo/__init__.py
|
||||||
|
'''
|
||||||
|
for d, dirs, files in os.walk(os.path.join(*path.split('/')[:2])):
|
||||||
|
_i = os.path.join(d, '__init__.py')
|
||||||
|
if not os.path.exists(_i):
|
||||||
|
logging.info('Adding missing __init__.py: %s' % _i)
|
||||||
|
open(_i, 'wb').close()
|
||||||
|
|
||||||
|
|
||||||
|
def sync_pyfile(src, dest):
|
||||||
|
src = src + '.py'
|
||||||
|
src_dir = os.path.dirname(src)
|
||||||
|
logging.info('Syncing pyfile: %s -> %s.' % (src, dest))
|
||||||
|
if not os.path.exists(dest):
|
||||||
|
os.makedirs(dest)
|
||||||
|
shutil.copy(src, dest)
|
||||||
|
if os.path.isfile(os.path.join(src_dir, '__init__.py')):
|
||||||
|
shutil.copy(os.path.join(src_dir, '__init__.py'),
|
||||||
|
dest)
|
||||||
|
ensure_init(dest)
|
||||||
|
|
||||||
|
|
||||||
|
def get_filter(opts=None):
|
||||||
|
opts = opts or []
|
||||||
|
if 'inc=*' in opts:
|
||||||
|
# do not filter any files, include everything
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _filter(dir, ls):
|
||||||
|
incs = [opt.split('=').pop() for opt in opts if 'inc=' in opt]
|
||||||
|
_filter = []
|
||||||
|
for f in ls:
|
||||||
|
_f = os.path.join(dir, f)
|
||||||
|
|
||||||
|
if not os.path.isdir(_f) and not _f.endswith('.py') and incs:
|
||||||
|
if True not in [fnmatch(_f, inc) for inc in incs]:
|
||||||
|
logging.debug('Not syncing %s, does not match include '
|
||||||
|
'filters (%s)' % (_f, incs))
|
||||||
|
_filter.append(f)
|
||||||
|
else:
|
||||||
|
logging.debug('Including file, which matches include '
|
||||||
|
'filters (%s): %s' % (incs, _f))
|
||||||
|
elif (os.path.isfile(_f) and not _f.endswith('.py')):
|
||||||
|
logging.debug('Not syncing file: %s' % f)
|
||||||
|
_filter.append(f)
|
||||||
|
elif (os.path.isdir(_f) and not
|
||||||
|
os.path.isfile(os.path.join(_f, '__init__.py'))):
|
||||||
|
logging.debug('Not syncing directory: %s' % f)
|
||||||
|
_filter.append(f)
|
||||||
|
return _filter
|
||||||
|
return _filter
|
||||||
|
|
||||||
|
|
||||||
|
def sync_directory(src, dest, opts=None):
|
||||||
|
if os.path.exists(dest):
|
||||||
|
logging.debug('Removing existing directory: %s' % dest)
|
||||||
|
shutil.rmtree(dest)
|
||||||
|
logging.info('Syncing directory: %s -> %s.' % (src, dest))
|
||||||
|
|
||||||
|
shutil.copytree(src, dest, ignore=get_filter(opts))
|
||||||
|
ensure_init(dest)
|
||||||
|
|
||||||
|
|
||||||
|
def sync(src, dest, module, opts=None):
|
||||||
|
|
||||||
|
# Sync charmhelpers/__init__.py for bootstrap code.
|
||||||
|
sync_pyfile(_src_path(src, '__init__'), dest)
|
||||||
|
|
||||||
|
# Sync other __init__.py files in the path leading to module.
|
||||||
|
m = []
|
||||||
|
steps = module.split('.')[:-1]
|
||||||
|
while steps:
|
||||||
|
m.append(steps.pop(0))
|
||||||
|
init = '.'.join(m + ['__init__'])
|
||||||
|
sync_pyfile(_src_path(src, init),
|
||||||
|
os.path.dirname(_dest_path(dest, init)))
|
||||||
|
|
||||||
|
# Sync the module, or maybe a .py file.
|
||||||
|
if os.path.isdir(_src_path(src, module)):
|
||||||
|
sync_directory(_src_path(src, module), _dest_path(dest, module), opts)
|
||||||
|
elif _is_pyfile(_src_path(src, module)):
|
||||||
|
sync_pyfile(_src_path(src, module),
|
||||||
|
os.path.dirname(_dest_path(dest, module)))
|
||||||
|
else:
|
||||||
|
logging.warn('Could not sync: %s. Neither a pyfile or directory, '
|
||||||
|
'does it even exist?' % module)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_sync_options(options):
|
||||||
|
if not options:
|
||||||
|
return []
|
||||||
|
return options.split(',')
|
||||||
|
|
||||||
|
|
||||||
|
def extract_options(inc, global_options=None):
|
||||||
|
global_options = global_options or []
|
||||||
|
if global_options and isinstance(global_options, six.string_types):
|
||||||
|
global_options = [global_options]
|
||||||
|
if '|' not in inc:
|
||||||
|
return (inc, global_options)
|
||||||
|
inc, opts = inc.split('|')
|
||||||
|
return (inc, parse_sync_options(opts) + global_options)
|
||||||
|
|
||||||
|
|
||||||
|
def sync_helpers(include, src, dest, options=None):
|
||||||
|
if not os.path.isdir(dest):
|
||||||
|
os.makedirs(dest)
|
||||||
|
|
||||||
|
global_options = parse_sync_options(options)
|
||||||
|
|
||||||
|
for inc in include:
|
||||||
|
if isinstance(inc, str):
|
||||||
|
inc, opts = extract_options(inc, global_options)
|
||||||
|
sync(src, dest, inc, opts)
|
||||||
|
elif isinstance(inc, dict):
|
||||||
|
# could also do nested dicts here.
|
||||||
|
for k, v in six.iteritems(inc):
|
||||||
|
if isinstance(v, list):
|
||||||
|
for m in v:
|
||||||
|
inc, opts = extract_options(m, global_options)
|
||||||
|
sync(src, dest, '%s.%s' % (k, inc), opts)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = optparse.OptionParser()
|
||||||
|
parser.add_option('-c', '--config', action='store', dest='config',
|
||||||
|
default=None, help='helper config file')
|
||||||
|
parser.add_option('-D', '--debug', action='store_true', dest='debug',
|
||||||
|
default=False, help='debug')
|
||||||
|
parser.add_option('-b', '--branch', action='store', dest='branch',
|
||||||
|
help='charm-helpers bzr branch (overrides config)')
|
||||||
|
parser.add_option('-d', '--destination', action='store', dest='dest_dir',
|
||||||
|
help='sync destination dir (overrides config)')
|
||||||
|
(opts, args) = parser.parse_args()
|
||||||
|
|
||||||
|
if opts.debug:
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
if opts.config:
|
||||||
|
logging.info('Loading charm helper config from %s.' % opts.config)
|
||||||
|
config = parse_config(opts.config)
|
||||||
|
if not config:
|
||||||
|
logging.error('Could not parse config from %s.' % opts.config)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
config = {}
|
||||||
|
|
||||||
|
if 'branch' not in config:
|
||||||
|
config['branch'] = CHARM_HELPERS_BRANCH
|
||||||
|
if opts.branch:
|
||||||
|
config['branch'] = opts.branch
|
||||||
|
if opts.dest_dir:
|
||||||
|
config['destination'] = opts.dest_dir
|
||||||
|
|
||||||
|
if 'destination' not in config:
|
||||||
|
logging.error('No destination dir. specified as option or config.')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if 'include' not in config:
|
||||||
|
if not args:
|
||||||
|
logging.error('No modules to sync specified as option or config.')
|
||||||
|
sys.exit(1)
|
||||||
|
config['include'] = []
|
||||||
|
[config['include'].append(a) for a in args]
|
||||||
|
|
||||||
|
sync_options = None
|
||||||
|
if 'options' in config:
|
||||||
|
sync_options = config['options']
|
||||||
|
tmpd = tempfile.mkdtemp()
|
||||||
|
try:
|
||||||
|
checkout = clone_helpers(tmpd, config['branch'])
|
||||||
|
sync_helpers(config['include'], checkout, config['destination'],
|
||||||
|
options=sync_options)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Could not sync: %s" % e)
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
logging.debug('Cleaning up %s' % tmpd)
|
||||||
|
shutil.rmtree(tmpd)
|
12
charm-helpers-sync.yaml
Normal file
12
charm-helpers-sync.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
branch: lp:charm-helpers
|
||||||
|
destination: hooks/charmhelpers
|
||||||
|
include:
|
||||||
|
- core
|
||||||
|
- fetch
|
||||||
|
- contrib.openstack|inc=*
|
||||||
|
- contrib.hahelpers
|
||||||
|
- contrib.network.ovs
|
||||||
|
- contrib.storage.linux
|
||||||
|
- payload.execd
|
||||||
|
- contrib.network.ip
|
||||||
|
- contrib.python.packages
|
5
config.yaml
Normal file
5
config.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
options:
|
||||||
|
external-interface:
|
||||||
|
default: eth1
|
||||||
|
type: string
|
||||||
|
description: The interface that will provide external connectivity
|
9
copyright
Normal file
9
copyright
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2012, Canonical Ltd.
|
||||||
|
License: GPL-3
|
||||||
|
|
||||||
|
License: GPL-3
|
||||||
|
On Debian GNU/Linux system you can find the complete text of the
|
||||||
|
GPL-3 license in '/usr/share/common-licenses/GPL-3'
|
1
hooks/config-changed
Symbolic link
1
hooks/config-changed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/install
Symbolic link
1
hooks/install
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/neutron-plugin-api-relation-broken
Symbolic link
1
hooks/neutron-plugin-api-relation-broken
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/neutron-plugin-api-relation-changed
Symbolic link
1
hooks/neutron-plugin-api-relation-changed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/neutron-plugin-api-relation-departed
Symbolic link
1
hooks/neutron-plugin-api-relation-departed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/neutron-plugin-api-relation-joined
Symbolic link
1
hooks/neutron-plugin-api-relation-joined
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
93
hooks/pg_gw_context.py
Normal file
93
hooks/pg_gw_context.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
relation_ids,
|
||||||
|
related_units,
|
||||||
|
relation_get,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
from charmhelpers.contrib.openstack import context
|
||||||
|
|
||||||
|
from socket import gethostname as get_unit_hostname
|
||||||
|
|
||||||
|
'''
|
||||||
|
#This function will be used to get information from neutron-api
|
||||||
|
def _neutron_api_settings():
|
||||||
|
neutron_settings = {
|
||||||
|
'neutron_security_groups': False,
|
||||||
|
'l2_population': True,
|
||||||
|
'overlay_network_type': 'gre',
|
||||||
|
}
|
||||||
|
for rid in relation_ids('neutron-plugin-api'):
|
||||||
|
for unit in related_units(rid):
|
||||||
|
rdata = relation_get(rid=rid, unit=unit)
|
||||||
|
if 'l2-population' not in rdata:
|
||||||
|
continue
|
||||||
|
neutron_settings = {
|
||||||
|
'l2_population': rdata['l2-population'],
|
||||||
|
'neutron_security_groups': rdata['neutron-security-groups'],
|
||||||
|
'overlay_network_type': rdata['overlay-network-type'],
|
||||||
|
}
|
||||||
|
# Override with configuration if set to true
|
||||||
|
if config('disable-security-groups'):
|
||||||
|
neutron_settings['neutron_security_groups'] = False
|
||||||
|
return neutron_settings
|
||||||
|
return neutron_settings
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def _pg_dir_settings():
|
||||||
|
'''
|
||||||
|
Inspects current neutron-plugin relation
|
||||||
|
'''
|
||||||
|
pg_settings = {
|
||||||
|
'pg_dir_ip': '192.168.100.201',
|
||||||
|
}
|
||||||
|
for rid in relation_ids('plumgrid'):
|
||||||
|
for unit in related_units(rid):
|
||||||
|
rdata = relation_get(rid=rid, unit=unit)
|
||||||
|
pg_settings = {
|
||||||
|
'pg_dir_ip': rdata['private-address'],
|
||||||
|
}
|
||||||
|
return pg_settings
|
||||||
|
|
||||||
|
|
||||||
|
class PGGwContext(context.NeutronContext):
|
||||||
|
interfaces = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugin(self):
|
||||||
|
return 'plumgrid'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def network_manager(self):
|
||||||
|
return 'neutron'
|
||||||
|
|
||||||
|
def _save_flag_file(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
#@property
|
||||||
|
#def neutron_security_groups(self):
|
||||||
|
# neutron_api_settings = _neutron_api_settings()
|
||||||
|
# return neutron_api_settings['neutron_security_groups']
|
||||||
|
|
||||||
|
def pg_ctxt(self):
|
||||||
|
#Generated Config for all Plumgrid templates inside
|
||||||
|
#the templates folder
|
||||||
|
pg_ctxt = super(PGGwContext, self).pg_ctxt()
|
||||||
|
if not pg_ctxt:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
conf = config()
|
||||||
|
pg_dir_settings = _pg_dir_settings()
|
||||||
|
pg_ctxt['local_ip'] = pg_dir_settings['pg_dir_ip']
|
||||||
|
|
||||||
|
#neutron_api_settings = _neutron_api_settings()
|
||||||
|
#TODO: Either get this value from the director or neutron-api charm
|
||||||
|
unit_hostname = get_unit_hostname()
|
||||||
|
pg_ctxt['pg_hostname'] = unit_hostname
|
||||||
|
pg_ctxt['interface'] = "juju-br0"
|
||||||
|
pg_ctxt['label'] = unit_hostname
|
||||||
|
pg_ctxt['fabric_mode'] = 'host'
|
||||||
|
|
||||||
|
pg_ctxt['ext_interface'] = conf['external-interface']
|
||||||
|
|
||||||
|
return pg_ctxt
|
47
hooks/pg_gw_hooks.py
Executable file
47
hooks/pg_gw_hooks.py
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
Hooks,
|
||||||
|
UnregisteredHookError,
|
||||||
|
log,
|
||||||
|
)
|
||||||
|
|
||||||
|
from pg_gw_utils import (
|
||||||
|
register_configs,
|
||||||
|
ensure_files,
|
||||||
|
restart_pg,
|
||||||
|
stop_pg,
|
||||||
|
)
|
||||||
|
|
||||||
|
hooks = Hooks()
|
||||||
|
CONFIGS = register_configs()
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook()
|
||||||
|
def install():
|
||||||
|
ensure_files()
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('plumgrid-plugin-relation-joined')
|
||||||
|
def plumgrid_dir():
|
||||||
|
ensure_files()
|
||||||
|
CONFIGS.write_all()
|
||||||
|
restart_pg()
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('stop')
|
||||||
|
def stop():
|
||||||
|
stop_pg()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
hooks.execute(sys.argv)
|
||||||
|
except UnregisteredHookError as e:
|
||||||
|
log('Unknown hook {} - skipping.'.format(e))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
128
hooks/pg_gw_utils.py
Normal file
128
hooks/pg_gw_utils.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
from charmhelpers.contrib.openstack.neutron import neutron_plugin_attribute
|
||||||
|
from copy import deepcopy
|
||||||
|
from charmhelpers.core.hookenv import log
|
||||||
|
from charmhelpers.core.host import (
|
||||||
|
write_file,
|
||||||
|
)
|
||||||
|
from charmhelpers.contrib.openstack import templating
|
||||||
|
from collections import OrderedDict
|
||||||
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
|
os_release,
|
||||||
|
)
|
||||||
|
import pg_gw_context
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
|
#Dont need these right now
|
||||||
|
NOVA_CONF_DIR = "/etc/nova"
|
||||||
|
NEUTRON_CONF_DIR = "/etc/neutron"
|
||||||
|
NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR
|
||||||
|
NEUTRON_DEFAULT = '/etc/default/neutron-server'
|
||||||
|
|
||||||
|
#Puppet Files
|
||||||
|
P_PGKA_CONF = '/opt/pg/etc/puppet/modules/sal/templates/keepalived.conf.erb'
|
||||||
|
P_PG_CONF = '/opt/pg/etc/puppet/modules/plumgrid/templates/plumgrid.conf.erb'
|
||||||
|
P_PGDEF_CONF = '/opt/pg/etc/puppet/modules/sal/templates/default.conf.erb'
|
||||||
|
|
||||||
|
#Plumgrid Files
|
||||||
|
PGKA_CONF = '/var/lib/libvirt/filesystems/plumgrid/etc/keepalived/keepalived.conf'
|
||||||
|
PG_CONF = '/var/lib/libvirt/filesystems/plumgrid/opt/pg/etc/plumgrid.conf'
|
||||||
|
PGDEF_CONF = '/var/lib/libvirt/filesystems/plumgrid/opt/pg/sal/nginx/conf.d/default.conf'
|
||||||
|
PGHN_CONF = '/var/lib/libvirt/filesystems/plumgrid-data/conf/etc/hostname'
|
||||||
|
PGHS_CONF = '/var/lib/libvirt/filesystems/plumgrid-data/conf/etc/hosts'
|
||||||
|
PGIFCS_CONF = '/var/lib/libvirt/filesystems/plumgrid-data/conf/pg/ifcs.conf'
|
||||||
|
IFCTL_CONF = '/var/run/plumgrid/lxc/ifc_list_gateway'
|
||||||
|
IFCTL_P_CONF = '/var/lib/libvirt/filesystems/plumgrid/var/run/plumgrid/lxc/ifc_list_gateway'
|
||||||
|
|
||||||
|
#EDGE SPECIFIC
|
||||||
|
SUDOERS_CONF = '/etc/sudoers.d/ifc_ctl_sudoers'
|
||||||
|
FILTERS_CONF_DIR = '/etc/nova/rootwrap.d'
|
||||||
|
FILTERS_CONF = '%s/network.filters' % FILTERS_CONF_DIR
|
||||||
|
|
||||||
|
BASE_RESOURCE_MAP = OrderedDict([
|
||||||
|
(PG_CONF, {
|
||||||
|
'services': ['plumgrid'],
|
||||||
|
'contexts': [pg_gw_context.PGGwContext()],
|
||||||
|
}),
|
||||||
|
(PGHN_CONF, {
|
||||||
|
'services': ['plumgrid'],
|
||||||
|
'contexts': [pg_gw_context.PGGwContext()],
|
||||||
|
}),
|
||||||
|
(PGHS_CONF, {
|
||||||
|
'services': ['plumgrid'],
|
||||||
|
'contexts': [pg_gw_context.PGGwContext()],
|
||||||
|
}),
|
||||||
|
(PGIFCS_CONF, {
|
||||||
|
'services': [],
|
||||||
|
'contexts': [pg_gw_context.PGGwContext()],
|
||||||
|
}),
|
||||||
|
(FILTERS_CONF, {
|
||||||
|
'services': [],
|
||||||
|
'contexts': [pg_gw_context.PGGwContext()],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
TEMPLATES = 'templates/'
|
||||||
|
|
||||||
|
|
||||||
|
def determine_packages():
|
||||||
|
return neutron_plugin_attribute('plumgrid', 'packages', 'neutron')
|
||||||
|
|
||||||
|
|
||||||
|
def register_configs(release=None):
|
||||||
|
release = release or os_release('neutron-common', base='icehouse')
|
||||||
|
configs = templating.OSConfigRenderer(templates_dir=TEMPLATES,
|
||||||
|
openstack_release=release)
|
||||||
|
for cfg, rscs in resource_map().iteritems():
|
||||||
|
configs.register(cfg, rscs['contexts'])
|
||||||
|
return configs
|
||||||
|
|
||||||
|
|
||||||
|
def resource_map():
|
||||||
|
'''
|
||||||
|
Dynamically generate a map of resources that will be managed for a single
|
||||||
|
hook execution.
|
||||||
|
'''
|
||||||
|
resource_map = deepcopy(BASE_RESOURCE_MAP)
|
||||||
|
return resource_map
|
||||||
|
|
||||||
|
|
||||||
|
def restart_map():
|
||||||
|
'''
|
||||||
|
Constructs a restart map based on charm config settings and relation
|
||||||
|
state.
|
||||||
|
'''
|
||||||
|
return {k: v['services'] for k, v in resource_map().iteritems()}
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_files():
|
||||||
|
_exec_cmd(cmd=['cp', '--remove-destination', '-f', P_PG_CONF, PG_CONF])
|
||||||
|
write_file(SUDOERS_CONF, "\nnova ALL=(root) NOPASSWD: /opt/pg/bin/ifc_ctl_pp *\n", owner='root', group='root', perms=0o644)
|
||||||
|
_exec_cmd(cmd=['mkdir', '-p', FILTERS_CONF_DIR])
|
||||||
|
_exec_cmd(cmd=['touch', FILTERS_CONF])
|
||||||
|
|
||||||
|
|
||||||
|
def restart_pg():
|
||||||
|
_exec_cmd(cmd=['virsh', '-c', 'lxc:', 'destroy', 'plumgrid'], error_msg='ERROR Destroying PLUMgrid')
|
||||||
|
_exec_cmd(cmd=['rm', IFCTL_CONF, IFCTL_P_CONF], error_msg='ERROR Removing ifc_ctl_gateway file')
|
||||||
|
_exec_cmd(cmd=['iptables', '-F'])
|
||||||
|
_exec_cmd(cmd=['virsh', '-c', 'lxc:', 'start', 'plumgrid'], error_msg='ERROR Starting PLUMgrid')
|
||||||
|
time.sleep(5)
|
||||||
|
_exec_cmd(cmd=['service', 'plumgrid', 'start'], error_msg='ERROR starting PLUMgrid service')
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
|
||||||
|
def stop_pg():
|
||||||
|
_exec_cmd(cmd=['virsh', '-c', 'lxc:', 'destroy', 'plumgrid'], error_msg='ERROR Destroying PLUMgrid')
|
||||||
|
time.sleep(2)
|
||||||
|
_exec_cmd(cmd=['rm', IFCTL_CONF, IFCTL_P_CONF], error_msg='ERROR Removing ifc_ctl_gateway file')
|
||||||
|
|
||||||
|
|
||||||
|
def _exec_cmd(cmd=None, error_msg='Command exited with ERRORs'):
|
||||||
|
if cmd is None:
|
||||||
|
log("NO command")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
except subprocess.CalledProcessError, e:
|
||||||
|
log(error_msg)
|
1
hooks/plumgrid-plugin-relation-broken
Symbolic link
1
hooks/plumgrid-plugin-relation-broken
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/plumgrid-plugin-relation-changed
Symbolic link
1
hooks/plumgrid-plugin-relation-changed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/plumgrid-plugin-relation-departed
Symbolic link
1
hooks/plumgrid-plugin-relation-departed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/plumgrid-plugin-relation-joined
Symbolic link
1
hooks/plumgrid-plugin-relation-joined
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/plumgrid-relation-broken
Symbolic link
1
hooks/plumgrid-relation-broken
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/plumgrid-relation-changed
Symbolic link
1
hooks/plumgrid-relation-changed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/plumgrid-relation-departed
Symbolic link
1
hooks/plumgrid-relation-departed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/plumgrid-relation-joined
Symbolic link
1
hooks/plumgrid-relation-joined
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/stop
Symbolic link
1
hooks/stop
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
1
hooks/upgrade-charm
Symbolic link
1
hooks/upgrade-charm
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
pg_gw_hooks.py
|
304
icon.svg
Normal file
304
icon.svg
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="96"
|
||||||
|
height="96"
|
||||||
|
id="svg6517"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="Gateway1.svg">
|
||||||
|
<defs
|
||||||
|
id="defs6519">
|
||||||
|
<linearGradient
|
||||||
|
id="Background">
|
||||||
|
<stop
|
||||||
|
id="stop4178"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#b8b8b8;stop-opacity:1" />
|
||||||
|
<stop
|
||||||
|
id="stop4180"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#c9c9c9;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<filter
|
||||||
|
style="color-interpolation-filters:sRGB;"
|
||||||
|
inkscape:label="Inner Shadow"
|
||||||
|
id="filter1121">
|
||||||
|
<feFlood
|
||||||
|
flood-opacity="0.59999999999999998"
|
||||||
|
flood-color="rgb(0,0,0)"
|
||||||
|
result="flood"
|
||||||
|
id="feFlood1123" />
|
||||||
|
<feComposite
|
||||||
|
in="flood"
|
||||||
|
in2="SourceGraphic"
|
||||||
|
operator="out"
|
||||||
|
result="composite1"
|
||||||
|
id="feComposite1125" />
|
||||||
|
<feGaussianBlur
|
||||||
|
in="composite1"
|
||||||
|
stdDeviation="1"
|
||||||
|
result="blur"
|
||||||
|
id="feGaussianBlur1127" />
|
||||||
|
<feOffset
|
||||||
|
dx="0"
|
||||||
|
dy="2"
|
||||||
|
result="offset"
|
||||||
|
id="feOffset1129" />
|
||||||
|
<feComposite
|
||||||
|
in="offset"
|
||||||
|
in2="SourceGraphic"
|
||||||
|
operator="atop"
|
||||||
|
result="composite2"
|
||||||
|
id="feComposite1131" />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
style="color-interpolation-filters:sRGB;"
|
||||||
|
inkscape:label="Drop Shadow"
|
||||||
|
id="filter950">
|
||||||
|
<feFlood
|
||||||
|
flood-opacity="0.25"
|
||||||
|
flood-color="rgb(0,0,0)"
|
||||||
|
result="flood"
|
||||||
|
id="feFlood952" />
|
||||||
|
<feComposite
|
||||||
|
in="flood"
|
||||||
|
in2="SourceGraphic"
|
||||||
|
operator="in"
|
||||||
|
result="composite1"
|
||||||
|
id="feComposite954" />
|
||||||
|
<feGaussianBlur
|
||||||
|
in="composite1"
|
||||||
|
stdDeviation="1"
|
||||||
|
result="blur"
|
||||||
|
id="feGaussianBlur956" />
|
||||||
|
<feOffset
|
||||||
|
dx="0"
|
||||||
|
dy="1"
|
||||||
|
result="offset"
|
||||||
|
id="feOffset958" />
|
||||||
|
<feComposite
|
||||||
|
in="SourceGraphic"
|
||||||
|
in2="offset"
|
||||||
|
operator="over"
|
||||||
|
result="composite2"
|
||||||
|
id="feComposite960" />
|
||||||
|
</filter>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath873">
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"
|
||||||
|
id="g875"
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">
|
||||||
|
<path
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"
|
||||||
|
d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"
|
||||||
|
id="path877"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="sssssssss" />
|
||||||
|
</g>
|
||||||
|
</clipPath>
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter891"
|
||||||
|
inkscape:label="Badge Shadow">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.71999962"
|
||||||
|
id="feGaussianBlur893" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="4.0745362"
|
||||||
|
inkscape:cx="57.131043"
|
||||||
|
inkscape:cy="49.018169"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:window-width="1366"
|
||||||
|
inkscape:window-height="705"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
showborder="true"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:showpageshadow="false">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid821" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="1,0"
|
||||||
|
position="16,48"
|
||||||
|
id="guide823" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="0,1"
|
||||||
|
position="64,80"
|
||||||
|
id="guide825" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="1,0"
|
||||||
|
position="80,40"
|
||||||
|
id="guide827" />
|
||||||
|
<sodipodi:guide
|
||||||
|
orientation="0,1"
|
||||||
|
position="64,16"
|
||||||
|
id="guide829" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata6522">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="BACKGROUND"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(268,-635.29076)"
|
||||||
|
style="display:inline">
|
||||||
|
<path
|
||||||
|
style="fill:#029bd6;fill-opacity:0.90980393;stroke:none;display:inline;filter:url(#filter1121)"
|
||||||
|
d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z"
|
||||||
|
id="path6455"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="sssssssss" />
|
||||||
|
<g
|
||||||
|
transform="matrix(1.2554482,0,0,1.2247945,-280.29427,621.82638)"
|
||||||
|
id="g3">
|
||||||
|
<g
|
||||||
|
id="g5">
|
||||||
|
<g
|
||||||
|
id="g7">
|
||||||
|
<polygon
|
||||||
|
style="fill:#ffffff"
|
||||||
|
points="72.643,41.599 72.643,24.878 56.501,25.459 60.839,29.796 49.781,40.854 56.813,47.891 67.872,36.83 "
|
||||||
|
id="polygon9" />
|
||||||
|
<polygon
|
||||||
|
style="fill:#ffffff"
|
||||||
|
points="40.048,74.195 23.327,74.195 23.908,58.055 28.246,62.393 39.299,51.34 46.332,58.372 35.279,69.426 "
|
||||||
|
id="polygon11" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g13">
|
||||||
|
<polygon
|
||||||
|
style="fill:#ffffff"
|
||||||
|
points="39.721,25.136 23.005,25.136 23.581,41.277 27.918,36.938 38.977,47.997 46.009,40.964 34.951,29.906 "
|
||||||
|
id="polygon15" />
|
||||||
|
<polygon
|
||||||
|
style="fill:#ffffff"
|
||||||
|
points="72.965,58.383 72.965,75.1 56.824,74.521 61.162,70.186 50.104,59.127 57.137,52.094 68.195,63.152 "
|
||||||
|
id="polygon17" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:10"
|
||||||
|
stroke-miterlimit="10"
|
||||||
|
cx="47.985001"
|
||||||
|
cy="49.990002"
|
||||||
|
r="10.165"
|
||||||
|
id="circle19" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="PLACE YOUR PICTOGRAM HERE"
|
||||||
|
style="display:inline" />
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="BADGE"
|
||||||
|
style="display:none"
|
||||||
|
sodipodi:insensitive="true">
|
||||||
|
<g
|
||||||
|
style="display:inline"
|
||||||
|
transform="translate(-340.00001,-581)"
|
||||||
|
id="g4394"
|
||||||
|
clip-path="none">
|
||||||
|
<g
|
||||||
|
id="g855">
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="maskhelper"
|
||||||
|
id="g870"
|
||||||
|
clip-path="url(#clipPath873)"
|
||||||
|
style="opacity:0.6;filter:url(#filter891)">
|
||||||
|
<path
|
||||||
|
transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"
|
||||||
|
d="m 264,552.36218 a 12,12 0 0 1 -12,12 12,12 0 0 1 -12,-12 12,12 0 0 1 12,-12 12,12 0 0 1 12,12 z"
|
||||||
|
sodipodi:ry="12"
|
||||||
|
sodipodi:rx="12"
|
||||||
|
sodipodi:cy="552.36218"
|
||||||
|
sodipodi:cx="252"
|
||||||
|
id="path844"
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
sodipodi:type="arc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g862">
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
id="path4398"
|
||||||
|
sodipodi:cx="252"
|
||||||
|
sodipodi:cy="552.36218"
|
||||||
|
sodipodi:rx="12"
|
||||||
|
sodipodi:ry="12"
|
||||||
|
d="m 264,552.36218 a 12,12 0 0 1 -12,12 12,12 0 0 1 -12,-12 12,12 0 0 1 12,-12 12,12 0 0 1 12,12 z"
|
||||||
|
transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />
|
||||||
|
<path
|
||||||
|
transform="matrix(1.25,0,0,1.25,33,-100.45273)"
|
||||||
|
d="m 264,552.36218 a 12,12 0 0 1 -12,12 12,12 0 0 1 -12,-12 12,12 0 0 1 12,-12 12,12 0 0 1 12,12 z"
|
||||||
|
sodipodi:ry="12"
|
||||||
|
sodipodi:rx="12"
|
||||||
|
sodipodi:cy="552.36218"
|
||||||
|
sodipodi:cx="252"
|
||||||
|
id="path4400"
|
||||||
|
style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
sodipodi:type="arc" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
id="path4459"
|
||||||
|
sodipodi:sides="5"
|
||||||
|
sodipodi:cx="666.19574"
|
||||||
|
sodipodi:cy="589.50385"
|
||||||
|
sodipodi:r1="7.2431178"
|
||||||
|
sodipodi:r2="4.3458705"
|
||||||
|
sodipodi:arg1="1.0471976"
|
||||||
|
sodipodi:arg2="1.6755161"
|
||||||
|
inkscape:flatsided="false"
|
||||||
|
inkscape:rounded="0.1"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 -0.18379,0.41279 0.0427,4.27917 -0.34859,4.5051 z"
|
||||||
|
transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 10 KiB |
24
metadata.yaml
Normal file
24
metadata.yaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
name: plumgrid-gateway
|
||||||
|
subordinate: false
|
||||||
|
maintainer: Bilal Baqar <bbaqar@plumgrid.com>
|
||||||
|
summary: "OpenStack Neutron OpenvSwitch Agent"
|
||||||
|
description: |
|
||||||
|
Neutron is a virtual network service for Openstack, and a part of
|
||||||
|
Netstack. Just like OpenStack Nova provides an API to dynamically
|
||||||
|
request and configure virtual servers, Neutron provides an API to
|
||||||
|
dynamically request and configure virtual networks. These networks
|
||||||
|
connect "interfaces" from other OpenStack services (e.g., virtual NICs
|
||||||
|
from Nova VMs). The Neutron API supports extensions to provide
|
||||||
|
advanced network capabilities (e.g., QoS, ACLs, network monitoring,
|
||||||
|
etc.)
|
||||||
|
.
|
||||||
|
This charm provides the Plumgrid Gateway
|
||||||
|
tags:
|
||||||
|
- openstack
|
||||||
|
requires:
|
||||||
|
plumgrid-plugin:
|
||||||
|
interface: plumgrid-plugin
|
||||||
|
plumgrid:
|
||||||
|
interface: plumgrid
|
||||||
|
neutron-plugin-api:
|
||||||
|
interface: neutron-plugin-api
|
5
setup.cfg
Normal file
5
setup.cfg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[nosetests]
|
||||||
|
verbosity=1
|
||||||
|
with-coverage=1
|
||||||
|
cover-erase=1
|
||||||
|
cover-package=hooks
|
2
templates/icehouse/hostname
Normal file
2
templates/icehouse/hostname
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{{ pg_hostname }}
|
||||||
|
|
10
templates/icehouse/hosts
Normal file
10
templates/icehouse/hosts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
127.0.0.1 localhost
|
||||||
|
127.0.1.1 {{ pg_hostname }}
|
||||||
|
|
||||||
|
# The following lines are desirable for IPv6 capable hosts
|
||||||
|
::1 ip6-localhost ip6-loopback
|
||||||
|
fe00::0 ip6-localnet
|
||||||
|
ff00::0 ip6-mcastprefix
|
||||||
|
ff02::1 ip6-allnodes
|
||||||
|
ff02::2 ip6-allrouters
|
||||||
|
|
6
templates/icehouse/ifcs.conf
Normal file
6
templates/icehouse/ifcs.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{{ interface }} = fabric_core host
|
||||||
|
{% if ext_interface -%}
|
||||||
|
{{ ext_interface }} = access_phys
|
||||||
|
|
||||||
|
{% endif -%}
|
||||||
|
|
94
templates/icehouse/network.filters
Normal file
94
templates/icehouse/network.filters
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# nova-rootwrap command filters for network nodes
|
||||||
|
# This file should be owned by (and only-writeable by) the root user
|
||||||
|
|
||||||
|
[Filters]
|
||||||
|
# nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap'
|
||||||
|
# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up'
|
||||||
|
# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev
|
||||||
|
# nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev)
|
||||||
|
# nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1]
|
||||||
|
# nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge
|
||||||
|
# nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', ..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ...
|
||||||
|
# nova/network/linux_net.py: 'ip', 'link', 'set', interface, address,..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up'
|
||||||
|
# nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up'
|
||||||
|
# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, ..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'link', 'set', dev, address, ..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up'
|
||||||
|
# nova/network/linux_net.py: 'ip', 'route', 'add', ..
|
||||||
|
# nova/network/linux_net.py: 'ip', 'route', 'del', .
|
||||||
|
# nova/network/linux_net.py: 'ip', 'route', 'show', 'dev', dev
|
||||||
|
ip: CommandFilter, ip, root
|
||||||
|
|
||||||
|
# nova/virt/libvirt/vif.py: 'ovs-vsctl', ...
|
||||||
|
# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ...
|
||||||
|
# nova/network/linux_net.py: 'ovs-vsctl', ....
|
||||||
|
ovs-vsctl: CommandFilter, ovs-vsctl, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'ovs-ofctl', ....
|
||||||
|
ovs-ofctl: CommandFilter, ovs-ofctl, root
|
||||||
|
|
||||||
|
# nova/virt/libvirt/vif.py: 'ivs-ctl', ...
|
||||||
|
# nova/virt/libvirt/vif.py: 'ivs-ctl', 'del-port', ...
|
||||||
|
# nova/network/linux_net.py: 'ivs-ctl', ....
|
||||||
|
ivs-ctl: CommandFilter, ivs-ctl, root
|
||||||
|
|
||||||
|
# nova/virt/libvirt/vif.py: 'ifc_ctl', ...
|
||||||
|
ifc_ctl: CommandFilter, /opt/pg/bin/ifc_ctl, root
|
||||||
|
|
||||||
|
# nova/virt/libvirt/vif.py: 'ebrctl', ...
|
||||||
|
ebrctl: CommandFilter, ebrctl, root
|
||||||
|
|
||||||
|
# nova/virt/libvirt/vif.py: 'mm-ctl', ...
|
||||||
|
mm-ctl: CommandFilter, mm-ctl, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'ebtables', '-D' ...
|
||||||
|
# nova/network/linux_net.py: 'ebtables', '-I' ...
|
||||||
|
ebtables: CommandFilter, ebtables, root
|
||||||
|
ebtables_usr: CommandFilter, ebtables, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
|
||||||
|
iptables-save: CommandFilter, iptables-save, root
|
||||||
|
ip6tables-save: CommandFilter, ip6tables-save, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
|
||||||
|
iptables-restore: CommandFilter, iptables-restore, root
|
||||||
|
ip6tables-restore: CommandFilter, ip6tables-restore, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
|
||||||
|
# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
|
||||||
|
arping: CommandFilter, arping, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
|
||||||
|
dhcp_release: CommandFilter, dhcp_release, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'kill', '-9', pid
|
||||||
|
# nova/network/linux_net.py: 'kill', '-HUP', pid
|
||||||
|
kill_dnsmasq: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'kill', pid
|
||||||
|
kill_radvd: KillFilter, root, /usr/sbin/radvd
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: dnsmasq call
|
||||||
|
dnsmasq: EnvFilter, env, root, CONFIG_FILE=, NETWORK_ID=, dnsmasq
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'..
|
||||||
|
radvd: CommandFilter, radvd, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'brctl', 'addbr', bridge
|
||||||
|
# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
|
||||||
|
# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
|
||||||
|
# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
|
||||||
|
brctl: CommandFilter, brctl, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'sysctl', ....
|
||||||
|
sysctl: CommandFilter, sysctl, root
|
||||||
|
|
||||||
|
# nova/network/linux_net.py: 'conntrack'
|
||||||
|
conntrack: CommandFilter, conntrack, root
|
11
templates/icehouse/plumgrid.conf
Normal file
11
templates/icehouse/plumgrid.conf
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
plumgrid_ip={{ local_ip }}
|
||||||
|
plumgrid_port=8001
|
||||||
|
mgmt_dev={{ interface }}
|
||||||
|
label={{ label}}
|
||||||
|
plumgrid_rsync_port=2222
|
||||||
|
plumgrid_rest_addr=0.0.0.0:9180
|
||||||
|
fabric_mode={{ fabric_mode }}
|
||||||
|
start_plumgrid_iovisor=yes
|
||||||
|
start_plumgrid=`/opt/pg/scripts/pg_is_director.sh $plumgrid_ip`
|
||||||
|
location=
|
||||||
|
|
21
templates/parts/rabbitmq
Normal file
21
templates/parts/rabbitmq
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% if rabbitmq_host or rabbitmq_hosts -%}
|
||||||
|
rabbit_userid = {{ rabbitmq_user }}
|
||||||
|
rabbit_virtual_host = {{ rabbitmq_virtual_host }}
|
||||||
|
rabbit_password = {{ rabbitmq_password }}
|
||||||
|
{% if rabbitmq_hosts -%}
|
||||||
|
rabbit_hosts = {{ rabbitmq_hosts }}
|
||||||
|
{% if rabbitmq_ha_queues -%}
|
||||||
|
rabbit_ha_queues = True
|
||||||
|
rabbit_durable_queues = False
|
||||||
|
{% endif -%}
|
||||||
|
{% else -%}
|
||||||
|
rabbit_host = {{ rabbitmq_host }}
|
||||||
|
{% endif -%}
|
||||||
|
{% if rabbit_ssl_port -%}
|
||||||
|
rabbit_use_ssl = True
|
||||||
|
rabbit_port = {{ rabbit_ssl_port }}
|
||||||
|
{% if rabbit_ssl_ca -%}
|
||||||
|
kombu_ssl_ca_certs = {{ rabbit_ssl_ca }}
|
||||||
|
{% endif -%}
|
||||||
|
{% endif -%}
|
||||||
|
{% endif -%}
|
5
tests/00-setup
Executable file
5
tests/00-setup
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
sudo add-apt-repository ppa:juju/stable -y
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install amulet python3-requests juju-deployer -y
|
39
tests/14-juno
Executable file
39
tests/14-juno
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import amulet
|
||||||
|
import requests
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestDeployment(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.deployment = amulet.Deployment(series='trusty')
|
||||||
|
cls.deployment.load_bundle_file(bundle_file='files/plumgrid-gateway.yaml', deployment_name='test')
|
||||||
|
try:
|
||||||
|
cls.deployment.setup(timeout=2000)
|
||||||
|
cls.deployment.sentry.wait()
|
||||||
|
except amulet.helpers.TimeoutError:
|
||||||
|
amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time")
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def test_plumgrid_gateway_external_interface(self):
|
||||||
|
external_interface = self.deployment.services['plumgrid-gateway']['options']['external-interface']
|
||||||
|
if not external_interface:
|
||||||
|
amulet.raise_status(amulet.FAIL, msg='plumgrid external-interface parameter was not found.')
|
||||||
|
output, code = self.deployment.sentry['plumgrid-gateway/0'].run("ethtool {}".format(external_interface))
|
||||||
|
if code != 0:
|
||||||
|
amulet.raise_status(amulet.FAIL, msg='external interface not found on the host')
|
||||||
|
|
||||||
|
def test_plumgrid_gateway_started(self):
|
||||||
|
agent_state = self.deployment.sentry['plumgrid-gateway/0'].info['agent-state']
|
||||||
|
if agent_state != 'started':
|
||||||
|
amulet.raise_status(amulet.FAIL, msg='plumgrid gateway is not in a started state')
|
||||||
|
|
||||||
|
def test_plumgrid_gateway_relation(self):
|
||||||
|
relation = self.deployment.sentry['plumgrid-gateway/0'].relation('plumgrid-plugin', 'neutron-iovisor:plumgrid-plugin')
|
||||||
|
if not relation['private-address']:
|
||||||
|
amulet.raise_status(amulet.FAIL, msg='private address was not set in the plumgrid gateway relation')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
98
tests/files/plumgrid-gateway.yaml
Normal file
98
tests/files/plumgrid-gateway.yaml
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
test:
|
||||||
|
series: 'trusty'
|
||||||
|
relations:
|
||||||
|
- - neutron-api
|
||||||
|
- neutron-iovisor
|
||||||
|
- - neutron-iovisor
|
||||||
|
- plumgrid-gateway
|
||||||
|
- - nova-cloud-controller
|
||||||
|
- nova-compute
|
||||||
|
- - glance
|
||||||
|
- nova-compute
|
||||||
|
- - nova-compute
|
||||||
|
- rabbitmq-server
|
||||||
|
- - mysql
|
||||||
|
- nova-compute
|
||||||
|
- - cinder
|
||||||
|
- nova-cloud-controller
|
||||||
|
- - nova-cloud-controller
|
||||||
|
- rabbitmq-server
|
||||||
|
- - glance
|
||||||
|
- nova-cloud-controller
|
||||||
|
- - keystone
|
||||||
|
- nova-cloud-controller
|
||||||
|
- - mysql
|
||||||
|
- nova-cloud-controller
|
||||||
|
- - neutron-api
|
||||||
|
- nova-cloud-controller
|
||||||
|
services:
|
||||||
|
cinder:
|
||||||
|
charm: cs:trusty/cinder
|
||||||
|
num_units: 1
|
||||||
|
options:
|
||||||
|
openstack-origin: cloud:trusty-juno
|
||||||
|
to: 'lxc:0'
|
||||||
|
glance:
|
||||||
|
charm: cs:trusty/glance
|
||||||
|
num_units: 1
|
||||||
|
options:
|
||||||
|
openstack-origin: cloud:trusty-juno
|
||||||
|
to: 'lxc:0'
|
||||||
|
keystone:
|
||||||
|
charm: cs:trusty/keystone
|
||||||
|
num_units: 1
|
||||||
|
options:
|
||||||
|
admin-password: plumgrid
|
||||||
|
openstack-origin: cloud:trusty-juno
|
||||||
|
to: 'lxc:0'
|
||||||
|
mysql:
|
||||||
|
charm: cs:trusty/mysql
|
||||||
|
num_units: 1
|
||||||
|
to: 'lxc:0'
|
||||||
|
neutron-api:
|
||||||
|
charm: cs:~juliann/trusty/neutron-api
|
||||||
|
num_units: 1
|
||||||
|
options:
|
||||||
|
install_keys: 'null'
|
||||||
|
install_sources: "deb http://10.22.24.200/debs ./"
|
||||||
|
neutron-plugin: "plumgrid"
|
||||||
|
neutron-security-groups: "true"
|
||||||
|
openstack-origin: "cloud:trusty-juno"
|
||||||
|
plumgrid-password: "plumgrid"
|
||||||
|
plumgrid-username: "plumgrid"
|
||||||
|
plumgrid-virtual-ip: "192.168.100.250"
|
||||||
|
to: 'lxc:0'
|
||||||
|
neutron-iovisor:
|
||||||
|
charm: cs:~juliann/trusty/neutron-iovisor
|
||||||
|
num_units: 1
|
||||||
|
options:
|
||||||
|
install_keys: 'null'
|
||||||
|
install_sources: "deb http://10.22.24.200/debs ./"
|
||||||
|
to: 'nova-compute'
|
||||||
|
plumgrid-gateway:
|
||||||
|
charm: cs:~juliann/trusty/plumgrid-gateway
|
||||||
|
num_units: 1
|
||||||
|
options:
|
||||||
|
external-interface: 'eth1'
|
||||||
|
to: 'nova-compute'
|
||||||
|
nova-cloud-controller:
|
||||||
|
charm: cs:trusty/nova-cloud-controller
|
||||||
|
num_units: 1
|
||||||
|
options:
|
||||||
|
console-access-protocol: novnc
|
||||||
|
network-manager: Neutron
|
||||||
|
openstack-origin: cloud:trusty-juno
|
||||||
|
quantum-security-groups: 'yes'
|
||||||
|
to: 'lxc:0'
|
||||||
|
nova-compute:
|
||||||
|
charm: cs:~juliann/trusty/nova-compute
|
||||||
|
num_units: 1
|
||||||
|
options:
|
||||||
|
enable-live-migration: true
|
||||||
|
enable-resize: true
|
||||||
|
migration-auth-type: ssh
|
||||||
|
openstack-origin: cloud:trusty-juno
|
||||||
|
rabbitmq-server:
|
||||||
|
charm: cs:trusty/rabbitmq-server
|
||||||
|
num_units: 1
|
||||||
|
to: 'lxc:0'
|
4
unit_tests/__init__.py
Normal file
4
unit_tests/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append('actions/')
|
||||||
|
sys.path.append('hooks/')
|
82
unit_tests/test_pg_gw_context.py
Normal file
82
unit_tests/test_pg_gw_context.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
from test_utils import CharmTestCase
|
||||||
|
from mock import patch
|
||||||
|
import pg_gw_context as context
|
||||||
|
import charmhelpers
|
||||||
|
|
||||||
|
TO_PATCH = [
|
||||||
|
#'_pg_dir_settings',
|
||||||
|
'config',
|
||||||
|
'get_unit_hostname',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def fake_context(settings):
|
||||||
|
def outer():
|
||||||
|
def inner():
|
||||||
|
return settings
|
||||||
|
return inner
|
||||||
|
return outer
|
||||||
|
|
||||||
|
|
||||||
|
class PGGwContextTest(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(PGGwContextTest, self).setUp(context, TO_PATCH)
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
self.test_config.set('external-interface', 'eth1')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(PGGwContextTest, self).tearDown()
|
||||||
|
|
||||||
|
@patch.object(context.PGGwContext, '_ensure_packages')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'https')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'is_clustered')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'config')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'unit_private_ip')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'unit_get')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'config_flags_parser')
|
||||||
|
@patch.object(context.PGGwContext, '_save_flag_file')
|
||||||
|
@patch.object(context, '_pg_dir_settings')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'neutron_plugin_attribute')
|
||||||
|
def test_neutroncc_context_api_rel(self, _npa, _pg_dir_settings, _save_flag_file,
|
||||||
|
_config_flag, _unit_get, _unit_priv_ip, _config,
|
||||||
|
_is_clus, _https, _ens_pkgs):
|
||||||
|
def mock_npa(plugin, section, manager):
|
||||||
|
if section == "driver":
|
||||||
|
return "neutron.randomdriver"
|
||||||
|
if section == "config":
|
||||||
|
return "neutron.randomconfig"
|
||||||
|
config = {'external-interface': "eth1"}
|
||||||
|
|
||||||
|
def mock_config(key=None):
|
||||||
|
if key:
|
||||||
|
return config.get(key)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
self.maxDiff = None
|
||||||
|
self.config.side_effect = mock_config
|
||||||
|
_npa.side_effect = mock_npa
|
||||||
|
_unit_get.return_value = '192.168.100.201'
|
||||||
|
_unit_priv_ip.return_value = '192.168.100.201'
|
||||||
|
self.get_unit_hostname.return_value = 'node0'
|
||||||
|
_is_clus.return_value = False
|
||||||
|
_config_flag.return_value = False
|
||||||
|
_pg_dir_settings.return_value = {'pg_dir_ip': '192.168.100.201'}
|
||||||
|
napi_ctxt = context.PGGwContext()
|
||||||
|
expect = {
|
||||||
|
'ext_interface': "eth1",
|
||||||
|
'config': 'neutron.randomconfig',
|
||||||
|
'core_plugin': 'neutron.randomdriver',
|
||||||
|
'local_ip': '192.168.100.201',
|
||||||
|
'network_manager': 'neutron',
|
||||||
|
'neutron_plugin': 'plumgrid',
|
||||||
|
'neutron_security_groups': None,
|
||||||
|
'neutron_url': 'https://192.168.100.201:9696',
|
||||||
|
'pg_hostname': 'node0',
|
||||||
|
'interface': 'juju-br0',
|
||||||
|
'label': 'node0',
|
||||||
|
'fabric_mode': 'host',
|
||||||
|
'neutron_alchemy_flags': False,
|
||||||
|
}
|
||||||
|
self.assertEquals(expect, napi_ctxt())
|
68
unit_tests/test_pg_gw_hooks.py
Normal file
68
unit_tests/test_pg_gw_hooks.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
from mock import MagicMock, patch
|
||||||
|
|
||||||
|
from test_utils import CharmTestCase
|
||||||
|
|
||||||
|
with patch('charmhelpers.core.hookenv.config') as config:
|
||||||
|
config.return_value = 'neutron'
|
||||||
|
import pg_gw_utils as utils
|
||||||
|
|
||||||
|
_reg = utils.register_configs
|
||||||
|
_map = utils.restart_map
|
||||||
|
|
||||||
|
utils.register_configs = MagicMock()
|
||||||
|
utils.restart_map = MagicMock()
|
||||||
|
|
||||||
|
import pg_gw_hooks as hooks
|
||||||
|
|
||||||
|
utils.register_configs = _reg
|
||||||
|
utils.restart_map = _map
|
||||||
|
|
||||||
|
TO_PATCH = [
|
||||||
|
#'apt_update',
|
||||||
|
#'apt_install',
|
||||||
|
#'apt_purge',
|
||||||
|
#'config',
|
||||||
|
'CONFIGS',
|
||||||
|
#'determine_packages',
|
||||||
|
#'determine_dvr_packages',
|
||||||
|
#'get_shared_secret',
|
||||||
|
#'git_install',
|
||||||
|
'log',
|
||||||
|
#'relation_ids',
|
||||||
|
#'relation_set',
|
||||||
|
#'configure_ovs',
|
||||||
|
#'use_dvr',
|
||||||
|
'ensure_files',
|
||||||
|
'stop_pg',
|
||||||
|
'restart_pg',
|
||||||
|
]
|
||||||
|
NEUTRON_CONF_DIR = "/etc/neutron"
|
||||||
|
|
||||||
|
NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR
|
||||||
|
|
||||||
|
|
||||||
|
class PGGwHooksTests(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(PGGwHooksTests, self).setUp(hooks, TO_PATCH)
|
||||||
|
|
||||||
|
#self.config.side_effect = self.test_config.get
|
||||||
|
hooks.hooks._config_save = False
|
||||||
|
|
||||||
|
def _call_hook(self, hookname):
|
||||||
|
hooks.hooks.execute([
|
||||||
|
'hooks/{}'.format(hookname)])
|
||||||
|
|
||||||
|
def test_install_hook(self):
|
||||||
|
self._call_hook('install')
|
||||||
|
self.ensure_files.assert_called_with()
|
||||||
|
|
||||||
|
def test_plumgrid_edge_joined(self):
|
||||||
|
self._call_hook('plumgrid-plugin-relation-joined')
|
||||||
|
self.ensure_files.assert_called_with()
|
||||||
|
self.CONFIGS.write_all.assert_called_with()
|
||||||
|
self.restart_pg.assert_called_with()
|
||||||
|
|
||||||
|
def test_stop(self):
|
||||||
|
self._call_hook('stop')
|
||||||
|
self.stop_pg.assert_called_with()
|
79
unit_tests/test_pg_gw_utils.py
Normal file
79
unit_tests/test_pg_gw_utils.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
from mock import MagicMock
|
||||||
|
from collections import OrderedDict
|
||||||
|
import charmhelpers.contrib.openstack.templating as templating
|
||||||
|
|
||||||
|
templating.OSConfigRenderer = MagicMock()
|
||||||
|
|
||||||
|
import pg_gw_utils as nutils
|
||||||
|
|
||||||
|
from test_utils import (
|
||||||
|
CharmTestCase,
|
||||||
|
)
|
||||||
|
import charmhelpers.core.hookenv as hookenv
|
||||||
|
|
||||||
|
|
||||||
|
TO_PATCH = [
|
||||||
|
'os_release',
|
||||||
|
'neutron_plugin_attribute',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DummyContext():
|
||||||
|
|
||||||
|
def __init__(self, return_value):
|
||||||
|
self.return_value = return_value
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
return self.return_value
|
||||||
|
|
||||||
|
|
||||||
|
class TestPGGwUtils(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestPGGwUtils, self).setUp(nutils, TO_PATCH)
|
||||||
|
#self.config.side_effect = self.test_config.get
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
# Reset cached cache
|
||||||
|
hookenv.cache = {}
|
||||||
|
|
||||||
|
def test_register_configs(self):
|
||||||
|
class _mock_OSConfigRenderer():
|
||||||
|
def __init__(self, templates_dir=None, openstack_release=None):
|
||||||
|
self.configs = []
|
||||||
|
self.ctxts = []
|
||||||
|
|
||||||
|
def register(self, config, ctxt):
|
||||||
|
self.configs.append(config)
|
||||||
|
self.ctxts.append(ctxt)
|
||||||
|
|
||||||
|
self.os_release.return_value = 'trusty'
|
||||||
|
templating.OSConfigRenderer.side_effect = _mock_OSConfigRenderer
|
||||||
|
_regconfs = nutils.register_configs()
|
||||||
|
confs = ['/var/lib/libvirt/filesystems/plumgrid/opt/pg/etc/plumgrid.conf',
|
||||||
|
'/var/lib/libvirt/filesystems/plumgrid-data/conf/etc/hostname',
|
||||||
|
'/var/lib/libvirt/filesystems/plumgrid-data/conf/etc/hosts',
|
||||||
|
'/var/lib/libvirt/filesystems/plumgrid-data/conf/pg/ifcs.conf',
|
||||||
|
'/etc/nova/rootwrap.d/network.filters']
|
||||||
|
self.assertItemsEqual(_regconfs.configs, confs)
|
||||||
|
|
||||||
|
def test_resource_map(self):
|
||||||
|
_map = nutils.resource_map()
|
||||||
|
svcs = ['plumgrid']
|
||||||
|
confs = [nutils.PG_CONF]
|
||||||
|
[self.assertIn(q_conf, _map.keys()) for q_conf in confs]
|
||||||
|
self.assertEqual(_map[nutils.PG_CONF]['services'], svcs)
|
||||||
|
|
||||||
|
def test_restart_map(self):
|
||||||
|
_restart_map = nutils.restart_map()
|
||||||
|
expect = OrderedDict([
|
||||||
|
(nutils.PG_CONF, ['plumgrid']),
|
||||||
|
(nutils.PGHN_CONF, ['plumgrid']),
|
||||||
|
(nutils.PGHS_CONF, ['plumgrid']),
|
||||||
|
(nutils.PGIFCS_CONF, []),
|
||||||
|
(nutils.FILTERS_CONF, []),
|
||||||
|
])
|
||||||
|
self.assertEqual(expect, _restart_map)
|
||||||
|
for item in _restart_map:
|
||||||
|
self.assertTrue(item in _restart_map)
|
||||||
|
self.assertTrue(expect[item] == _restart_map[item])
|
121
unit_tests/test_utils.py
Normal file
121
unit_tests/test_utils.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import logging
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from mock import patch, MagicMock
|
||||||
|
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
'''
|
||||||
|
Walk backwords from __file__ looking for config.yaml, load and return the
|
||||||
|
'options' section'
|
||||||
|
'''
|
||||||
|
config = None
|
||||||
|
f = __file__
|
||||||
|
while config is None:
|
||||||
|
d = os.path.dirname(f)
|
||||||
|
if os.path.isfile(os.path.join(d, 'config.yaml')):
|
||||||
|
config = os.path.join(d, 'config.yaml')
|
||||||
|
break
|
||||||
|
f = d
|
||||||
|
|
||||||
|
if not config:
|
||||||
|
logging.error('Could not find config.yaml in any parent directory '
|
||||||
|
'of %s. ' % file)
|
||||||
|
raise Exception
|
||||||
|
|
||||||
|
return yaml.safe_load(open(config).read())['options']
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_config():
|
||||||
|
'''
|
||||||
|
Load default charm config from config.yaml return as a dict.
|
||||||
|
If no default is set in config.yaml, its value is None.
|
||||||
|
'''
|
||||||
|
default_config = {}
|
||||||
|
config = load_config()
|
||||||
|
for k, v in config.iteritems():
|
||||||
|
if 'default' in v:
|
||||||
|
default_config[k] = v['default']
|
||||||
|
else:
|
||||||
|
default_config[k] = None
|
||||||
|
return default_config
|
||||||
|
|
||||||
|
|
||||||
|
class CharmTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self, obj, patches):
|
||||||
|
super(CharmTestCase, self).setUp()
|
||||||
|
self.patches = patches
|
||||||
|
self.obj = obj
|
||||||
|
self.test_config = TestConfig()
|
||||||
|
self.test_relation = TestRelation()
|
||||||
|
self.patch_all()
|
||||||
|
|
||||||
|
def patch(self, method):
|
||||||
|
_m = patch.object(self.obj, method)
|
||||||
|
mock = _m.start()
|
||||||
|
self.addCleanup(_m.stop)
|
||||||
|
return mock
|
||||||
|
|
||||||
|
def patch_all(self):
|
||||||
|
for method in self.patches:
|
||||||
|
setattr(self, method, self.patch(method))
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfig(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = get_default_config()
|
||||||
|
|
||||||
|
def get(self, attr=None):
|
||||||
|
if not attr:
|
||||||
|
return self.get_all()
|
||||||
|
try:
|
||||||
|
return self.config[attr]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_all(self):
|
||||||
|
return self.config
|
||||||
|
|
||||||
|
def set(self, attr, value):
|
||||||
|
if attr not in self.config:
|
||||||
|
raise KeyError
|
||||||
|
self.config[attr] = value
|
||||||
|
|
||||||
|
|
||||||
|
class TestRelation(object):
|
||||||
|
|
||||||
|
def __init__(self, relation_data={}):
|
||||||
|
self.relation_data = relation_data
|
||||||
|
|
||||||
|
def set(self, relation_data):
|
||||||
|
self.relation_data = relation_data
|
||||||
|
|
||||||
|
def get(self, attribute=None, unit=None, rid=None):
|
||||||
|
if attribute is None:
|
||||||
|
return self.relation_data
|
||||||
|
elif attribute in self.relation_data:
|
||||||
|
return self.relation_data[attribute]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def patch_open():
|
||||||
|
'''Patch open() to allow mocking both open() itself and the file that is
|
||||||
|
yielded.
|
||||||
|
|
||||||
|
Yields the mock for "open" and "file", respectively.'''
|
||||||
|
mock_open = MagicMock(spec=open)
|
||||||
|
mock_file = MagicMock(spec=file)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def stub_open(*args, **kwargs):
|
||||||
|
mock_open(*args, **kwargs)
|
||||||
|
yield mock_file
|
||||||
|
|
||||||
|
with patch('__builtin__.open', stub_open):
|
||||||
|
yield mock_open, mock_file
|
Loading…
x
Reference in New Issue
Block a user