Merge with ~openstack-charmers/charms/trusty/neutron-api/next
This commit is contained in:
commit
ce0b5b9dcd
|
@ -1,2 +1,3 @@
|
|||
bin
|
||||
.coverage
|
||||
tags
|
||||
|
|
6
Makefile
6
Makefile
|
@ -2,7 +2,7 @@
|
|||
PYTHON := /usr/bin/env python
|
||||
|
||||
lint:
|
||||
@flake8 --exclude hooks/charmhelpers hooks unit_tests tests
|
||||
@flake8 --exclude hooks/charmhelpers actions hooks unit_tests tests
|
||||
@charm proof
|
||||
|
||||
unit_test:
|
||||
|
@ -23,9 +23,7 @@ test:
|
|||
# coreycb note: The -v should only be temporary until Amulet sends
|
||||
# raise_status() messages to stderr:
|
||||
# https://bugs.launchpad.net/amulet/+bug/1320357
|
||||
@juju test -v -p AMULET_HTTP_PROXY --timeout 900 \
|
||||
00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse \
|
||||
16-basic-trusty-juno
|
||||
@juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700
|
||||
|
||||
publish: lint unit_test
|
||||
bzr push lp:charms/neutron-api
|
||||
|
|
91
README.md
91
README.md
|
@ -23,6 +23,97 @@ This charm also supports scale out and high availability using the hacluster cha
|
|||
juju set neutron-api vip=<VIP FOR ACCESS>
|
||||
juju add-relation neutron-hacluster neutron-api
|
||||
|
||||
# Deploying from source
|
||||
|
||||
The minimum openstack-origin-git config required to deploy from source is:
|
||||
|
||||
openstack-origin-git: include-file://neutron-juno.yaml
|
||||
|
||||
neutron-juno.yaml
|
||||
repositories:
|
||||
- {name: requirements,
|
||||
repository: 'git://github.com/openstack/requirements',
|
||||
branch: stable/juno}
|
||||
- {name: neutron,
|
||||
repository: 'git://github.com/openstack/neutron',
|
||||
branch: stable/juno}
|
||||
|
||||
Note that there are only two 'name' values the charm knows about: 'requirements'
|
||||
and 'neutron'. These repositories must correspond to these 'name' values.
|
||||
Additionally, the requirements repository must be specified first and the
|
||||
neutron repository must be specified last. All other repostories are installed
|
||||
in the order in which they are specified.
|
||||
|
||||
The following is a full list of current tip repos (may not be up-to-date):
|
||||
|
||||
openstack-origin-git: include-file://neutron-master.yaml
|
||||
|
||||
neutron-master.yaml
|
||||
repositories:
|
||||
- {name: requirements,
|
||||
repository: 'git://github.com/openstack/requirements',
|
||||
branch: master}
|
||||
- {name: oslo-concurrency,
|
||||
repository: 'git://github.com/openstack/oslo.concurrency',
|
||||
branch: master}
|
||||
- {name: oslo-config,
|
||||
repository: 'git://github.com/openstack/oslo.config',
|
||||
branch: master}
|
||||
- {name: oslo-context,
|
||||
repository: 'git://github.com/openstack/oslo.context',
|
||||
branch: master}
|
||||
- {name: oslo-db,
|
||||
repository: 'git://github.com/openstack/oslo.db',
|
||||
branch: master}
|
||||
- {name: oslo-i18n,
|
||||
repository: 'git://github.com/openstack/oslo.i18n',
|
||||
branch: master}
|
||||
- {name: oslo-messaging,
|
||||
repository: 'git://github.com/openstack/oslo.messaging',
|
||||
branch: master}
|
||||
- {name: oslo-middleware,
|
||||
repository': 'git://github.com/openstack/oslo.middleware',
|
||||
branch: master}
|
||||
- {name: oslo-rootwrap',
|
||||
repository: 'git://github.com/openstack/oslo.rootwrap',
|
||||
branch: master}
|
||||
- {name: oslo-serialization,
|
||||
repository: 'git://github.com/openstack/oslo.serialization',
|
||||
branch: master}
|
||||
- {name: oslo-utils,
|
||||
repository: 'git://github.com/openstack/oslo.utils',
|
||||
branch: master}
|
||||
- {name: pbr,
|
||||
repository: 'git://github.com/openstack-dev/pbr',
|
||||
branch: master}
|
||||
- {name: stevedore,
|
||||
repository: 'git://github.com/openstack/stevedore',
|
||||
branch: 'master'}
|
||||
- {name: python-keystoneclient,
|
||||
repository: 'git://github.com/openstack/python-keystoneclient',
|
||||
branch: master}
|
||||
- {name: python-neutronclient,
|
||||
repository: 'git://github.com/openstack/python-neutronclient',
|
||||
branch: master}
|
||||
- {name: python-novaclient,
|
||||
repository': 'git://github.com/openstack/python-novaclient',
|
||||
branch: master}
|
||||
- {name: keystonemiddleware,
|
||||
repository: 'git://github.com/openstack/keystonemiddleware',
|
||||
branch: master}
|
||||
- {name: neutron-fwaas,
|
||||
repository': 'git://github.com/openstack/neutron-fwaas',
|
||||
branch: master}
|
||||
- {name: neutron-lbaas,
|
||||
repository: 'git://github.com/openstack/neutron-lbaas',
|
||||
branch: master}
|
||||
- {name: neutron-vpnaas,
|
||||
repository: 'git://github.com/openstack/neutron-vpnaas',
|
||||
branch: master}
|
||||
- {name: neutron,
|
||||
repository: 'git://github.com/openstack/neutron',
|
||||
branch: master}
|
||||
|
||||
# Restrictions
|
||||
|
||||
This charm only support deployment with OpenStack Icehouse or better.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
git-reinstall:
|
||||
description: Reinstall neutron-api from the openstack-origin-git repositories.
|
|
@ -0,0 +1 @@
|
|||
git_reinstall.py
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
sys.path.append('hooks/')
|
||||
|
||||
from charmhelpers.contrib.openstack.utils import (
|
||||
git_install_requested,
|
||||
)
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
action_set,
|
||||
action_fail,
|
||||
config,
|
||||
)
|
||||
|
||||
from neutron_api_utils import (
|
||||
git_install,
|
||||
)
|
||||
|
||||
from neutron_api_hooks import (
|
||||
config_changed,
|
||||
)
|
||||
|
||||
|
||||
def git_reinstall():
|
||||
"""Reinstall from source and restart services.
|
||||
|
||||
If the openstack-origin-git config option was used to install openstack
|
||||
from source git repositories, then this action can be used to reinstall
|
||||
from updated git repositories, followed by a restart of services."""
|
||||
if not git_install_requested():
|
||||
action_fail('openstack-origin-git is not configured')
|
||||
return
|
||||
|
||||
try:
|
||||
git_install(config('openstack-origin-git'))
|
||||
config_changed()
|
||||
except:
|
||||
action_set({'traceback': traceback.format_exc()})
|
||||
action_fail('git-reinstall resulted in an unexpected error')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
git_reinstall()
|
34
config.yaml
34
config.yaml
|
@ -14,6 +14,22 @@ options:
|
|||
Note that updating this setting to a source that is known to
|
||||
provide a later version of OpenStack will trigger a software
|
||||
upgrade.
|
||||
|
||||
Note that when openstack-origin-git is specified, openstack
|
||||
specific packages will be installed from source rather than
|
||||
from the openstack-origin repository.
|
||||
openstack-origin-git:
|
||||
default:
|
||||
type: string
|
||||
description: |
|
||||
Specifies a YAML-formatted dictionary listing the git
|
||||
repositories and branches from which to install OpenStack and
|
||||
its dependencies.
|
||||
|
||||
Note that the installed config files will be determined based on
|
||||
the OpenStack release of the openstack-origin option.
|
||||
|
||||
For more details see README.md.
|
||||
rabbit-user:
|
||||
default: neutron
|
||||
type: string
|
||||
|
@ -69,10 +85,10 @@ options:
|
|||
default: gre
|
||||
type: string
|
||||
description: |
|
||||
Overlay network type to use choose one of:
|
||||
Overlay network types to use, valid options include:
|
||||
.
|
||||
gre
|
||||
vxlan
|
||||
vxlan
|
||||
.
|
||||
neutron-plugin-ppa-key:
|
||||
type: string
|
||||
|
@ -86,6 +102,20 @@ options:
|
|||
type: string
|
||||
default:
|
||||
description: Optional URL to Neutron Plugin containing python packages.
|
||||
Multiple types can be provided - field is space delimited.
|
||||
flat-network-providers:
|
||||
type: string
|
||||
default:
|
||||
description: |
|
||||
Space-delimited list of Neutron flat network providers.
|
||||
vlan-ranges:
|
||||
type: string
|
||||
default: "physnet1:1000:2000"
|
||||
description: |
|
||||
Space-delimited list of <physical_network>:<vlan_min>:<vlan_max> or
|
||||
<physical_network> specifying physical_network names usable for VLAN
|
||||
provider and tenant networks, as well as ranges of VLAN tags on each
|
||||
available for allocation to tenant networks.
|
||||
# Quota configuration settings
|
||||
quota-security-group:
|
||||
default: 10
|
||||
|
|
|
@ -247,7 +247,9 @@ class NRPE(object):
|
|||
|
||||
service('restart', 'nagios-nrpe-server')
|
||||
|
||||
for rid in relation_ids("local-monitors"):
|
||||
monitor_ids = relation_ids("local-monitors") + \
|
||||
relation_ids("nrpe-external-master")
|
||||
for rid in monitor_ids:
|
||||
relation_set(relation_id=rid, monitors=yaml.dump(monitors))
|
||||
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ from charmhelpers.core.strutils import (
|
|||
bool_from_string,
|
||||
)
|
||||
|
||||
DC_RESOURCE_NAME = 'DC'
|
||||
|
||||
|
||||
class HAIncompleteConfig(Exception):
|
||||
pass
|
||||
|
@ -95,6 +97,27 @@ def is_clustered():
|
|||
return False
|
||||
|
||||
|
||||
def is_crm_dc():
|
||||
"""
|
||||
Determine leadership by querying the pacemaker Designated Controller
|
||||
"""
|
||||
cmd = ['crm', 'status']
|
||||
try:
|
||||
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
if not isinstance(status, six.text_type):
|
||||
status = six.text_type(status, "utf-8")
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
current_dc = ''
|
||||
for line in status.split('\n'):
|
||||
if line.startswith('Current DC'):
|
||||
# Current DC: juju-lytrusty-machine-2 (168108163) - partition with quorum
|
||||
current_dc = line.split(':')[1].split()[0]
|
||||
if current_dc == get_unit_hostname():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound)
|
||||
def is_crm_leader(resource, retry=False):
|
||||
"""
|
||||
|
@ -104,6 +127,8 @@ def is_crm_leader(resource, retry=False):
|
|||
We allow this operation to be retried to avoid the possibility of getting a
|
||||
false negative. See LP #1396246 for more info.
|
||||
"""
|
||||
if resource == DC_RESOURCE_NAME:
|
||||
return is_crm_dc()
|
||||
cmd = ['crm', 'resource', 'show', resource]
|
||||
try:
|
||||
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
|
|
|
@ -44,17 +44,24 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||
Determine if the local branch being tested is derived from its
|
||||
stable or next (dev) branch, and based on this, use the corresonding
|
||||
stable or next branches for the other_services."""
|
||||
base_charms = ['mysql', 'mongodb', 'rabbitmq-server']
|
||||
base_charms = ['mysql', 'mongodb']
|
||||
|
||||
if self.series in ['precise', 'trusty']:
|
||||
base_series = self.series
|
||||
else:
|
||||
base_series = self.current_next
|
||||
|
||||
if self.stable:
|
||||
for svc in other_services:
|
||||
temp = 'lp:charms/{}'
|
||||
svc['location'] = temp.format(svc['name'])
|
||||
temp = 'lp:charms/{}/{}'
|
||||
svc['location'] = temp.format(base_series,
|
||||
svc['name'])
|
||||
else:
|
||||
for svc in other_services:
|
||||
if svc['name'] in base_charms:
|
||||
temp = 'lp:charms/{}'
|
||||
svc['location'] = temp.format(svc['name'])
|
||||
temp = 'lp:charms/{}/{}'
|
||||
svc['location'] = temp.format(base_series,
|
||||
svc['name'])
|
||||
else:
|
||||
temp = 'lp:~openstack-charmers/charms/{}/{}/next'
|
||||
svc['location'] = temp.format(self.current_next,
|
||||
|
@ -99,9 +106,12 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||
Return an integer representing the enum value of the openstack
|
||||
release.
|
||||
"""
|
||||
# Must be ordered by OpenStack release (not by Ubuntu release):
|
||||
(self.precise_essex, self.precise_folsom, self.precise_grizzly,
|
||||
self.precise_havana, self.precise_icehouse,
|
||||
self.trusty_icehouse, self.trusty_juno, self.trusty_kilo) = range(8)
|
||||
self.trusty_icehouse, self.trusty_juno, self.utopic_juno,
|
||||
self.trusty_kilo, self.vivid_kilo) = range(10)
|
||||
|
||||
releases = {
|
||||
('precise', None): self.precise_essex,
|
||||
('precise', 'cloud:precise-folsom'): self.precise_folsom,
|
||||
|
@ -110,7 +120,9 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||
('precise', 'cloud:precise-icehouse'): self.precise_icehouse,
|
||||
('trusty', None): self.trusty_icehouse,
|
||||
('trusty', 'cloud:trusty-juno'): self.trusty_juno,
|
||||
('trusty', 'cloud:trusty-kilo'): self.trusty_kilo}
|
||||
('trusty', 'cloud:trusty-kilo'): self.trusty_kilo,
|
||||
('utopic', None): self.utopic_juno,
|
||||
('vivid', None): self.vivid_kilo}
|
||||
return releases[(self.series, self.openstack)]
|
||||
|
||||
def _get_openstack_release_string(self):
|
||||
|
|
|
@ -320,14 +320,15 @@ def db_ssl(rdata, ctxt, ssl_dir):
|
|||
|
||||
|
||||
class IdentityServiceContext(OSContextGenerator):
|
||||
interfaces = ['identity-service']
|
||||
|
||||
def __init__(self, service=None, service_user=None):
|
||||
def __init__(self, service=None, service_user=None, rel_name='identity-service'):
|
||||
self.service = service
|
||||
self.service_user = service_user
|
||||
self.rel_name = rel_name
|
||||
self.interfaces = [self.rel_name]
|
||||
|
||||
def __call__(self):
|
||||
log('Generating template context for identity-service', level=DEBUG)
|
||||
log('Generating template context for ' + self.rel_name, level=DEBUG)
|
||||
ctxt = {}
|
||||
|
||||
if self.service and self.service_user:
|
||||
|
@ -341,7 +342,7 @@ class IdentityServiceContext(OSContextGenerator):
|
|||
|
||||
ctxt['signing_dir'] = cachedir
|
||||
|
||||
for rid in relation_ids('identity-service'):
|
||||
for rid in relation_ids(self.rel_name):
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
serv_host = rdata.get('service_host')
|
||||
|
@ -458,6 +459,11 @@ class AMQPContext(OSContextGenerator):
|
|||
|
||||
ctxt['rabbitmq_hosts'] = ','.join(sorted(rabbitmq_hosts))
|
||||
|
||||
oslo_messaging_flags = conf.get('oslo-messaging-flags', None)
|
||||
if oslo_messaging_flags:
|
||||
ctxt['oslo_messaging_flags'] = config_flags_parser(
|
||||
oslo_messaging_flags)
|
||||
|
||||
if not context_complete(ctxt):
|
||||
return {}
|
||||
|
||||
|
@ -812,13 +818,12 @@ class NeutronContext(OSContextGenerator):
|
|||
self.network_manager)
|
||||
config = neutron_plugin_attribute(self.plugin, 'config',
|
||||
self.network_manager)
|
||||
nuage_ctxt = {
|
||||
'core_plugin': driver,
|
||||
'neutron_plugin': 'vsp',
|
||||
'neutron_security_groups': self.neutron_security_groups,
|
||||
'local_ip': unit_private_ip(),
|
||||
'config': config
|
||||
}
|
||||
nuage_ctxt = {'core_plugin': driver,
|
||||
'neutron_plugin': 'vsp',
|
||||
'neutron_security_groups': self.neutron_security_groups,
|
||||
'local_ip': unit_private_ip(),
|
||||
'config': config}
|
||||
|
||||
return nuage_ctxt
|
||||
|
||||
def nvp_ctxt(self):
|
||||
|
@ -898,14 +903,14 @@ class NeutronContext(OSContextGenerator):
|
|||
|
||||
if self.plugin == 'ovs':
|
||||
ctxt.update(self.ovs_ctxt())
|
||||
elif self.plugin == 'vsp':
|
||||
ctxt.update(self.nuage_ctxt())
|
||||
elif self.plugin in ['nvp', 'nsx']:
|
||||
ctxt.update(self.nvp_ctxt())
|
||||
elif self.plugin == 'n1kv':
|
||||
ctxt.update(self.n1kv_ctxt())
|
||||
elif self.plugin == 'Calico':
|
||||
ctxt.update(self.calico_ctxt())
|
||||
elif self.plugin == 'vsp':
|
||||
ctxt.update(self.nuage_ctxt())
|
||||
|
||||
alchemy_flags = config('neutron-alchemy-flags')
|
||||
if alchemy_flags:
|
||||
|
|
|
@ -194,6 +194,19 @@ def neutron_plugins():
|
|||
'nova-api-metadata']],
|
||||
'server_packages': ['neutron-server', 'calico-control'],
|
||||
'server_services': ['neutron-server']
|
||||
},
|
||||
'vsp': {
|
||||
'config': '/etc/neutron/plugins/nuage/nuage_plugin.ini',
|
||||
'driver': 'neutron.plugins.nuage.plugin.NuagePlugin',
|
||||
'contexts': [
|
||||
context.SharedDBContext(user=config('neutron-database-user'),
|
||||
database=config('neutron-database'),
|
||||
relation_prefix='neutron',
|
||||
ssl_dir=NEUTRON_CONF_DIR)],
|
||||
'services': [],
|
||||
'packages': [],
|
||||
'server_packages': ['neutron-server', 'neutron-plugin-nuage'],
|
||||
'server_services': ['neutron-server']
|
||||
}
|
||||
}
|
||||
if release >= 'icehouse':
|
||||
|
@ -257,11 +270,14 @@ def network_manager():
|
|||
def parse_mappings(mappings):
|
||||
parsed = {}
|
||||
if mappings:
|
||||
mappings = mappings.split(' ')
|
||||
mappings = mappings.split()
|
||||
for m in mappings:
|
||||
p = m.partition(':')
|
||||
if p[1] == ':':
|
||||
parsed[p[0].strip()] = p[2].strip()
|
||||
key = p[0].strip()
|
||||
if p[1]:
|
||||
parsed[key] = p[2].strip()
|
||||
else:
|
||||
parsed[key] = ''
|
||||
|
||||
return parsed
|
||||
|
||||
|
@ -284,13 +300,13 @@ def parse_data_port_mappings(mappings, default_bridge='br-data'):
|
|||
Returns dict of the form {bridge:port}.
|
||||
"""
|
||||
_mappings = parse_mappings(mappings)
|
||||
if not _mappings:
|
||||
if not _mappings or list(_mappings.values()) == ['']:
|
||||
if not mappings:
|
||||
return {}
|
||||
|
||||
# For backwards-compatibility we need to support port-only provided in
|
||||
# config.
|
||||
_mappings = {default_bridge: mappings.split(' ')[0]}
|
||||
_mappings = {default_bridge: mappings.split()[0]}
|
||||
|
||||
bridges = _mappings.keys()
|
||||
ports = _mappings.values()
|
||||
|
@ -310,6 +326,8 @@ def parse_vlan_range_mappings(mappings):
|
|||
|
||||
Mappings must be a space-delimited list of provider:start:end mappings.
|
||||
|
||||
The start:end range is optional and may be omitted.
|
||||
|
||||
Returns dict of the form {provider: (start, end)}.
|
||||
"""
|
||||
_mappings = parse_mappings(mappings)
|
||||
|
|
|
@ -9,5 +9,9 @@ respawn
|
|||
exec start-stop-daemon --start --chuid {{ user_name }} \
|
||||
--chdir {{ start_dir }} --name {{ process_name }} \
|
||||
--exec {{ executable_name }} -- \
|
||||
{% for config_file in config_files -%}
|
||||
--config-file={{ config_file }} \
|
||||
{% endfor -%}
|
||||
{% if log_file -%}
|
||||
--log-file={{ log_file }}
|
||||
{% endif -%}
|
||||
|
|
|
@ -510,8 +510,10 @@ def git_clone_and_install(projects_yaml, core_project):
|
|||
repository: 'git://git.openstack.org/openstack/requirements.git',
|
||||
branch: 'stable/icehouse'}
|
||||
directory: /mnt/openstack-git
|
||||
http_proxy: http://squid.internal:3128
|
||||
https_proxy: https://squid.internal:3128
|
||||
|
||||
The directory key is optional.
|
||||
The directory, http_proxy, and https_proxy keys are optional.
|
||||
"""
|
||||
global requirements_dir
|
||||
parent_dir = '/mnt/openstack-git'
|
||||
|
@ -522,6 +524,13 @@ def git_clone_and_install(projects_yaml, core_project):
|
|||
projects = yaml.load(projects_yaml)
|
||||
_git_validate_projects_yaml(projects, core_project)
|
||||
|
||||
old_environ = dict(os.environ)
|
||||
|
||||
if 'http_proxy' in projects.keys():
|
||||
os.environ['http_proxy'] = projects['http_proxy']
|
||||
if 'https_proxy' in projects.keys():
|
||||
os.environ['https_proxy'] = projects['https_proxy']
|
||||
|
||||
if 'directory' in projects.keys():
|
||||
parent_dir = projects['directory']
|
||||
|
||||
|
@ -536,6 +545,8 @@ def git_clone_and_install(projects_yaml, core_project):
|
|||
repo_dir = _git_clone_and_install_single(repo, branch, parent_dir,
|
||||
update_requirements=True)
|
||||
|
||||
os.environ = old_environ
|
||||
|
||||
|
||||
def _git_validate_projects_yaml(projects, core_project):
|
||||
"""
|
||||
|
|
|
@ -20,11 +20,15 @@
|
|||
# Authors:
|
||||
# Charm Helpers Developers <juju@lists.ubuntu.com>
|
||||
|
||||
from __future__ import print_function
|
||||
from functools import wraps
|
||||
import os
|
||||
import json
|
||||
import yaml
|
||||
import subprocess
|
||||
import sys
|
||||
import errno
|
||||
import tempfile
|
||||
from subprocess import CalledProcessError
|
||||
|
||||
import six
|
||||
|
@ -56,15 +60,17 @@ def cached(func):
|
|||
|
||||
will cache the result of unit_get + 'test' for future calls.
|
||||
"""
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
global cache
|
||||
key = str((func, args, kwargs))
|
||||
try:
|
||||
return cache[key]
|
||||
except KeyError:
|
||||
res = func(*args, **kwargs)
|
||||
cache[key] = res
|
||||
return res
|
||||
pass # Drop out of the exception handler scope.
|
||||
res = func(*args, **kwargs)
|
||||
cache[key] = res
|
||||
return res
|
||||
return wrapper
|
||||
|
||||
|
||||
|
@ -87,7 +93,18 @@ def log(message, level=None):
|
|||
if not isinstance(message, six.string_types):
|
||||
message = repr(message)
|
||||
command += [message]
|
||||
subprocess.call(command)
|
||||
# Missing juju-log should not cause failures in unit tests
|
||||
# Send log output to stderr
|
||||
try:
|
||||
subprocess.call(command)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
if level:
|
||||
message = "{}: {}".format(level, message)
|
||||
message = "juju-log: {}".format(message)
|
||||
print(message, file=sys.stderr)
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
class Serializable(UserDict):
|
||||
|
@ -165,7 +182,7 @@ def local_unit():
|
|||
|
||||
def remote_unit():
|
||||
"""The remote unit for the current relation hook"""
|
||||
return os.environ['JUJU_REMOTE_UNIT']
|
||||
return os.environ.get('JUJU_REMOTE_UNIT', None)
|
||||
|
||||
|
||||
def service_name():
|
||||
|
@ -237,6 +254,12 @@ class Config(dict):
|
|||
except KeyError:
|
||||
return (self._prev_dict or {})[key]
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
def keys(self):
|
||||
prev_keys = []
|
||||
if self._prev_dict is not None:
|
||||
|
@ -340,14 +363,29 @@ def relation_set(relation_id=None, relation_settings=None, **kwargs):
|
|||
"""Set relation information for the current unit"""
|
||||
relation_settings = relation_settings if relation_settings else {}
|
||||
relation_cmd_line = ['relation-set']
|
||||
accepts_file = "--file" in subprocess.check_output(
|
||||
relation_cmd_line + ["--help"])
|
||||
if relation_id is not None:
|
||||
relation_cmd_line.extend(('-r', relation_id))
|
||||
for k, v in (list(relation_settings.items()) + list(kwargs.items())):
|
||||
if v is None:
|
||||
relation_cmd_line.append('{}='.format(k))
|
||||
else:
|
||||
relation_cmd_line.append('{}={}'.format(k, v))
|
||||
subprocess.check_call(relation_cmd_line)
|
||||
settings = relation_settings.copy()
|
||||
settings.update(kwargs)
|
||||
if accepts_file:
|
||||
# --file was introduced in Juju 1.23.2. Use it by default if
|
||||
# available, since otherwise we'll break if the relation data is
|
||||
# too big. Ideally we should tell relation-set to read the data from
|
||||
# stdin, but that feature is broken in 1.23.2: Bug #1454678.
|
||||
with tempfile.NamedTemporaryFile(delete=False) as settings_file:
|
||||
settings_file.write(yaml.safe_dump(settings).encode("utf-8"))
|
||||
subprocess.check_call(
|
||||
relation_cmd_line + ["--file", settings_file.name])
|
||||
os.remove(settings_file.name)
|
||||
else:
|
||||
for key, value in settings.items():
|
||||
if value is None:
|
||||
relation_cmd_line.append('{}='.format(key))
|
||||
else:
|
||||
relation_cmd_line.append('{}={}'.format(key, value))
|
||||
subprocess.check_call(relation_cmd_line)
|
||||
# Flush cache of any relation-gets for local unit
|
||||
flush(local_unit())
|
||||
|
||||
|
@ -496,6 +534,11 @@ def unit_get(attribute):
|
|||
return None
|
||||
|
||||
|
||||
def unit_public_ip():
|
||||
"""Get this unit's public IP address"""
|
||||
return unit_get('public-address')
|
||||
|
||||
|
||||
def unit_private_ip():
|
||||
"""Get this unit's private IP address"""
|
||||
return unit_get('private-address')
|
||||
|
@ -592,3 +635,49 @@ def action_fail(message):
|
|||
|
||||
The results set by action_set are preserved."""
|
||||
subprocess.check_call(['action-fail', message])
|
||||
|
||||
|
||||
def status_set(workload_state, message):
|
||||
"""Set the workload state with a message
|
||||
|
||||
Use status-set to set the workload state with a message which is visible
|
||||
to the user via juju status. If the status-set command is not found then
|
||||
assume this is juju < 1.23 and juju-log the message unstead.
|
||||
|
||||
workload_state -- valid juju workload state.
|
||||
message -- status update message
|
||||
"""
|
||||
valid_states = ['maintenance', 'blocked', 'waiting', 'active']
|
||||
if workload_state not in valid_states:
|
||||
raise ValueError(
|
||||
'{!r} is not a valid workload state'.format(workload_state)
|
||||
)
|
||||
cmd = ['status-set', workload_state, message]
|
||||
try:
|
||||
ret = subprocess.call(cmd)
|
||||
if ret == 0:
|
||||
return
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
log_message = 'status-set failed: {} {}'.format(workload_state,
|
||||
message)
|
||||
log(log_message, level='INFO')
|
||||
|
||||
|
||||
def status_get():
|
||||
"""Retrieve the previously set juju workload state
|
||||
|
||||
If the status-set command is not found then assume this is juju < 1.23 and
|
||||
return 'unknown'
|
||||
"""
|
||||
cmd = ['status-get']
|
||||
try:
|
||||
raw_status = subprocess.check_output(cmd, universal_newlines=True)
|
||||
status = raw_status.rstrip()
|
||||
return status
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
return 'unknown'
|
||||
else:
|
||||
raise
|
||||
|
|
|
@ -90,7 +90,7 @@ def service_available(service_name):
|
|||
['service', service_name, 'status'],
|
||||
stderr=subprocess.STDOUT).decode('UTF-8')
|
||||
except subprocess.CalledProcessError as e:
|
||||
return 'unrecognized service' not in e.output
|
||||
return b'unrecognized service' not in e.output
|
||||
else:
|
||||
return True
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import os
|
||||
import re
|
||||
import json
|
||||
from collections import Iterable
|
||||
from collections import Iterable, OrderedDict
|
||||
|
||||
from charmhelpers.core import host
|
||||
from charmhelpers.core import hookenv
|
||||
|
@ -119,7 +119,7 @@ class ServiceManager(object):
|
|||
"""
|
||||
self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json')
|
||||
self._ready = None
|
||||
self.services = {}
|
||||
self.services = OrderedDict()
|
||||
for service in services or []:
|
||||
service_name = service['service']
|
||||
self.services[service_name] = service
|
||||
|
|
|
@ -33,9 +33,9 @@ def bool_from_string(value):
|
|||
|
||||
value = value.strip().lower()
|
||||
|
||||
if value in ['y', 'yes', 'true', 't']:
|
||||
if value in ['y', 'yes', 'true', 't', 'on']:
|
||||
return True
|
||||
elif value in ['n', 'no', 'false', 'f']:
|
||||
elif value in ['n', 'no', 'false', 'f', 'off']:
|
||||
return False
|
||||
|
||||
msg = "Unable to interpret string value '%s' as boolean" % (value)
|
||||
|
|
|
@ -158,7 +158,7 @@ def filter_installed_packages(packages):
|
|||
|
||||
def apt_cache(in_memory=True):
|
||||
"""Build and return an apt cache"""
|
||||
import apt_pkg
|
||||
from apt import apt_pkg
|
||||
apt_pkg.init()
|
||||
if in_memory:
|
||||
apt_pkg.config.set("Dir::Cache::pkgcache", "")
|
||||
|
|
|
@ -14,6 +14,11 @@ from charmhelpers.contrib.openstack.utils import (
|
|||
os_release,
|
||||
)
|
||||
|
||||
VLAN = 'vlan'
|
||||
VXLAN = 'vxlan'
|
||||
GRE = 'gre'
|
||||
OVERLAY_NET_TYPES = [VXLAN, GRE]
|
||||
|
||||
|
||||
def get_l2population():
|
||||
plugin = config('neutron-plugin')
|
||||
|
@ -21,10 +26,12 @@ def get_l2population():
|
|||
|
||||
|
||||
def get_overlay_network_type():
|
||||
overlay_net = config('overlay-network-type')
|
||||
if overlay_net not in ['vxlan', 'gre']:
|
||||
raise Exception('Unsupported overlay-network-type')
|
||||
return overlay_net
|
||||
overlay_networks = config('overlay-network-type').split()
|
||||
for overlay_net in overlay_networks:
|
||||
if overlay_net not in OVERLAY_NET_TYPES:
|
||||
raise ValueError('Unsupported overlay-network-type %s'
|
||||
% overlay_net)
|
||||
return ','.join(overlay_networks)
|
||||
|
||||
|
||||
def get_l3ha():
|
||||
|
@ -32,10 +39,6 @@ def get_l3ha():
|
|||
if os_release('neutron-server') < 'juno':
|
||||
log('Disabling L3 HA, enable-l3ha is not valid before Juno')
|
||||
return False
|
||||
if config('overlay-network-type') not in ['vlan', 'gre', 'vxlan']:
|
||||
log('Disabling L3 HA, enable-l3ha requires the use of the vxlan, '
|
||||
'vlan or gre overlay network')
|
||||
return False
|
||||
if get_l2population():
|
||||
log('Disabling L3 HA, l2-population must be disabled with L3 HA')
|
||||
return False
|
||||
|
@ -49,10 +52,11 @@ def get_dvr():
|
|||
if os_release('neutron-server') < 'juno':
|
||||
log('Disabling DVR, enable-dvr is not valid before Juno')
|
||||
return False
|
||||
if config('overlay-network-type') != 'vxlan':
|
||||
log('Disabling DVR, enable-dvr requires the use of the vxlan '
|
||||
'overlay network')
|
||||
return False
|
||||
if os_release('neutron-server') == 'juno':
|
||||
if VXLAN not in config('overlay-network-type').split():
|
||||
log('Disabling DVR, enable-dvr requires the use of the vxlan '
|
||||
'overlay network for OpenStack Juno')
|
||||
return False
|
||||
if get_l3ha():
|
||||
log('Disabling DVR, enable-l3ha must be disabled with dvr')
|
||||
return False
|
||||
|
@ -126,6 +130,25 @@ class NeutronCCContext(context.NeutronContext):
|
|||
def _save_flag_file(self):
|
||||
pass
|
||||
|
||||
def get_neutron_api_rel_settings(self):
|
||||
settings = {}
|
||||
for rid in relation_ids('neutron-api'):
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
cell_type = rdata.get('cell_type')
|
||||
settings['nova_url'] = rdata.get('nova_url')
|
||||
settings['restart_trigger'] = rdata.get('restart_trigger')
|
||||
# If there are multiple nova-cloud-controllers joined to this
|
||||
# service in a cell deployment then ignore the non-api cell
|
||||
# ones
|
||||
if cell_type and not cell_type == "api":
|
||||
continue
|
||||
|
||||
if settings['nova_url']:
|
||||
return settings
|
||||
|
||||
return settings
|
||||
|
||||
def __call__(self):
|
||||
from neutron_api_utils import api_port
|
||||
ctxt = super(NeutronCCContext, self).__call__()
|
||||
|
@ -182,19 +205,18 @@ class NeutronCCContext(context.NeutronContext):
|
|||
ctxt['quota_router'] = config('quota-router')
|
||||
ctxt['quota_floatingip'] = config('quota-floatingip')
|
||||
|
||||
for rid in relation_ids('neutron-api'):
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
cell_type = rdata.get('cell_type')
|
||||
ctxt['nova_url'] = rdata.get('nova_url')
|
||||
ctxt['restart_trigger'] = rdata.get('restart_trigger')
|
||||
# If there are multiple nova-cloud-controllers joined to this
|
||||
# service in a cell deployment then ignore the non-api cell
|
||||
# ones
|
||||
if cell_type and not cell_type == "api":
|
||||
continue
|
||||
if ctxt['nova_url']:
|
||||
return ctxt
|
||||
n_api_settings = self.get_neutron_api_rel_settings()
|
||||
if n_api_settings:
|
||||
ctxt.update(n_api_settings)
|
||||
|
||||
flat_providers = config('flat-network-providers')
|
||||
if flat_providers:
|
||||
ctxt['network_providers'] = ','.join(flat_providers.split())
|
||||
|
||||
vlan_ranges = config('vlan-ranges')
|
||||
if vlan_ranges:
|
||||
ctxt['vlan_ranges'] = ','.join(vlan_ranges.split())
|
||||
|
||||
return ctxt
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ from charmhelpers.core.hookenv import (
|
|||
UnregisteredHookError,
|
||||
config,
|
||||
is_relation_made,
|
||||
local_unit,
|
||||
log,
|
||||
ERROR,
|
||||
relation_get,
|
||||
|
@ -25,6 +26,7 @@ from charmhelpers.core.hookenv import (
|
|||
from charmhelpers.core.host import (
|
||||
restart_on_change,
|
||||
service_reload,
|
||||
service_restart,
|
||||
)
|
||||
|
||||
from charmhelpers.fetch import (
|
||||
|
@ -35,20 +37,27 @@ from charmhelpers.fetch import (
|
|||
)
|
||||
|
||||
from charmhelpers.contrib.openstack.utils import (
|
||||
config_value_changed,
|
||||
configure_installation_source,
|
||||
git_install_requested,
|
||||
openstack_upgrade_available,
|
||||
os_requires_version,
|
||||
os_release,
|
||||
sync_db_with_multi_ipv6_addresses
|
||||
)
|
||||
|
||||
from neutron_api_utils import (
|
||||
CLUSTER_RES,
|
||||
NEUTRON_CONF,
|
||||
api_port,
|
||||
determine_packages,
|
||||
determine_ports,
|
||||
do_openstack_upgrade,
|
||||
git_install,
|
||||
dvr_router_present,
|
||||
l3ha_router_present,
|
||||
migrate_neutron_database,
|
||||
neutron_ready,
|
||||
register_configs,
|
||||
restart_map,
|
||||
services,
|
||||
|
@ -65,6 +74,7 @@ from neutron_api_context import (
|
|||
|
||||
from charmhelpers.contrib.hahelpers.cluster import (
|
||||
get_hacluster_config,
|
||||
is_elected_leader,
|
||||
)
|
||||
|
||||
from charmhelpers.payload.execd import execd_preinstall
|
||||
|
@ -95,6 +105,25 @@ hooks = Hooks()
|
|||
CONFIGS = register_configs()
|
||||
|
||||
|
||||
def conditional_neutron_migration():
|
||||
if os_release('neutron-server') < 'kilo':
|
||||
log('Not running neutron database migration as migrations are handled '
|
||||
'by the neutron-server process or nova-cloud-controller charm.')
|
||||
return
|
||||
|
||||
if is_elected_leader(CLUSTER_RES):
|
||||
allowed_units = relation_get('allowed_units')
|
||||
if allowed_units and local_unit() in allowed_units.split():
|
||||
migrate_neutron_database()
|
||||
service_restart('neutron-server')
|
||||
else:
|
||||
log('Not running neutron database migration, either no'
|
||||
' allowed_units or this unit is not present')
|
||||
return
|
||||
else:
|
||||
log('Not running neutron database migration, not leader')
|
||||
|
||||
|
||||
def configure_https():
|
||||
'''
|
||||
Enables SSL API Apache config if appropriate and kicks identity-service
|
||||
|
@ -122,7 +151,7 @@ def configure_https():
|
|||
def install():
|
||||
execd_preinstall()
|
||||
configure_installation_source(config('openstack-origin'))
|
||||
packages = determine_packages()
|
||||
packages = determine_packages(config('openstack-origin'))
|
||||
|
||||
if config('neutron-plugin') == 'vsp':
|
||||
source = config('neutron-plugin-repository-url')
|
||||
|
@ -158,6 +187,8 @@ def install():
|
|||
log('install failed with error: {}'.format(e.message))
|
||||
raise Exception(e)
|
||||
|
||||
git_install(config('openstack-origin-git'))
|
||||
|
||||
[open_port(port) for port in determine_ports()]
|
||||
|
||||
|
||||
|
@ -180,27 +211,35 @@ def vsd_changed(relation_id=None, remote_unit=None):
|
|||
@hooks.hook('config-changed')
|
||||
@restart_on_change(restart_map(), stopstart=True)
|
||||
def config_changed():
|
||||
if l3ha_router_present() and not get_l3ha():
|
||||
e = ('Cannot disable Router HA while ha enabled routers exist. Please'
|
||||
' remove any ha routers')
|
||||
log(e, level=ERROR)
|
||||
raise Exception(e)
|
||||
if dvr_router_present() and not get_dvr():
|
||||
e = ('Cannot disable dvr while dvr enabled routers exist. Please'
|
||||
' remove any distributed routers')
|
||||
log(e, level=ERROR)
|
||||
raise Exception(e)
|
||||
apt_install(filter_installed_packages(
|
||||
determine_packages(config('openstack-origin'))),
|
||||
fatal=True)
|
||||
# If neutron is ready to be queried then check for incompatability between
|
||||
# existing neutron objects and charm settings
|
||||
if neutron_ready():
|
||||
if l3ha_router_present() and not get_l3ha():
|
||||
e = ('Cannot disable Router HA while ha enabled routers exist.'
|
||||
' Please remove any ha routers')
|
||||
log(e, level=ERROR)
|
||||
raise Exception(e)
|
||||
if dvr_router_present() and not get_dvr():
|
||||
e = ('Cannot disable dvr while dvr enabled routers exist. Please'
|
||||
' remove any distributed routers')
|
||||
log(e, level=ERROR)
|
||||
raise Exception(e)
|
||||
if config('prefer-ipv6'):
|
||||
setup_ipv6()
|
||||
sync_db_with_multi_ipv6_addresses(config('database'),
|
||||
config('database-user'))
|
||||
|
||||
global CONFIGS
|
||||
if openstack_upgrade_available('neutron-server'):
|
||||
do_openstack_upgrade(CONFIGS)
|
||||
if git_install_requested():
|
||||
if config_value_changed('openstack-origin-git'):
|
||||
git_install(config('openstack-origin-git'))
|
||||
else:
|
||||
if openstack_upgrade_available('neutron-server'):
|
||||
do_openstack_upgrade(CONFIGS)
|
||||
|
||||
apt_install(filter_installed_packages(
|
||||
determine_packages(config('openstack-origin'))),
|
||||
fatal=True)
|
||||
configure_https()
|
||||
update_nrpe_config()
|
||||
CONFIGS.write_all()
|
||||
|
@ -271,12 +310,14 @@ def db_changed():
|
|||
log('shared-db relation incomplete. Peer not ready?')
|
||||
return
|
||||
CONFIGS.write_all()
|
||||
conditional_neutron_migration()
|
||||
|
||||
|
||||
@hooks.hook('pgsql-db-relation-changed')
|
||||
@restart_on_change(restart_map())
|
||||
def postgresql_neutron_db_changed():
|
||||
CONFIGS.write(NEUTRON_CONF)
|
||||
conditional_neutron_migration()
|
||||
|
||||
|
||||
@hooks.hook('amqp-relation-broken',
|
||||
|
|
|
@ -2,6 +2,8 @@ from collections import OrderedDict
|
|||
from copy import deepcopy
|
||||
from functools import partial
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from base64 import b64encode
|
||||
from charmhelpers.contrib.openstack import context, templating
|
||||
from charmhelpers.contrib.openstack.neutron import (
|
||||
|
@ -11,6 +13,9 @@ from charmhelpers.contrib.openstack.neutron import (
|
|||
from charmhelpers.contrib.openstack.utils import (
|
||||
os_release,
|
||||
get_os_codename_install_source,
|
||||
git_install_requested,
|
||||
git_clone_and_install,
|
||||
git_src_dir,
|
||||
configure_installation_source,
|
||||
)
|
||||
|
||||
|
@ -27,9 +32,17 @@ from charmhelpers.fetch import (
|
|||
)
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
lsb_release
|
||||
adduser,
|
||||
add_group,
|
||||
add_user_to_group,
|
||||
mkdir,
|
||||
lsb_release,
|
||||
service_restart,
|
||||
write_file,
|
||||
)
|
||||
|
||||
from charmhelpers.core.templating import render
|
||||
|
||||
import neutron_api_context
|
||||
|
||||
TEMPLATES = 'templates/'
|
||||
|
@ -53,6 +66,29 @@ KILO_PACKAGES = [
|
|||
'python-neutron-vpnaas',
|
||||
]
|
||||
|
||||
BASE_GIT_PACKAGES = [
|
||||
'libxml2-dev',
|
||||
'libxslt1-dev',
|
||||
'python-dev',
|
||||
'python-pip',
|
||||
'python-setuptools',
|
||||
'zlib1g-dev',
|
||||
]
|
||||
|
||||
# ubuntu packages that should not be installed when deploying from git
|
||||
GIT_PACKAGE_BLACKLIST = [
|
||||
'neutron-server',
|
||||
'neutron-plugin-ml2',
|
||||
'python-keystoneclient',
|
||||
'python-six',
|
||||
]
|
||||
|
||||
GIT_PACKAGE_BLACKLIST_KILO = [
|
||||
'python-neutron-lbaas',
|
||||
'python-neutron-fwaas',
|
||||
'python-neutron-vpnaas',
|
||||
]
|
||||
|
||||
BASE_SERVICES = [
|
||||
'neutron-server'
|
||||
]
|
||||
|
@ -115,17 +151,30 @@ def api_port(service):
|
|||
def determine_packages(source=None):
|
||||
# currently all packages match service names
|
||||
packages = [] + BASE_PACKAGES
|
||||
|
||||
for v in resource_map().values():
|
||||
packages.extend(v['services'])
|
||||
pkgs = neutron_plugin_attribute(config('neutron-plugin'),
|
||||
'server_packages',
|
||||
'neutron')
|
||||
packages.extend(pkgs)
|
||||
|
||||
if get_os_codename_install_source(source) >= 'kilo':
|
||||
packages.extend(KILO_PACKAGES)
|
||||
|
||||
if config('neutron-plugin') == 'vsp':
|
||||
packages.append('python-nuagenetlib')
|
||||
|
||||
if git_install_requested():
|
||||
packages.extend(BASE_GIT_PACKAGES)
|
||||
# don't include packages that will be installed from git
|
||||
packages = list(set(packages))
|
||||
for p in GIT_PACKAGE_BLACKLIST:
|
||||
packages.remove(p)
|
||||
if get_os_codename_install_source(source) >= 'kilo':
|
||||
for p in GIT_PACKAGE_BLACKLIST_KILO:
|
||||
packages.remove(p)
|
||||
|
||||
return list(set(packages))
|
||||
|
||||
|
||||
|
@ -213,6 +262,7 @@ def do_openstack_upgrade(configs):
|
|||
|
||||
:param configs: The charms main OSConfigRenderer object.
|
||||
"""
|
||||
cur_os_rel = os_release('neutron-server')
|
||||
new_src = config('openstack-origin')
|
||||
new_os_rel = get_os_codename_install_source(new_src)
|
||||
|
||||
|
@ -234,6 +284,38 @@ def do_openstack_upgrade(configs):
|
|||
|
||||
# set CONFIGS to load templates from new release
|
||||
configs.set_release(openstack_release=new_os_rel)
|
||||
# Before kilo it's nova-cloud-controllers job
|
||||
if new_os_rel >= 'kilo':
|
||||
stamp_neutron_database(cur_os_rel)
|
||||
migrate_neutron_database()
|
||||
|
||||
|
||||
def stamp_neutron_database(release):
|
||||
'''Stamp the database with the current release before upgrade.'''
|
||||
log('Stamping the neutron database with release %s.' % release)
|
||||
plugin = config('neutron-plugin')
|
||||
cmd = ['neutron-db-manage',
|
||||
'--config-file', NEUTRON_CONF,
|
||||
'--config-file', neutron_plugin_attribute(plugin,
|
||||
'config',
|
||||
'neutron'),
|
||||
'stamp',
|
||||
release]
|
||||
subprocess.check_output(cmd)
|
||||
|
||||
|
||||
def migrate_neutron_database():
|
||||
'''Initializes a new database or upgrades an existing database.'''
|
||||
log('Migrating the neutron database.')
|
||||
plugin = config('neutron-plugin')
|
||||
cmd = ['neutron-db-manage',
|
||||
'--config-file', NEUTRON_CONF,
|
||||
'--config-file', neutron_plugin_attribute(plugin,
|
||||
'config',
|
||||
'neutron'),
|
||||
'upgrade',
|
||||
'head']
|
||||
subprocess.check_output(cmd)
|
||||
|
||||
|
||||
def get_topics():
|
||||
|
@ -262,8 +344,8 @@ def setup_ipv6():
|
|||
apt_install('haproxy/trusty-backports', fatal=True)
|
||||
|
||||
|
||||
def router_feature_present(feature):
|
||||
''' Check For dvr enabled routers '''
|
||||
def get_neutron_client():
|
||||
''' Return a neutron client if possible '''
|
||||
env = neutron_api_context.IdentityServiceContext()()
|
||||
if not env:
|
||||
log('Unable to check resources at this time')
|
||||
|
@ -277,6 +359,12 @@ def router_feature_present(feature):
|
|||
tenant_name=env['admin_tenant_name'],
|
||||
auth_url=auth_url,
|
||||
region_name=env['region'])
|
||||
return neutron_client
|
||||
|
||||
|
||||
def router_feature_present(feature):
|
||||
''' Check For dvr enabled routers '''
|
||||
neutron_client = get_neutron_client()
|
||||
for router in neutron_client.list_routers()['routers']:
|
||||
if router.get(feature, False):
|
||||
return True
|
||||
|
@ -285,3 +373,83 @@ def router_feature_present(feature):
|
|||
l3ha_router_present = partial(router_feature_present, feature='ha')
|
||||
|
||||
dvr_router_present = partial(router_feature_present, feature='distributed')
|
||||
|
||||
|
||||
def neutron_ready():
|
||||
''' Check if neutron is ready by running arbitrary query'''
|
||||
neutron_client = get_neutron_client()
|
||||
if not neutron_client:
|
||||
log('No neutron client, neutron not ready')
|
||||
return False
|
||||
try:
|
||||
neutron_client.list_routers()
|
||||
log('neutron client ready')
|
||||
return True
|
||||
except:
|
||||
log('neutron query failed, neutron not ready ')
|
||||
return False
|
||||
|
||||
|
||||
def git_install(projects_yaml):
|
||||
"""Perform setup, and install git repos specified in yaml parameter."""
|
||||
if git_install_requested():
|
||||
git_pre_install()
|
||||
git_clone_and_install(projects_yaml, core_project='neutron')
|
||||
git_post_install(projects_yaml)
|
||||
|
||||
|
||||
def git_pre_install():
|
||||
"""Perform pre-install setup."""
|
||||
dirs = [
|
||||
'/var/lib/neutron',
|
||||
'/var/lib/neutron/lock',
|
||||
'/var/log/neutron',
|
||||
]
|
||||
|
||||
logs = [
|
||||
'/var/log/neutron/server.log',
|
||||
]
|
||||
|
||||
adduser('neutron', shell='/bin/bash', system_user=True)
|
||||
add_group('neutron', system_group=True)
|
||||
add_user_to_group('neutron', 'neutron')
|
||||
|
||||
for d in dirs:
|
||||
mkdir(d, owner='neutron', group='neutron', perms=0755, force=False)
|
||||
|
||||
for l in logs:
|
||||
write_file(l, '', owner='neutron', group='neutron', perms=0600)
|
||||
|
||||
|
||||
def git_post_install(projects_yaml):
|
||||
"""Perform post-install setup."""
|
||||
src_etc = os.path.join(git_src_dir(projects_yaml, 'neutron'), 'etc')
|
||||
configs = [
|
||||
{'src': src_etc,
|
||||
'dest': '/etc/neutron'},
|
||||
{'src': os.path.join(src_etc, 'neutron/plugins'),
|
||||
'dest': '/etc/neutron/plugins'},
|
||||
{'src': os.path.join(src_etc, 'neutron/rootwrap.d'),
|
||||
'dest': '/etc/neutron/rootwrap.d'},
|
||||
]
|
||||
|
||||
for c in configs:
|
||||
if os.path.exists(c['dest']):
|
||||
shutil.rmtree(c['dest'])
|
||||
shutil.copytree(c['src'], c['dest'])
|
||||
|
||||
render('git/neutron_sudoers', '/etc/sudoers.d/neutron_sudoers', {},
|
||||
perms=0o440)
|
||||
|
||||
neutron_api_context = {
|
||||
'service_description': 'Neutron API server',
|
||||
'charm_name': 'neutron-api',
|
||||
'process_name': 'neutron-server',
|
||||
}
|
||||
|
||||
# NOTE(coreycb): Needs systemd support
|
||||
render('git/upstart/neutron-server.upstart',
|
||||
'/etc/init/neutron-server.conf',
|
||||
neutron_api_context, perms=0o644)
|
||||
|
||||
service_restart('neutron-server')
|
||||
|
|
|
@ -12,7 +12,7 @@ description: |
|
|||
etc.)
|
||||
.
|
||||
This charm provides the OpenStack Neutron API service.
|
||||
categories:
|
||||
tags:
|
||||
- openstack
|
||||
provides:
|
||||
nrpe-external-master:
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Defaults:neutron !requiretty
|
||||
|
||||
neutron ALL = (root) NOPASSWD: /usr/local/bin/neutron-rootwrap /etc/neutron/rootwrap.conf *
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
description "{{ service_description }}"
|
||||
author "Juju {{ charm_name }} Charm <juju@localhost>"
|
||||
|
||||
start on runlevel [2345]
|
||||
stop on runlevel [!2345]
|
||||
|
||||
respawn
|
||||
|
||||
chdir /var/run
|
||||
|
||||
pre-start script
|
||||
mkdir -p /var/run/neutron
|
||||
chown neutron:root /var/run/neutron
|
||||
end script
|
||||
|
||||
script
|
||||
[ -r /etc/default/{{ process_name }} ] && . /etc/default/{{ process_name }}
|
||||
[ -r "$NEUTRON_PLUGIN_CONFIG" ] && CONF_ARG="--config-file $NEUTRON_PLUGIN_CONFIG"
|
||||
exec start-stop-daemon --start --chuid neutron --exec /usr/local/bin/neutron-server -- \
|
||||
--config-file /etc/neutron/neutron.conf \
|
||||
--log-file /var/log/neutron/server.log $CONF_ARG
|
||||
end script
|
|
@ -4,8 +4,8 @@
|
|||
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||
###############################################################################
|
||||
[ml2]
|
||||
type_drivers = gre,vxlan,vlan,flat
|
||||
tenant_network_types = gre,vxlan,vlan,flat
|
||||
type_drivers = {{ overlay_network_type }},vlan,flat
|
||||
tenant_network_types = {{ overlay_network_type }},vlan,flat
|
||||
mechanism_drivers = openvswitch,hyperv,l2population
|
||||
|
||||
[ml2_type_gre]
|
||||
|
@ -15,10 +15,10 @@ tunnel_id_ranges = 1:1000
|
|||
vni_ranges = 1001:2000
|
||||
|
||||
[ml2_type_vlan]
|
||||
network_vlan_ranges = physnet1:1000:2000
|
||||
network_vlan_ranges = {{ vlan_ranges }}
|
||||
|
||||
[ml2_type_flat]
|
||||
flat_networks = physnet1
|
||||
flat_networks = {{ network_providers }}
|
||||
|
||||
[ovs]
|
||||
enable_tunneling = True
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# kilo
|
||||
###############################################################################
|
||||
# [ WARNING ]
|
||||
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||
###############################################################################
|
||||
[ml2]
|
||||
type_drivers = {{ overlay_network_type }},vlan,flat
|
||||
tenant_network_types = {{ overlay_network_type }},vlan,flat
|
||||
mechanism_drivers = openvswitch,l2population
|
||||
|
||||
[ml2_type_gre]
|
||||
tunnel_id_ranges = 1:1000
|
||||
|
||||
[ml2_type_vxlan]
|
||||
vni_ranges = 1001:2000
|
||||
|
||||
[ml2_type_vlan]
|
||||
network_vlan_ranges = {{ vlan_ranges }}
|
||||
|
||||
[ml2_type_flat]
|
||||
flat_networks = {{ network_providers }}
|
||||
|
||||
[ovs]
|
||||
enable_tunneling = True
|
||||
local_ip = {{ local_ip }}
|
||||
|
||||
[agent]
|
||||
tunnel_types = {{ overlay_network_type }}
|
||||
|
||||
[securitygroup]
|
||||
{% if neutron_security_groups -%}
|
||||
enable_security_group = True
|
||||
firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
|
||||
{% else -%}
|
||||
enable_security_group = False
|
||||
{% endif -%}
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""Amulet tests on a basic neutron-api deployment on trusty-kilo."""
|
||||
|
||||
from basic_deployment import NeutronAPIBasicDeployment
|
||||
|
||||
if __name__ == '__main__':
|
||||
deployment = NeutronAPIBasicDeployment(series='trusty',
|
||||
openstack='cloud:trusty-kilo',
|
||||
source='cloud:trusty-updates/kilo')
|
||||
deployment.run_tests()
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""Amulet tests on a basic neutron-api deployment on utopic-juno."""
|
||||
|
||||
from basic_deployment import NeutronAPIBasicDeployment
|
||||
|
||||
if __name__ == '__main__':
|
||||
deployment = NeutronAPIBasicDeployment(series='utopic')
|
||||
deployment.run_tests()
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""Amulet tests on a basic neutron-api deployment on vivid-kilo."""
|
||||
|
||||
from basic_deployment import NeutronAPIBasicDeployment
|
||||
|
||||
if __name__ == '__main__':
|
||||
deployment = NeutronAPIBasicDeployment(series='vivid')
|
||||
deployment.run_tests()
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""Amulet tests on a basic neutron-api git deployment on trusty-icehouse."""
|
||||
|
||||
from basic_deployment import NeutronAPIBasicDeployment
|
||||
|
||||
if __name__ == '__main__':
|
||||
deployment = NeutronAPIBasicDeployment(series='trusty', git=True)
|
||||
deployment.run_tests()
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""Amulet tests on a basic neutron-api git deployment on trusty-juno."""
|
||||
|
||||
from basic_deployment import NeutronAPIBasicDeployment
|
||||
|
||||
if __name__ == '__main__':
|
||||
deployment = NeutronAPIBasicDeployment(series='trusty',
|
||||
openstack='cloud:trusty-juno',
|
||||
source='cloud:trusty-updates/juno',
|
||||
git=True)
|
||||
deployment.run_tests()
|
|
@ -1,6 +1,44 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
Basic neutron-api functional test.
|
||||
|
||||
test_* methods are called in sort order.
|
||||
|
||||
Convention to ensure desired test order:
|
||||
1xx service and endpoint checks
|
||||
2xx relation checks
|
||||
3xx config checks
|
||||
4xx functional checks
|
||||
9xx restarts and other final checks
|
||||
|
||||
Common relation definitions:
|
||||
- [ neutron-api, mysql ]
|
||||
- [ neutron-api, rabbitmq-server ]
|
||||
- [ neutron-api, nova-cloud-controller ]
|
||||
- [ neutron-api, neutron-openvswitch ]
|
||||
- [ neutron-api, keystone ]
|
||||
- [ neutron-api, neutron-gateway ]
|
||||
|
||||
Resultant relations of neutron-api service:
|
||||
relations:
|
||||
amqp:
|
||||
- rabbitmq-server
|
||||
cluster:
|
||||
- neutron-api
|
||||
identity-service:
|
||||
- keystone
|
||||
neutron-api:
|
||||
- nova-cloud-controller
|
||||
neutron-plugin-api: # not inspected due to
|
||||
- neutron-openvswitch # bug 1421388
|
||||
shared-db:
|
||||
- mysql
|
||||
"""
|
||||
|
||||
import amulet
|
||||
import os
|
||||
import time
|
||||
import yaml
|
||||
|
||||
from charmhelpers.contrib.openstack.amulet.deployment import (
|
||||
OpenStackAmuletDeployment
|
||||
|
@ -13,16 +51,18 @@ from charmhelpers.contrib.openstack.amulet.utils import (
|
|||
)
|
||||
|
||||
# Use DEBUG to turn on debug logging
|
||||
u = OpenStackAmuletUtils(ERROR)
|
||||
u = OpenStackAmuletUtils(DEBUG)
|
||||
|
||||
|
||||
class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
||||
"""Amulet tests on a basic neutron-api deployment."""
|
||||
|
||||
def __init__(self, series, openstack=None, source=None, stable=False):
|
||||
def __init__(self, series, openstack=None, source=None, git=False,
|
||||
stable=False):
|
||||
"""Deploy the entire test environment."""
|
||||
super(NeutronAPIBasicDeployment, self).__init__(series, openstack,
|
||||
source, stable)
|
||||
self.git = git
|
||||
self._add_services()
|
||||
self._add_relations()
|
||||
self._configure_services()
|
||||
|
@ -65,11 +105,30 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
|
||||
def _configure_services(self):
|
||||
"""Configure all of the services."""
|
||||
neutron_api_config = {}
|
||||
if self.git:
|
||||
branch = 'stable/' + self._get_openstack_release_string()
|
||||
amulet_http_proxy = os.environ.get('AMULET_HTTP_PROXY')
|
||||
openstack_origin_git = {
|
||||
'repositories': [
|
||||
{'name': 'requirements',
|
||||
'repository': 'git://github.com/openstack/requirements',
|
||||
'branch': branch},
|
||||
{'name': 'neutron',
|
||||
'repository': 'git://github.com/openstack/neutron',
|
||||
'branch': branch},
|
||||
],
|
||||
'directory': '/mnt/openstack-git',
|
||||
'http_proxy': amulet_http_proxy,
|
||||
'https_proxy': amulet_http_proxy,
|
||||
}
|
||||
neutron_api_config['openstack-origin-git'] = yaml.dump(openstack_origin_git)
|
||||
keystone_config = {'admin-password': 'openstack',
|
||||
'admin-token': 'ubuntutesting'}
|
||||
nova_cc_config = {'network-manager': 'Quantum',
|
||||
'quantum-security-groups': 'yes'}
|
||||
configs = {'keystone': keystone_config,
|
||||
configs = {'neutron-api': neutron_api_config,
|
||||
'keystone': keystone_config,
|
||||
'nova-cloud-controller': nova_cc_config}
|
||||
super(NeutronAPIBasicDeployment, self)._configure_services(configs)
|
||||
|
||||
|
@ -83,9 +142,55 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
self.quantum_gateway_sentry = self.d.sentry.unit['quantum-gateway/0']
|
||||
self.neutron_api_sentry = self.d.sentry.unit['neutron-api/0']
|
||||
self.nova_compute_sentry = self.d.sentry.unit['nova-compute/0']
|
||||
u.log.debug('openstack release val: {}'.format(
|
||||
self._get_openstack_release()))
|
||||
u.log.debug('openstack release str: {}'.format(
|
||||
self._get_openstack_release_string()))
|
||||
# Let things settle a bit before moving forward
|
||||
time.sleep(30)
|
||||
|
||||
def test_neutron_api_shared_db_relation(self):
|
||||
|
||||
def test_100_services(self):
|
||||
"""Verify the expected services are running on the corresponding
|
||||
service units."""
|
||||
u.log.debug('Checking status of system services...')
|
||||
# Fails vivid-kilo, bug 1454754
|
||||
neutron_api_services = ['status neutron-server']
|
||||
neutron_services = ['status neutron-dhcp-agent',
|
||||
'status neutron-lbaas-agent',
|
||||
'status neutron-metadata-agent',
|
||||
'status neutron-plugin-openvswitch-agent',
|
||||
'status neutron-ovs-cleanup']
|
||||
|
||||
if self._get_openstack_release() <= self.trusty_juno:
|
||||
neutron_services.append('status neutron-vpn-agent')
|
||||
|
||||
if self._get_openstack_release() < self.trusty_kilo:
|
||||
# Juno or earlier
|
||||
neutron_services.append('status neutron-metering-agent')
|
||||
|
||||
nova_cc_services = ['status nova-api-ec2',
|
||||
'status nova-api-os-compute',
|
||||
'status nova-objectstore',
|
||||
'status nova-cert',
|
||||
'status nova-scheduler',
|
||||
'status nova-conductor']
|
||||
|
||||
commands = {
|
||||
self.mysql_sentry: ['status mysql'],
|
||||
self.keystone_sentry: ['status keystone'],
|
||||
self.nova_cc_sentry: nova_cc_services,
|
||||
self.quantum_gateway_sentry: neutron_services,
|
||||
self.neutron_api_sentry: neutron_api_services,
|
||||
}
|
||||
|
||||
ret = u.validate_services(commands)
|
||||
if ret:
|
||||
amulet.raise_status(amulet.FAIL, msg=ret)
|
||||
|
||||
def test_200_neutron_api_shared_db_relation(self):
|
||||
"""Verify the neutron-api to mysql shared-db relation data"""
|
||||
u.log.debug('Checking neutron-api:mysql relation data...')
|
||||
unit = self.neutron_api_sentry
|
||||
relation = ['shared-db', 'mysql:shared-db']
|
||||
expected = {
|
||||
|
@ -100,23 +205,32 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('neutron-api shared-db', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_shared_db_neutron_api_relation(self):
|
||||
def test_201_shared_db_neutron_api_relation(self):
|
||||
"""Verify the mysql to neutron-api shared-db relation data"""
|
||||
u.log.debug('Checking mysql:neutron-api relation data...')
|
||||
unit = self.mysql_sentry
|
||||
relation = ['shared-db', 'neutron-api:shared-db']
|
||||
expected = {
|
||||
'allowed_units': 'nova-cloud-controller/0 neutron-api/0',
|
||||
'db_host': u.valid_ip,
|
||||
'private-address': u.valid_ip,
|
||||
}
|
||||
|
||||
if self._get_openstack_release() == self.precise_icehouse:
|
||||
# Precise
|
||||
expected['allowed_units'] = 'nova-cloud-controller/0 neutron-api/0'
|
||||
else:
|
||||
# Not Precise
|
||||
expected['allowed_units'] = 'neutron-api/0'
|
||||
|
||||
ret = u.validate_relation_data(unit, relation, expected)
|
||||
rel_data = unit.relation('shared-db', 'neutron-api:shared-db')
|
||||
if ret or 'password' not in rel_data:
|
||||
message = u.relation_error('mysql shared-db', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_neutron_api_amqp_relation(self):
|
||||
def test_202_neutron_api_amqp_relation(self):
|
||||
"""Verify the neutron-api to rabbitmq-server amqp relation data"""
|
||||
u.log.debug('Checking neutron-api:amqp relation data...')
|
||||
unit = self.neutron_api_sentry
|
||||
relation = ['amqp', 'rabbitmq-server:amqp']
|
||||
expected = {
|
||||
|
@ -130,8 +244,9 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('neutron-api amqp', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_amqp_neutron_api_relation(self):
|
||||
def test_203_amqp_neutron_api_relation(self):
|
||||
"""Verify the rabbitmq-server to neutron-api amqp relation data"""
|
||||
u.log.debug('Checking amqp:neutron-api relation data...')
|
||||
unit = self.rabbitmq_sentry
|
||||
relation = ['amqp', 'neutron-api:amqp']
|
||||
rel_data = unit.relation('amqp', 'neutron-api:amqp')
|
||||
|
@ -145,8 +260,9 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('rabbitmq amqp', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_neutron_api_identity_relation(self):
|
||||
def test_204_neutron_api_identity_relation(self):
|
||||
"""Verify the neutron-api to keystone identity-service relation data"""
|
||||
u.log.debug('Checking neutron-api:keystone relation data...')
|
||||
unit = self.neutron_api_sentry
|
||||
relation = ['identity-service', 'keystone:identity-service']
|
||||
api_ip = unit.relation('identity-service',
|
||||
|
@ -166,8 +282,9 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('neutron-api identity-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_keystone_neutron_api_identity_relation(self):
|
||||
def test_205_keystone_neutron_api_identity_relation(self):
|
||||
"""Verify the keystone to neutron-api identity-service relation data"""
|
||||
u.log.debug('Checking keystone:neutron-api relation data...')
|
||||
unit = self.keystone_sentry
|
||||
relation = ['identity-service', 'neutron-api:identity-service']
|
||||
id_relation = unit.relation('identity-service',
|
||||
|
@ -186,8 +303,9 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('neutron-api identity-service', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_neutron_api_plugin_relation(self):
|
||||
def test_206_neutron_api_plugin_relation(self):
|
||||
"""Verify neutron-api to neutron-openvswitch neutron-plugin-api"""
|
||||
u.log.debug('Checking neutron-api:neutron-ovs relation data...')
|
||||
unit = self.neutron_api_sentry
|
||||
relation = ['neutron-plugin-api',
|
||||
'neutron-openvswitch:neutron-plugin-api']
|
||||
|
@ -199,33 +317,9 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('neutron-api neutron-plugin-api', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
# XXX Test missing to examine the relation data neutron-openvswitch is
|
||||
# receiving. Current;y this data cannot be interegated due to
|
||||
# Bug#1421388
|
||||
|
||||
def test_z_restart_on_config_change(self):
|
||||
"""Verify that the specified services are restarted when the config
|
||||
is changed.
|
||||
|
||||
Note(coreycb): The method name with the _z_ is a little odd
|
||||
but it forces the test to run last. It just makes things
|
||||
easier because restarting services requires re-authorization.
|
||||
"""
|
||||
conf = '/etc/neutron/neutron.conf'
|
||||
services = ['neutron-server']
|
||||
self.d.configure('neutron-api', {'use-syslog': 'True'})
|
||||
stime = 60
|
||||
for s in services:
|
||||
if not u.service_restarted(self.neutron_api_sentry, s, conf,
|
||||
pgrep_full=True, sleep_time=stime):
|
||||
self.d.configure('neutron-api', {'use-syslog': 'False'})
|
||||
msg = "service {} didn't restart after config change".format(s)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
stime = 0
|
||||
self.d.configure('neutron-api', {'use-syslog': 'False'})
|
||||
|
||||
def test_neutron_api_novacc_relation(self):
|
||||
def test_207_neutron_api_novacc_relation(self):
|
||||
"""Verify the neutron-api to nova-cloud-controller relation data"""
|
||||
u.log.debug('Checking neutron-api:novacc relation data...')
|
||||
unit = self.neutron_api_sentry
|
||||
relation = ['neutron-api', 'nova-cloud-controller:neutron-api']
|
||||
api_ip = unit.relation('identity-service',
|
||||
|
@ -242,8 +336,9 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('neutron-api neutron-api', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_novacc_neutron_api_relation(self):
|
||||
def test_208_novacc_neutron_api_relation(self):
|
||||
"""Verify the nova-cloud-controller to neutron-api relation data"""
|
||||
u.log.debug('Checking novacc:neutron-api relation data...')
|
||||
unit = self.nova_cc_sentry
|
||||
relation = ['neutron-api', 'neutron-api:neutron-api']
|
||||
cc_ip = unit.relation('neutron-api',
|
||||
|
@ -258,8 +353,37 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
message = u.relation_error('nova-cc neutron-api', ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_neutron_config(self):
|
||||
# XXX Test missing to examine the relation data neutron-openvswitch is
|
||||
# receiving. Current;y this data cannot be interegated due to
|
||||
# Bug#1421388
|
||||
|
||||
def test_900_restart_on_config_change(self):
|
||||
"""Verify that the specified services are restarted when the config
|
||||
is changed.
|
||||
|
||||
Note(coreycb): The method name with the _z_ is a little odd
|
||||
but it forces the test to run last. It just makes things
|
||||
easier because restarting services requires re-authorization.
|
||||
"""
|
||||
u.log.debug('Checking novacc neutron-api relation data...')
|
||||
conf = '/etc/neutron/neutron.conf'
|
||||
services = ['neutron-server']
|
||||
u.log.debug('Making config change on neutron-api service...')
|
||||
self.d.configure('neutron-api', {'use-syslog': 'True'})
|
||||
stime = 60
|
||||
for s in services:
|
||||
u.log.debug("Checking that service restarted: {}".format(s))
|
||||
if not u.service_restarted(self.neutron_api_sentry, s, conf,
|
||||
pgrep_full=True, sleep_time=stime):
|
||||
self.d.configure('neutron-api', {'use-syslog': 'False'})
|
||||
msg = "service {} didn't restart after config change".format(s)
|
||||
amulet.raise_status(amulet.FAIL, msg=msg)
|
||||
stime = 0
|
||||
self.d.configure('neutron-api', {'use-syslog': 'False'})
|
||||
|
||||
def test_300_neutron_config(self):
|
||||
"""Verify the data in the neutron config file."""
|
||||
u.log.debug('Checking neutron.conf config file data...')
|
||||
unit = self.neutron_api_sentry
|
||||
cc_relation = self.nova_cc_sentry.relation('neutron-api',
|
||||
'neutron-api:neutron-api')
|
||||
|
@ -280,10 +404,6 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
'DEFAULT': {
|
||||
'verbose': 'False',
|
||||
'debug': 'False',
|
||||
'rabbit_userid': 'neutron',
|
||||
'rabbit_virtual_host': 'openstack',
|
||||
'rabbit_password': rabbitmq_relation['password'],
|
||||
'rabbit_host': rabbitmq_relation['hostname'],
|
||||
'bind_port': '9686',
|
||||
'nova_url': cc_relation['nova_url'],
|
||||
'nova_region_name': 'RegionOne',
|
||||
|
@ -294,12 +414,6 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
},
|
||||
'keystone_authtoken': {
|
||||
'signing_dir': '/var/cache/neutron',
|
||||
'service_protocol': ks_rel['service_protocol'],
|
||||
'service_host': ks_rel['service_host'],
|
||||
'service_port': ks_rel['service_port'],
|
||||
'auth_host': ks_rel['auth_host'],
|
||||
'auth_port': ks_rel['auth_port'],
|
||||
'auth_protocol': ks_rel['auth_protocol'],
|
||||
'admin_tenant_name': 'services',
|
||||
'admin_user': 'quantum',
|
||||
'admin_password': ks_rel['service_password'],
|
||||
|
@ -309,23 +423,57 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
},
|
||||
}
|
||||
|
||||
if self._get_openstack_release() >= self.trusty_kilo:
|
||||
# Kilo or later
|
||||
expected.update(
|
||||
{
|
||||
'oslo_messaging_rabbit': {
|
||||
'rabbit_userid': 'neutron',
|
||||
'rabbit_virtual_host': 'openstack',
|
||||
'rabbit_password': rabbitmq_relation['password'],
|
||||
'rabbit_host': rabbitmq_relation['hostname']
|
||||
}
|
||||
}
|
||||
)
|
||||
else:
|
||||
# Juno or earlier
|
||||
expected['DEFAULT'].update(
|
||||
{
|
||||
'rabbit_userid': 'neutron',
|
||||
'rabbit_virtual_host': 'openstack',
|
||||
'rabbit_password': rabbitmq_relation['password'],
|
||||
'rabbit_host': rabbitmq_relation['hostname']
|
||||
}
|
||||
)
|
||||
expected['keystone_authtoken'].update(
|
||||
{
|
||||
'service_protocol': ks_rel['service_protocol'],
|
||||
'service_host': ks_rel['service_host'],
|
||||
'service_port': ks_rel['service_port'],
|
||||
'auth_host': ks_rel['auth_host'],
|
||||
'auth_port': ks_rel['auth_port'],
|
||||
'auth_protocol': ks_rel['auth_protocol']
|
||||
}
|
||||
)
|
||||
|
||||
for section, pairs in expected.iteritems():
|
||||
ret = u.validate_config_data(unit, conf, section, pairs)
|
||||
if ret:
|
||||
message = "neutron config error: {}".format(ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_ml2_config(self):
|
||||
def test_301_ml2_config(self):
|
||||
"""Verify the data in the ml2 config file. This is only available
|
||||
since icehouse."""
|
||||
u.log.debug('Checking ml2 config file data...')
|
||||
unit = self.neutron_api_sentry
|
||||
conf = '/etc/neutron/plugins/ml2/ml2_conf.ini'
|
||||
neutron_api_relation = unit.relation('shared-db', 'mysql:shared-db')
|
||||
|
||||
expected = {
|
||||
'ml2': {
|
||||
'type_drivers': 'gre,vxlan,vlan,flat',
|
||||
'tenant_network_types': 'gre,vxlan,vlan,flat',
|
||||
'mechanism_drivers': 'openvswitch,hyperv,l2population',
|
||||
'type_drivers': 'gre,vlan,flat',
|
||||
'tenant_network_types': 'gre,vlan,flat',
|
||||
},
|
||||
'ml2_type_gre': {
|
||||
'tunnel_id_ranges': '1:1000'
|
||||
|
@ -345,37 +493,23 @@ class NeutronAPIBasicDeployment(OpenStackAmuletDeployment):
|
|||
}
|
||||
}
|
||||
|
||||
if self._get_openstack_release() >= self.trusty_kilo:
|
||||
# Kilo or later
|
||||
expected['ml2'].update(
|
||||
{
|
||||
'mechanism_drivers': 'openvswitch,l2population'
|
||||
}
|
||||
)
|
||||
else:
|
||||
# Juno or earlier
|
||||
expected['ml2'].update(
|
||||
{
|
||||
'mechanism_drivers': 'openvswitch,hyperv,l2population'
|
||||
}
|
||||
)
|
||||
|
||||
for section, pairs in expected.iteritems():
|
||||
ret = u.validate_config_data(unit, conf, section, pairs)
|
||||
if ret:
|
||||
message = "ml2 config error: {}".format(ret)
|
||||
amulet.raise_status(amulet.FAIL, msg=message)
|
||||
|
||||
def test_services(self):
|
||||
"""Verify the expected services are running on the corresponding
|
||||
service units."""
|
||||
neutron_services = ['status neutron-dhcp-agent',
|
||||
'status neutron-lbaas-agent',
|
||||
'status neutron-metadata-agent',
|
||||
'status neutron-plugin-openvswitch-agent',
|
||||
'status neutron-vpn-agent',
|
||||
'status neutron-metering-agent',
|
||||
'status neutron-ovs-cleanup']
|
||||
|
||||
nova_cc_services = ['status nova-api-ec2',
|
||||
'status nova-api-os-compute',
|
||||
'status nova-objectstore',
|
||||
'status nova-cert',
|
||||
'status nova-scheduler',
|
||||
'status nova-conductor']
|
||||
|
||||
commands = {
|
||||
self.mysql_sentry: ['status mysql'],
|
||||
self.keystone_sentry: ['status keystone'],
|
||||
self.nova_cc_sentry: nova_cc_services,
|
||||
self.quantum_gateway_sentry: neutron_services
|
||||
}
|
||||
|
||||
ret = u.validate_services(commands)
|
||||
if ret:
|
||||
amulet.raise_status(amulet.FAIL, msg=ret)
|
||||
|
|
|
@ -79,6 +79,9 @@ class AmuletUtils(object):
|
|||
for k, v in six.iteritems(commands):
|
||||
for cmd in v:
|
||||
output, code = k.run(cmd)
|
||||
self.log.debug('{} `{}` returned '
|
||||
'{}'.format(k.info['unit_name'],
|
||||
cmd, code))
|
||||
if code != 0:
|
||||
return "command `{}` returned {}".format(cmd, str(code))
|
||||
return None
|
||||
|
@ -86,7 +89,11 @@ class AmuletUtils(object):
|
|||
def _get_config(self, unit, filename):
|
||||
"""Get a ConfigParser object for parsing a unit's config file."""
|
||||
file_contents = unit.file_contents(filename)
|
||||
config = ConfigParser.ConfigParser()
|
||||
|
||||
# NOTE(beisner): by default, ConfigParser does not handle options
|
||||
# with no value, such as the flags used in the mysql my.cnf file.
|
||||
# https://bugs.python.org/issue7005
|
||||
config = ConfigParser.ConfigParser(allow_no_value=True)
|
||||
config.readfp(io.StringIO(file_contents))
|
||||
return config
|
||||
|
||||
|
@ -118,6 +125,9 @@ class AmuletUtils(object):
|
|||
longs, or can be a function that evaluate a variable and returns a
|
||||
bool.
|
||||
"""
|
||||
self.log.debug('actual: {}'.format(repr(actual)))
|
||||
self.log.debug('expected: {}'.format(repr(expected)))
|
||||
|
||||
for k, v in six.iteritems(expected):
|
||||
if k in actual:
|
||||
if (isinstance(v, six.string_types) or
|
||||
|
@ -134,7 +144,6 @@ class AmuletUtils(object):
|
|||
def validate_relation_data(self, sentry_unit, relation, expected):
|
||||
"""Validate actual relation data based on expected relation data."""
|
||||
actual = sentry_unit.relation(relation[0], relation[1])
|
||||
self.log.debug('actual: {}'.format(repr(actual)))
|
||||
return self._validate_dict_data(expected, actual)
|
||||
|
||||
def _validate_list_data(self, expected, actual):
|
||||
|
|
|
@ -44,17 +44,24 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||
Determine if the local branch being tested is derived from its
|
||||
stable or next (dev) branch, and based on this, use the corresonding
|
||||
stable or next branches for the other_services."""
|
||||
base_charms = ['mysql', 'mongodb', 'rabbitmq-server']
|
||||
base_charms = ['mysql', 'mongodb']
|
||||
|
||||
if self.series in ['precise', 'trusty']:
|
||||
base_series = self.series
|
||||
else:
|
||||
base_series = self.current_next
|
||||
|
||||
if self.stable:
|
||||
for svc in other_services:
|
||||
temp = 'lp:charms/{}'
|
||||
svc['location'] = temp.format(svc['name'])
|
||||
temp = 'lp:charms/{}/{}'
|
||||
svc['location'] = temp.format(base_series,
|
||||
svc['name'])
|
||||
else:
|
||||
for svc in other_services:
|
||||
if svc['name'] in base_charms:
|
||||
temp = 'lp:charms/{}'
|
||||
svc['location'] = temp.format(svc['name'])
|
||||
temp = 'lp:charms/{}/{}'
|
||||
svc['location'] = temp.format(base_series,
|
||||
svc['name'])
|
||||
else:
|
||||
temp = 'lp:~openstack-charmers/charms/{}/{}/next'
|
||||
svc['location'] = temp.format(self.current_next,
|
||||
|
@ -99,9 +106,12 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||
Return an integer representing the enum value of the openstack
|
||||
release.
|
||||
"""
|
||||
# Must be ordered by OpenStack release (not by Ubuntu release):
|
||||
(self.precise_essex, self.precise_folsom, self.precise_grizzly,
|
||||
self.precise_havana, self.precise_icehouse,
|
||||
self.trusty_icehouse, self.trusty_juno, self.trusty_kilo) = range(8)
|
||||
self.trusty_icehouse, self.trusty_juno, self.utopic_juno,
|
||||
self.trusty_kilo, self.vivid_kilo) = range(10)
|
||||
|
||||
releases = {
|
||||
('precise', None): self.precise_essex,
|
||||
('precise', 'cloud:precise-folsom'): self.precise_folsom,
|
||||
|
@ -110,7 +120,9 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||
('precise', 'cloud:precise-icehouse'): self.precise_icehouse,
|
||||
('trusty', None): self.trusty_icehouse,
|
||||
('trusty', 'cloud:trusty-juno'): self.trusty_juno,
|
||||
('trusty', 'cloud:trusty-kilo'): self.trusty_kilo}
|
||||
('trusty', 'cloud:trusty-kilo'): self.trusty_kilo,
|
||||
('utopic', None): self.utopic_juno,
|
||||
('vivid', None): self.vivid_kilo}
|
||||
return releases[(self.series, self.openstack)]
|
||||
|
||||
def _get_openstack_release_string(self):
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
import sys
|
||||
|
||||
sys.path.append('actions/')
|
||||
sys.path.append('hooks/')
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
from mock import patch, MagicMock
|
||||
|
||||
with patch('charmhelpers.core.hookenv.config') as config:
|
||||
config.return_value = 'neutron'
|
||||
import neutron_api_utils as utils # noqa
|
||||
|
||||
# Need to do some early patching to get the module loaded.
|
||||
_reg = utils.register_configs
|
||||
_map = utils.restart_map
|
||||
|
||||
utils.register_configs = MagicMock()
|
||||
utils.restart_map = MagicMock()
|
||||
|
||||
import git_reinstall
|
||||
|
||||
# Unpatch it now that its loaded.
|
||||
utils.register_configs = _reg
|
||||
utils.restart_map = _map
|
||||
|
||||
from test_utils import (
|
||||
CharmTestCase
|
||||
)
|
||||
|
||||
TO_PATCH = [
|
||||
'config',
|
||||
]
|
||||
|
||||
|
||||
openstack_origin_git = \
|
||||
"""repositories:
|
||||
- {name: requirements,
|
||||
repository: 'git://git.openstack.org/openstack/requirements',
|
||||
branch: stable/juno}
|
||||
- {name: neutron,
|
||||
repository: 'git://git.openstack.org/openstack/neutron',
|
||||
branch: stable/juno}"""
|
||||
|
||||
|
||||
class TestNeutronAPIActions(CharmTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNeutronAPIActions, self).setUp(git_reinstall, TO_PATCH)
|
||||
self.config.side_effect = self.test_config.get
|
||||
|
||||
@patch.object(git_reinstall, 'action_set')
|
||||
@patch.object(git_reinstall, 'action_fail')
|
||||
@patch.object(git_reinstall, 'git_install')
|
||||
@patch.object(git_reinstall, 'config_changed')
|
||||
def test_git_reinstall(self, config_changed, git_install, action_fail,
|
||||
action_set):
|
||||
self.test_config.set('openstack-origin-git', openstack_origin_git)
|
||||
|
||||
git_reinstall.git_reinstall()
|
||||
|
||||
git_install.assert_called_with(openstack_origin_git)
|
||||
self.assertTrue(git_install.called)
|
||||
self.assertTrue(config_changed.called)
|
||||
self.assertFalse(action_set.called)
|
||||
self.assertFalse(action_fail.called)
|
||||
|
||||
@patch.object(git_reinstall, 'action_set')
|
||||
@patch.object(git_reinstall, 'action_fail')
|
||||
@patch.object(git_reinstall, 'git_install')
|
||||
@patch.object(git_reinstall, 'config_changed')
|
||||
@patch('charmhelpers.contrib.openstack.utils.config')
|
||||
def test_git_reinstall_not_configured(self, _config, config_changed,
|
||||
git_install, action_fail,
|
||||
action_set):
|
||||
_config.return_value = None
|
||||
|
||||
git_reinstall.git_reinstall()
|
||||
|
||||
msg = 'openstack-origin-git is not configured'
|
||||
action_fail.assert_called_with(msg)
|
||||
self.assertFalse(git_install.called)
|
||||
self.assertFalse(action_set.called)
|
||||
|
||||
@patch.object(git_reinstall, 'action_set')
|
||||
@patch.object(git_reinstall, 'action_fail')
|
||||
@patch.object(git_reinstall, 'git_install')
|
||||
@patch.object(git_reinstall, 'config_changed')
|
||||
@patch('traceback.format_exc')
|
||||
@patch('charmhelpers.contrib.openstack.utils.config')
|
||||
def test_git_reinstall_exception(self, _config, format_exc,
|
||||
config_changed, git_install, action_fail,
|
||||
action_set):
|
||||
_config.return_value = openstack_origin_git
|
||||
e = OSError('something bad happened')
|
||||
git_install.side_effect = e
|
||||
traceback = (
|
||||
"Traceback (most recent call last):\n"
|
||||
" File \"actions/git_reinstall.py\", line 37, in git_reinstall\n"
|
||||
" git_install(config(\'openstack-origin-git\'))\n"
|
||||
" File \"/usr/lib/python2.7/dist-packages/mock.py\", line 964, in __call__\n" # noqa
|
||||
" return _mock_self._mock_call(*args, **kwargs)\n"
|
||||
" File \"/usr/lib/python2.7/dist-packages/mock.py\", line 1019, in _mock_call\n" # noqa
|
||||
" raise effect\n"
|
||||
"OSError: something bad happened\n")
|
||||
format_exc.return_value = traceback
|
||||
|
||||
git_reinstall.git_reinstall()
|
||||
|
||||
msg = 'git-reinstall resulted in an unexpected error'
|
||||
action_fail.assert_called_with(msg)
|
||||
action_set.assert_called_with({'traceback': traceback})
|
|
@ -34,12 +34,16 @@ class GeneralTests(CharmTestCase):
|
|||
self.test_config.set('overlay-network-type', 'gre')
|
||||
self.assertEquals(context.get_overlay_network_type(), 'gre')
|
||||
|
||||
def test_get_overlay_network_type_multi(self):
|
||||
self.test_config.set('overlay-network-type', 'gre vxlan')
|
||||
self.assertEquals(context.get_overlay_network_type(), 'gre,vxlan')
|
||||
|
||||
def test_get_overlay_network_type_unsupported(self):
|
||||
self.test_config.set('overlay-network-type', 'tokenring')
|
||||
with self.assertRaises(Exception) as _exceptctxt:
|
||||
with self.assertRaises(ValueError) as _exceptctxt:
|
||||
context.get_overlay_network_type()
|
||||
self.assertEqual(_exceptctxt.exception.message,
|
||||
'Unsupported overlay-network-type')
|
||||
'Unsupported overlay-network-type tokenring')
|
||||
|
||||
def test_get_l3ha(self):
|
||||
self.test_config.set('enable-l3ha', True)
|
||||
|
@ -65,14 +69,6 @@ class GeneralTests(CharmTestCase):
|
|||
self.os_release.return_value = 'juno'
|
||||
self.assertEquals(context.get_l3ha(), False)
|
||||
|
||||
def test_get_l3ha_badoverlay(self):
|
||||
self.test_config.set('enable-l3ha', True)
|
||||
self.test_config.set('overlay-network-type', 'tokenring')
|
||||
self.test_config.set('neutron-plugin', 'ovs')
|
||||
self.test_config.set('l2-population', False)
|
||||
self.os_release.return_value = 'juno'
|
||||
self.assertEquals(context.get_l3ha(), False)
|
||||
|
||||
def test_get_dvr(self):
|
||||
self.test_config.set('enable-dvr', True)
|
||||
self.test_config.set('enable-l3ha', False)
|
||||
|
@ -109,6 +105,24 @@ class GeneralTests(CharmTestCase):
|
|||
self.os_release.return_value = 'juno'
|
||||
self.assertEquals(context.get_dvr(), False)
|
||||
|
||||
def test_get_dvr_gre_kilo(self):
|
||||
self.test_config.set('enable-dvr', True)
|
||||
self.test_config.set('enable-l3ha', False)
|
||||
self.test_config.set('overlay-network-type', 'gre')
|
||||
self.test_config.set('neutron-plugin', 'ovs')
|
||||
self.test_config.set('l2-population', True)
|
||||
self.os_release.return_value = 'kilo'
|
||||
self.assertEquals(context.get_dvr(), True)
|
||||
|
||||
def test_get_dvr_vxlan_kilo(self):
|
||||
self.test_config.set('enable-dvr', True)
|
||||
self.test_config.set('enable-l3ha', False)
|
||||
self.test_config.set('overlay-network-type', 'vxlan')
|
||||
self.test_config.set('neutron-plugin', 'ovs')
|
||||
self.test_config.set('l2-population', True)
|
||||
self.os_release.return_value = 'kilo'
|
||||
self.assertEquals(context.get_dvr(), True)
|
||||
|
||||
def test_get_dvr_l3ha_on(self):
|
||||
self.test_config.set('enable-dvr', True)
|
||||
self.test_config.set('enable-l3ha', True)
|
||||
|
@ -291,6 +305,7 @@ class NeutronCCContextTest(CharmTestCase):
|
|||
'quota_security_group_rule': 100,
|
||||
'quota_subnet': 10,
|
||||
'quota_vip': 10,
|
||||
'vlan_ranges': 'physnet1:1000:2000',
|
||||
}
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
with patch.object(napi_ctxt, '_ensure_packages'):
|
||||
|
@ -301,6 +316,7 @@ class NeutronCCContextTest(CharmTestCase):
|
|||
@patch('__builtin__.__import__')
|
||||
def test_neutroncc_context_vxlan(self, _import, plugin, nm):
|
||||
plugin.return_value = None
|
||||
self.test_config.set('flat-network-providers', 'physnet2 physnet3')
|
||||
self.test_config.set('overlay-network-type', 'vxlan')
|
||||
ctxt_data = {
|
||||
'debug': True,
|
||||
|
@ -322,6 +338,8 @@ class NeutronCCContextTest(CharmTestCase):
|
|||
'quota_security_group_rule': 100,
|
||||
'quota_subnet': 10,
|
||||
'quota_vip': 10,
|
||||
'vlan_ranges': 'physnet1:1000:2000',
|
||||
'network_providers': 'physnet2,physnet3',
|
||||
}
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
with patch.object(napi_ctxt, '_ensure_packages'):
|
||||
|
@ -359,6 +377,7 @@ class NeutronCCContextTest(CharmTestCase):
|
|||
'quota_security_group_rule': 100,
|
||||
'quota_subnet': 10,
|
||||
'quota_vip': 10,
|
||||
'vlan_ranges': 'physnet1:1000:2000',
|
||||
}
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
with patch.object(napi_ctxt, '_ensure_packages'):
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from mock import MagicMock, patch, call
|
||||
import yaml
|
||||
from test_utils import CharmTestCase
|
||||
|
||||
|
||||
|
@ -34,6 +35,7 @@ TO_PATCH = [
|
|||
'determine_ports',
|
||||
'do_openstack_upgrade',
|
||||
'dvr_router_present',
|
||||
'local_unit',
|
||||
'l3ha_router_present',
|
||||
'execd_preinstall',
|
||||
'filter_installed_packages',
|
||||
|
@ -41,14 +43,20 @@ TO_PATCH = [
|
|||
'get_l3ha',
|
||||
'get_l2population',
|
||||
'get_overlay_network_type',
|
||||
'git_install',
|
||||
'is_elected_leader',
|
||||
'is_relation_made',
|
||||
'log',
|
||||
'migrate_neutron_database',
|
||||
'neutron_ready',
|
||||
'open_port',
|
||||
'openstack_upgrade_available',
|
||||
'os_release',
|
||||
'os_requires_version',
|
||||
'relation_get',
|
||||
'relation_ids',
|
||||
'relation_set',
|
||||
'service_restart',
|
||||
'unit_get',
|
||||
'get_iface_for_address',
|
||||
'get_netmask_for_address',
|
||||
|
@ -108,7 +116,9 @@ class NeutronAPIHooksTests(CharmTestCase):
|
|||
hooks.hooks.execute([
|
||||
'hooks/{}'.format(hookname)])
|
||||
|
||||
def test_install_hook(self):
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_install_hook(self, git_requested):
|
||||
git_requested.return_value = False
|
||||
_pkgs = ['foo', 'bar']
|
||||
_ports = [80, 81, 82]
|
||||
_port_calls = [call(port) for port in _ports]
|
||||
|
@ -125,7 +135,9 @@ class NeutronAPIHooksTests(CharmTestCase):
|
|||
self.open_port.assert_has_calls(_port_calls)
|
||||
self.assertTrue(self.execd_preinstall.called)
|
||||
|
||||
def test_nuage_install_hook(self):
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_nuage_install_hook(self, git_requested):
|
||||
git_requested.return_value = False
|
||||
self.test_config.set('neutron-plugin', 'vsp')
|
||||
self.test_config.set('neutron-plugin-repository-url',
|
||||
"deb http://10.14.4.1/nuage trusty main")
|
||||
|
@ -146,8 +158,44 @@ class NeutronAPIHooksTests(CharmTestCase):
|
|||
self.open_port.assert_has_calls(_port_calls)
|
||||
self.assertTrue(self.execd_preinstall.called)
|
||||
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_install_hook_git(self, git_requested):
|
||||
git_requested.return_value = True
|
||||
_pkgs = ['foo', 'bar']
|
||||
_ports = [80, 81, 82]
|
||||
_port_calls = [call(port) for port in _ports]
|
||||
self.determine_packages.return_value = _pkgs
|
||||
self.determine_ports.return_value = _ports
|
||||
repo = 'cloud:trusty-juno'
|
||||
openstack_origin_git = {
|
||||
'repositories': [
|
||||
{'name': 'requirements',
|
||||
'repository': 'git://git.openstack.org/openstack/requirements', # noqa
|
||||
'branch': 'stable/juno'},
|
||||
{'name': 'neutron',
|
||||
'repository': 'git://git.openstack.org/openstack/neutron',
|
||||
'branch': 'stable/juno'}
|
||||
],
|
||||
'directory': '/mnt/openstack-git',
|
||||
}
|
||||
projects_yaml = yaml.dump(openstack_origin_git)
|
||||
self.test_config.set('openstack-origin', repo)
|
||||
self.test_config.set('openstack-origin-git', projects_yaml)
|
||||
self._call_hook('install')
|
||||
self.assertTrue(self.execd_preinstall.called)
|
||||
self.configure_installation_source.assert_called_with(repo)
|
||||
self.apt_update.assert_called_with()
|
||||
self.apt_install.assert_has_calls([
|
||||
call(_pkgs, fatal=True),
|
||||
])
|
||||
self.git_install.assert_called_with(projects_yaml)
|
||||
self.open_port.assert_has_calls(_port_calls)
|
||||
|
||||
@patch.object(hooks, 'configure_https')
|
||||
def test_config_changed(self, conf_https):
|
||||
@patch.object(hooks, 'git_install_requested')
|
||||
def test_config_changed(self, git_requested, conf_https):
|
||||
git_requested.return_value = False
|
||||
self.neutron_ready.return_value = True
|
||||
self.openstack_upgrade_available.return_value = True
|
||||
self.dvr_router_present.return_value = False
|
||||
self.l3ha_router_present.return_value = False
|
||||
|
@ -170,6 +218,74 @@ class NeutronAPIHooksTests(CharmTestCase):
|
|||
self.assertTrue(self.do_openstack_upgrade.called)
|
||||
self.assertTrue(self.apt_install.called)
|
||||
|
||||
def test_config_changed_nodvr_disprouters(self):
|
||||
self.neutron_ready.return_value = True
|
||||
self.dvr_router_present.return_value = True
|
||||
self.get_dvr.return_value = False
|
||||
with self.assertRaises(Exception) as context:
|
||||
self._call_hook('config-changed')
|
||||
self.assertEqual(context.exception.message,
|
||||
'Cannot disable dvr while dvr enabled routers exist.'
|
||||
' Please remove any distributed routers')
|
||||
|
||||
def test_config_changed_nol3ha_harouters(self):
|
||||
self.neutron_ready.return_value = True
|
||||
self.dvr_router_present.return_value = False
|
||||
self.l3ha_router_present.return_value = True
|
||||
self.get_l3ha.return_value = False
|
||||
with self.assertRaises(Exception) as context:
|
||||
self._call_hook('config-changed')
|
||||
self.assertEqual(context.exception.message,
|
||||
'Cannot disable Router HA while ha enabled routers'
|
||||
' exist. Please remove any ha routers')
|
||||
|
||||
@patch.object(hooks, 'configure_https')
|
||||
@patch.object(hooks, 'git_install_requested')
|
||||
@patch.object(hooks, 'config_value_changed')
|
||||
def test_config_changed_git(self, config_val_changed, git_requested,
|
||||
configure_https):
|
||||
git_requested.return_value = True
|
||||
self.neutron_ready.return_value = True
|
||||
self.dvr_router_present.return_value = False
|
||||
self.l3ha_router_present.return_value = False
|
||||
self.relation_ids.side_effect = self._fake_relids
|
||||
_n_api_rel_joined = self.patch('neutron_api_relation_joined')
|
||||
_n_plugin_api_rel_joined =\
|
||||
self.patch('neutron_plugin_api_relation_joined')
|
||||
_amqp_rel_joined = self.patch('amqp_joined')
|
||||
_id_rel_joined = self.patch('identity_joined')
|
||||
_id_cluster_joined = self.patch('cluster_joined')
|
||||
_zmq_joined = self.patch('zeromq_configuration_relation_joined')
|
||||
repo = 'cloud:trusty-juno'
|
||||
openstack_origin_git = {
|
||||
'repositories': [
|
||||
{'name': 'requirements',
|
||||
'repository':
|
||||
'git://git.openstack.org/openstack/requirements',
|
||||
'branch': 'stable/juno'},
|
||||
{'name': 'neutron',
|
||||
'repository': 'git://git.openstack.org/openstack/neutron',
|
||||
'branch': 'stable/juno'}
|
||||
],
|
||||
'directory': '/mnt/openstack-git',
|
||||
}
|
||||
projects_yaml = yaml.dump(openstack_origin_git)
|
||||
self.test_config.set('openstack-origin', repo)
|
||||
self.test_config.set('openstack-origin-git', projects_yaml)
|
||||
self._call_hook('config-changed')
|
||||
self.git_install.assert_called_with(projects_yaml)
|
||||
self.assertFalse(self.do_openstack_upgrade.called)
|
||||
self.assertTrue(self.apt_install.called)
|
||||
self.assertTrue(configure_https.called)
|
||||
self.assertTrue(self.update_nrpe_config.called)
|
||||
self.assertTrue(self.CONFIGS.write_all.called)
|
||||
self.assertTrue(_n_api_rel_joined.called)
|
||||
self.assertTrue(_n_plugin_api_rel_joined.called)
|
||||
self.assertTrue(_amqp_rel_joined.called)
|
||||
self.assertTrue(_id_rel_joined.called)
|
||||
self.assertTrue(_zmq_joined.called)
|
||||
self.assertTrue(_id_cluster_joined.called)
|
||||
|
||||
def test_amqp_joined(self):
|
||||
self._call_hook('amqp-relation-joined')
|
||||
self.relation_set.assert_called_with(
|
||||
|
@ -223,19 +339,23 @@ class NeutronAPIHooksTests(CharmTestCase):
|
|||
'Attempting to associate a postgresql database when'
|
||||
' there is already associated a mysql one')
|
||||
|
||||
def test_shared_db_changed(self):
|
||||
@patch.object(hooks, 'conditional_neutron_migration')
|
||||
def test_shared_db_changed(self, cond_neutron_mig):
|
||||
self.CONFIGS.complete_contexts.return_value = ['shared-db']
|
||||
self._call_hook('shared-db-relation-changed')
|
||||
self.assertTrue(self.CONFIGS.write_all.called)
|
||||
cond_neutron_mig.assert_called_with()
|
||||
|
||||
def test_shared_db_changed_partial_ctxt(self):
|
||||
self.CONFIGS.complete_contexts.return_value = []
|
||||
self._call_hook('shared-db-relation-changed')
|
||||
self.assertFalse(self.CONFIGS.write_all.called)
|
||||
|
||||
def test_pgsql_db_changed(self):
|
||||
@patch.object(hooks, 'conditional_neutron_migration')
|
||||
def test_pgsql_db_changed(self, cond_neutron_mig):
|
||||
self._call_hook('pgsql-db-relation-changed')
|
||||
self.assertTrue(self.CONFIGS.write.called)
|
||||
cond_neutron_mig.assert_called_with()
|
||||
|
||||
def test_amqp_broken(self):
|
||||
self._call_hook('amqp-relation-broken')
|
||||
|
@ -609,3 +729,46 @@ class NeutronAPIHooksTests(CharmTestCase):
|
|||
call('service', 'apache2', 'reload')]
|
||||
self.check_call.assert_called_has_calls(calls)
|
||||
self.assertTrue(_id_rel_joined.called)
|
||||
|
||||
def test_conditional_neutron_migration_icehouse(self):
|
||||
self.os_release.return_value = 'icehouse'
|
||||
hooks.conditional_neutron_migration()
|
||||
self.log.assert_called_with(
|
||||
'Not running neutron database migration as migrations are handled '
|
||||
'by the neutron-server process or nova-cloud-controller charm.'
|
||||
)
|
||||
|
||||
def test_conditional_neutron_migration_ncc_rel_leader_juno(self):
|
||||
self.test_relation.set({
|
||||
'allowed_units': 'neutron-api/0 neutron-api/1 neutron-api/4',
|
||||
})
|
||||
self.local_unit.return_value = 'neutron-api/1'
|
||||
self.is_elected_leader.return_value = True
|
||||
self.os_release.return_value = 'juno'
|
||||
hooks.conditional_neutron_migration()
|
||||
self.log.assert_called_with(
|
||||
'Not running neutron database migration as migrations are handled'
|
||||
' by the neutron-server process or nova-cloud-controller charm.'
|
||||
)
|
||||
|
||||
def test_conditional_neutron_migration_ncc_rel_leader_kilo(self):
|
||||
self.test_relation.set({
|
||||
'allowed_units': 'neutron-api/0 neutron-api/1 neutron-api/4',
|
||||
})
|
||||
self.local_unit.return_value = 'neutron-api/1'
|
||||
self.is_elected_leader.return_value = True
|
||||
self.os_release.return_value = 'kilo'
|
||||
hooks.conditional_neutron_migration()
|
||||
self.migrate_neutron_database.assert_called_with()
|
||||
self.service_restart.assert_called_with('neutron-server')
|
||||
|
||||
def test_conditional_neutron_migration_ncc_rel_notleader(self):
|
||||
self.is_elected_leader.return_value = False
|
||||
self.os_release.return_value = 'juno'
|
||||
hooks.conditional_neutron_migration()
|
||||
self.assertFalse(self.migrate_neutron_database.called)
|
||||
self.assertFalse(self.service_restart.called)
|
||||
self.log.assert_called_with(
|
||||
'Not running neutron database migration as migrations are handled '
|
||||
'by the neutron-server process or nova-cloud-controller charm.'
|
||||
)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
|
||||
from mock import MagicMock, patch
|
||||
from mock import MagicMock, patch, call
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
import charmhelpers.contrib.openstack.templating as templating
|
||||
import charmhelpers.contrib.openstack.utils
|
||||
import neutron_api_context as ncontext
|
||||
|
||||
templating.OSConfigRenderer = MagicMock()
|
||||
|
||||
|
@ -29,8 +31,18 @@ TO_PATCH = [
|
|||
'log',
|
||||
'neutron_plugin_attribute',
|
||||
'os_release',
|
||||
'subprocess',
|
||||
]
|
||||
|
||||
openstack_origin_git = \
|
||||
"""repositories:
|
||||
- {name: requirements,
|
||||
repository: 'git://git.openstack.org/openstack/requirements',
|
||||
branch: stable/juno}
|
||||
- {name: neutron,
|
||||
repository: 'git://git.openstack.org/openstack/neutron',
|
||||
branch: stable/juno}"""
|
||||
|
||||
|
||||
def _mock_npa(plugin, attr, net_manager=None):
|
||||
plugins = {
|
||||
|
@ -58,13 +70,21 @@ def _mock_npa(plugin, attr, net_manager=None):
|
|||
return plugins[plugin][attr]
|
||||
|
||||
|
||||
class DummyIdentityServiceContext():
|
||||
|
||||
def __init__(self, return_value):
|
||||
self.return_value = return_value
|
||||
|
||||
def __call__(self):
|
||||
return self.return_value
|
||||
|
||||
|
||||
class TestNeutronAPIUtils(CharmTestCase):
|
||||
def setUp(self):
|
||||
super(TestNeutronAPIUtils, self).setUp(nutils, TO_PATCH)
|
||||
self.config.side_effect = self.test_config.get
|
||||
self.test_config.set('region', 'region101')
|
||||
self.neutron_plugin_attribute.side_effect = _mock_npa
|
||||
self.os_release.side_effect = 'trusty'
|
||||
|
||||
def tearDown(self):
|
||||
# Reset cached cache
|
||||
|
@ -74,13 +94,17 @@ class TestNeutronAPIUtils(CharmTestCase):
|
|||
port = nutils.api_port('neutron-server')
|
||||
self.assertEqual(port, nutils.API_PORTS['neutron-server'])
|
||||
|
||||
def test_determine_packages(self):
|
||||
@patch.object(nutils, 'git_install_requested')
|
||||
def test_determine_packages(self, git_requested):
|
||||
git_requested.return_value = False
|
||||
pkg_list = nutils.determine_packages()
|
||||
expect = deepcopy(nutils.BASE_PACKAGES)
|
||||
expect.extend(['neutron-server', 'neutron-plugin-ml2'])
|
||||
self.assertItemsEqual(pkg_list, expect)
|
||||
|
||||
def test_determine_vsp_packages(self):
|
||||
@patch.object(nutils, 'git_install_requested')
|
||||
def test_determine_vsp_packages(self, git_requested):
|
||||
git_requested.return_value = False
|
||||
self.test_config.set('neutron-plugin', 'vsp')
|
||||
self.get_os_codename_install_source.return_value = 'juno'
|
||||
pkg_list = nutils.determine_packages()
|
||||
|
@ -89,7 +113,9 @@ class TestNeutronAPIUtils(CharmTestCase):
|
|||
'python-nuagenetlib'])
|
||||
self.assertItemsEqual(pkg_list, expect)
|
||||
|
||||
def test_determine_packages_kilo(self):
|
||||
@patch.object(nutils, 'git_install_requested')
|
||||
def test_determine_packages_kilo(self, git_requested):
|
||||
git_requested.return_value = False
|
||||
self.get_os_codename_install_source.return_value = 'kilo'
|
||||
pkg_list = nutils.determine_packages()
|
||||
expect = deepcopy(nutils.BASE_PACKAGES)
|
||||
|
@ -178,13 +204,19 @@ class TestNeutronAPIUtils(CharmTestCase):
|
|||
nutils.keystone_ca_cert_b64()
|
||||
self.assertTrue(self.b64encode.called)
|
||||
|
||||
def test_do_openstack_upgrade(self):
|
||||
@patch.object(nutils, 'migrate_neutron_database')
|
||||
@patch.object(nutils, 'stamp_neutron_database')
|
||||
@patch.object(nutils, 'git_install_requested')
|
||||
def test_do_openstack_upgrade_juno(self, git_requested,
|
||||
stamp_neutron_db, migrate_neutron_db):
|
||||
git_requested.return_value = False
|
||||
self.config.side_effect = self.test_config.get
|
||||
self.test_config.set('openstack-origin', 'cloud:trusty-juno')
|
||||
self.os_release.side_effect = 'icehouse'
|
||||
self.os_release.return_value = 'icehouse'
|
||||
self.get_os_codename_install_source.return_value = 'juno'
|
||||
configs = MagicMock()
|
||||
nutils.do_openstack_upgrade(configs)
|
||||
self.os_release.assert_called_with('neutron-server')
|
||||
self.log.assert_called()
|
||||
self.configure_installation_source.assert_called_with(
|
||||
'cloud:trusty-juno'
|
||||
|
@ -203,3 +235,254 @@ class TestNeutronAPIUtils(CharmTestCase):
|
|||
options=dpkg_opts,
|
||||
fatal=True)
|
||||
configs.set_release.assert_called_with(openstack_release='juno')
|
||||
self.assertItemsEqual(stamp_neutron_db.call_args_list, [])
|
||||
self.assertItemsEqual(migrate_neutron_db.call_args_list, [])
|
||||
|
||||
@patch.object(charmhelpers.contrib.openstack.utils,
|
||||
'get_os_codename_install_source')
|
||||
@patch.object(nutils, 'migrate_neutron_database')
|
||||
@patch.object(nutils, 'stamp_neutron_database')
|
||||
@patch.object(nutils, 'git_install_requested')
|
||||
def test_do_openstack_upgrade_kilo(self, git_requested,
|
||||
stamp_neutron_db, migrate_neutron_db,
|
||||
gsrc):
|
||||
git_requested.return_value = False
|
||||
self.os_release.return_value = 'juno'
|
||||
self.config.side_effect = self.test_config.get
|
||||
self.test_config.set('openstack-origin', 'cloud:trusty-kilo')
|
||||
gsrc.return_value = 'kilo'
|
||||
self.get_os_codename_install_source.return_value = 'kilo'
|
||||
configs = MagicMock()
|
||||
nutils.do_openstack_upgrade(configs)
|
||||
self.os_release.assert_called_with('neutron-server')
|
||||
self.log.assert_called()
|
||||
self.configure_installation_source.assert_called_with(
|
||||
'cloud:trusty-kilo'
|
||||
)
|
||||
self.apt_update.assert_called_with(fatal=True)
|
||||
dpkg_opts = [
|
||||
'--option', 'Dpkg::Options::=--force-confnew',
|
||||
'--option', 'Dpkg::Options::=--force-confdef',
|
||||
]
|
||||
self.apt_upgrade.assert_called_with(options=dpkg_opts,
|
||||
fatal=True,
|
||||
dist=True)
|
||||
pkgs = nutils.determine_packages()
|
||||
pkgs.sort()
|
||||
self.apt_install.assert_called_with(packages=pkgs,
|
||||
options=dpkg_opts,
|
||||
fatal=True)
|
||||
configs.set_release.assert_called_with(openstack_release='kilo')
|
||||
stamp_neutron_db.assert_called_with('juno')
|
||||
migrate_neutron_db.assert_called_with()
|
||||
|
||||
@patch.object(ncontext, 'IdentityServiceContext')
|
||||
@patch('neutronclient.v2_0.client.Client')
|
||||
def test_get_neutron_client(self, nclient, IdentityServiceContext):
|
||||
creds = {
|
||||
'auth_protocol': 'http',
|
||||
'auth_host': 'myhost',
|
||||
'auth_port': '2222',
|
||||
'admin_user': 'bob',
|
||||
'admin_password': 'pa55w0rd',
|
||||
'admin_tenant_name': 'tenant1',
|
||||
'region': 'region2',
|
||||
}
|
||||
IdentityServiceContext.return_value = \
|
||||
DummyIdentityServiceContext(return_value=creds)
|
||||
nutils.get_neutron_client()
|
||||
nclient.assert_called_with(
|
||||
username='bob',
|
||||
tenant_name='tenant1',
|
||||
password='pa55w0rd',
|
||||
auth_url='http://myhost:2222/v2.0',
|
||||
region_name='region2',
|
||||
)
|
||||
|
||||
@patch.object(ncontext, 'IdentityServiceContext')
|
||||
def test_get_neutron_client_noidservice(self, IdentityServiceContext):
|
||||
creds = {}
|
||||
IdentityServiceContext.return_value = \
|
||||
DummyIdentityServiceContext(return_value=creds)
|
||||
self.assertEquals(nutils.get_neutron_client(), None)
|
||||
|
||||
@patch.object(nutils, 'get_neutron_client')
|
||||
def test_router_feature_present_keymissing(self, get_neutron_client):
|
||||
routers = {
|
||||
'routers': [
|
||||
{
|
||||
u'status': u'ACTIVE',
|
||||
u'external_gateway_info': {
|
||||
u'network_id': u'eedffb9b-b93e-49c6-9545-47c656c9678e',
|
||||
u'enable_snat': True
|
||||
}, u'name': u'provider-router',
|
||||
u'admin_state_up': True,
|
||||
u'tenant_id': u'b240d06e38394780a3ea296138cdd174',
|
||||
u'routes': [],
|
||||
u'id': u'84182bc8-eede-4564-9c87-1a56bdb26a90',
|
||||
}
|
||||
]
|
||||
}
|
||||
get_neutron_client.list_routers.return_value = routers
|
||||
self.assertEquals(nutils.router_feature_present('ha'), False)
|
||||
|
||||
@patch.object(nutils, 'get_neutron_client')
|
||||
def test_router_feature_present_keyfalse(self, get_neutron_client):
|
||||
routers = {
|
||||
'routers': [
|
||||
{
|
||||
u'status': u'ACTIVE',
|
||||
u'external_gateway_info': {
|
||||
u'network_id': u'eedffb9b-b93e-49c6-9545-47c656c9678e',
|
||||
u'enable_snat': True
|
||||
}, u'name': u'provider-router',
|
||||
u'admin_state_up': True,
|
||||
u'tenant_id': u'b240d06e38394780a3ea296138cdd174',
|
||||
u'routes': [],
|
||||
u'id': u'84182bc8-eede-4564-9c87-1a56bdb26a90',
|
||||
u'ha': False,
|
||||
}
|
||||
]
|
||||
}
|
||||
dummy_client = MagicMock()
|
||||
dummy_client.list_routers.return_value = routers
|
||||
get_neutron_client.return_value = dummy_client
|
||||
self.assertEquals(nutils.router_feature_present('ha'), False)
|
||||
|
||||
@patch.object(nutils, 'get_neutron_client')
|
||||
def test_router_feature_present_keytrue(self, get_neutron_client):
|
||||
routers = {
|
||||
'routers': [
|
||||
{
|
||||
u'status': u'ACTIVE',
|
||||
u'external_gateway_info': {
|
||||
u'network_id': u'eedffb9b-b93e-49c6-9545-47c656c9678e',
|
||||
u'enable_snat': True
|
||||
}, u'name': u'provider-router',
|
||||
u'admin_state_up': True,
|
||||
u'tenant_id': u'b240d06e38394780a3ea296138cdd174',
|
||||
u'routes': [],
|
||||
u'id': u'84182bc8-eede-4564-9c87-1a56bdb26a90',
|
||||
u'ha': True,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
dummy_client = MagicMock()
|
||||
dummy_client.list_routers.return_value = routers
|
||||
get_neutron_client.return_value = dummy_client
|
||||
self.assertEquals(nutils.router_feature_present('ha'), True)
|
||||
|
||||
@patch.object(nutils, 'get_neutron_client')
|
||||
def test_neutron_ready(self, get_neutron_client):
|
||||
dummy_client = MagicMock()
|
||||
dummy_client.list_routers.return_value = []
|
||||
get_neutron_client.return_value = dummy_client
|
||||
self.assertEquals(nutils.neutron_ready(), True)
|
||||
|
||||
@patch.object(nutils, 'get_neutron_client')
|
||||
def test_neutron_ready_noclient(self, get_neutron_client):
|
||||
get_neutron_client.return_value = None
|
||||
self.assertEquals(nutils.neutron_ready(), False)
|
||||
|
||||
@patch.object(nutils, 'get_neutron_client')
|
||||
def test_neutron_ready_clientexception(self, get_neutron_client):
|
||||
dummy_client = MagicMock()
|
||||
dummy_client.list_routers.side_effect = Exception('Boom!')
|
||||
get_neutron_client.return_value = dummy_client
|
||||
self.assertEquals(nutils.neutron_ready(), False)
|
||||
|
||||
@patch.object(nutils, 'git_install_requested')
|
||||
@patch.object(nutils, 'git_clone_and_install')
|
||||
@patch.object(nutils, 'git_post_install')
|
||||
@patch.object(nutils, 'git_pre_install')
|
||||
def test_git_install(self, git_pre, git_post, git_clone_and_install,
|
||||
git_requested):
|
||||
projects_yaml = openstack_origin_git
|
||||
git_requested.return_value = True
|
||||
nutils.git_install(projects_yaml)
|
||||
self.assertTrue(git_pre.called)
|
||||
git_clone_and_install.assert_called_with(openstack_origin_git,
|
||||
core_project='neutron')
|
||||
self.assertTrue(git_post.called)
|
||||
|
||||
@patch.object(nutils, 'mkdir')
|
||||
@patch.object(nutils, 'write_file')
|
||||
@patch.object(nutils, 'add_user_to_group')
|
||||
@patch.object(nutils, 'add_group')
|
||||
@patch.object(nutils, 'adduser')
|
||||
def test_git_pre_install(self, adduser, add_group, add_user_to_group,
|
||||
write_file, mkdir):
|
||||
nutils.git_pre_install()
|
||||
adduser.assert_called_with('neutron', shell='/bin/bash',
|
||||
system_user=True)
|
||||
add_group.assert_called_with('neutron', system_group=True)
|
||||
add_user_to_group.assert_called_with('neutron', 'neutron')
|
||||
expected = [
|
||||
call('/var/lib/neutron', owner='neutron',
|
||||
group='neutron', perms=0755, force=False),
|
||||
call('/var/lib/neutron/lock', owner='neutron',
|
||||
group='neutron', perms=0755, force=False),
|
||||
call('/var/log/neutron', owner='neutron',
|
||||
group='neutron', perms=0755, force=False),
|
||||
]
|
||||
self.assertEquals(mkdir.call_args_list, expected)
|
||||
expected = [
|
||||
call('/var/log/neutron/server.log', '', owner='neutron',
|
||||
group='neutron', perms=0600),
|
||||
]
|
||||
self.assertEquals(write_file.call_args_list, expected)
|
||||
|
||||
@patch.object(nutils, 'git_src_dir')
|
||||
@patch.object(nutils, 'service_restart')
|
||||
@patch.object(nutils, 'render')
|
||||
@patch('os.path.join')
|
||||
@patch('os.path.exists')
|
||||
@patch('shutil.copytree')
|
||||
@patch('shutil.rmtree')
|
||||
def test_git_post_install(self, rmtree, copytree, exists, join, render,
|
||||
service_restart, git_src_dir):
|
||||
projects_yaml = openstack_origin_git
|
||||
join.return_value = 'joined-string'
|
||||
nutils.git_post_install(projects_yaml)
|
||||
expected = [
|
||||
call('joined-string', '/etc/neutron'),
|
||||
call('joined-string', '/etc/neutron/plugins'),
|
||||
call('joined-string', '/etc/neutron/rootwrap.d'),
|
||||
]
|
||||
copytree.assert_has_calls(expected)
|
||||
neutron_api_context = {
|
||||
'service_description': 'Neutron API server',
|
||||
'charm_name': 'neutron-api',
|
||||
'process_name': 'neutron-server',
|
||||
}
|
||||
expected = [
|
||||
call('git/neutron_sudoers', '/etc/sudoers.d/neutron_sudoers', {},
|
||||
perms=0o440),
|
||||
call('git/upstart/neutron-server.upstart',
|
||||
'/etc/init/neutron-server.conf',
|
||||
neutron_api_context, perms=0o644),
|
||||
]
|
||||
self.assertEquals(render.call_args_list, expected)
|
||||
expected = [
|
||||
call('neutron-server'),
|
||||
]
|
||||
self.assertEquals(service_restart.call_args_list, expected)
|
||||
|
||||
def test_stamp_neutron_database(self):
|
||||
nutils.stamp_neutron_database('icehouse')
|
||||
cmd = ['neutron-db-manage',
|
||||
'--config-file', '/etc/neutron/neutron.conf',
|
||||
'--config-file', '/etc/neutron/plugins/ml2/ml2_conf.ini',
|
||||
'stamp',
|
||||
'icehouse']
|
||||
self.subprocess.check_output.assert_called_with(cmd)
|
||||
|
||||
def test_migrate_neutron_database(self):
|
||||
nutils.migrate_neutron_database()
|
||||
cmd = ['neutron-db-manage',
|
||||
'--config-file', '/etc/neutron/neutron.conf',
|
||||
'--config-file', '/etc/neutron/plugins/ml2/ml2_conf.ini',
|
||||
'upgrade',
|
||||
'head']
|
||||
self.subprocess.check_output.assert_called_with(cmd)
|
||||
|
|
Loading…
Reference in New Issue