diff --git a/Makefile b/Makefile index 00e1717..c353b10 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,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 1200 \ - 00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse + @juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700 bin/charm_helpers_sync.py: @mkdir -p bin diff --git a/config.yaml b/config.yaml index 587ee34..dcb3aec 100644 --- a/config.yaml +++ b/config.yaml @@ -79,6 +79,12 @@ options: default: description: | Base64 encoded SSL key to use with certificate specified as ssl_cert. + ssl_ca: + type: string + default: + description: | + Base64 encoded SSL CA to use with the certificate and key provided - only + required if you are providing a privately signed ssl_cert and ssl_key. # General Swift Proxy configuration bind-port: default: 8080 @@ -116,6 +122,15 @@ options: Timeouts from these requests can be recovered from so setting this to something lower than node-timeout would provide quicker error recovery while allowing for a longer timeout for non-recoverable requests (PUTs). + # Logging configuration + debug: + default: False + type: boolean + description: Enable debug level logging. + log-headers: + default: False + type: boolean + description: Enable logging of all request headers. # Manual Keystone configuration. keystone-auth-host: type: string diff --git a/hooks/charmhelpers/contrib/charmsupport/nrpe.py b/hooks/charmhelpers/contrib/charmsupport/nrpe.py index 9d961cf..95a79c2 100644 --- a/hooks/charmhelpers/contrib/charmsupport/nrpe.py +++ b/hooks/charmhelpers/contrib/charmsupport/nrpe.py @@ -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)) diff --git a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py index 0e0db56..461a702 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py @@ -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): diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index dd51bfb..400eaf8 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -459,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 {} @@ -808,6 +813,19 @@ class NeutronContext(OSContextGenerator): return ovs_ctxt + def nuage_ctxt(self): + driver = neutron_plugin_attribute(self.plugin, 'driver', + 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} + + return nuage_ctxt + def nvp_ctxt(self): driver = neutron_plugin_attribute(self.plugin, 'driver', self.network_manager) @@ -891,6 +909,8 @@ class NeutronContext(OSContextGenerator): 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: diff --git a/hooks/charmhelpers/contrib/openstack/neutron.py b/hooks/charmhelpers/contrib/openstack/neutron.py index f885105..02c92e9 100644 --- a/hooks/charmhelpers/contrib/openstack/neutron.py +++ b/hooks/charmhelpers/contrib/openstack/neutron.py @@ -180,6 +180,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': diff --git a/hooks/charmhelpers/contrib/openstack/templates/git.upstart b/hooks/charmhelpers/contrib/openstack/templates/git.upstart index da94ad1..4bed404 100644 --- a/hooks/charmhelpers/contrib/openstack/templates/git.upstart +++ b/hooks/charmhelpers/contrib/openstack/templates/git.upstart @@ -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 -%} diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py index 78c5e2d..f90a028 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/hooks/charmhelpers/contrib/openstack/utils.py @@ -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): """ diff --git a/hooks/charmhelpers/contrib/peerstorage/__init__.py b/hooks/charmhelpers/contrib/peerstorage/__init__.py index 0f02100..ed89aef 100644 --- a/hooks/charmhelpers/contrib/peerstorage/__init__.py +++ b/hooks/charmhelpers/contrib/peerstorage/__init__.py @@ -175,6 +175,8 @@ def peer_retrieve_by_prefix(prefix, relation_name='cluster', delimiter='_', exc_list = exc_list if exc_list else [] peerdb_settings = peer_retrieve('-', relation_name=relation_name) matched = {} + if peerdb_settings is None: + return matched for k, v in peerdb_settings.items(): full_prefix = prefix + delimiter if k.startswith(full_prefix): diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py index 60820cc..8c97eac 100644 --- a/hooks/charmhelpers/core/hookenv.py +++ b/hooks/charmhelpers/core/hookenv.py @@ -20,11 +20,14 @@ # Authors: # Charm Helpers Developers +from __future__ import print_function +from functools import wraps import os import json import yaml import subprocess import sys +import errno from subprocess import CalledProcessError import six @@ -56,15 +59,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 +92,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 +181,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 +253,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: @@ -507,6 +529,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') @@ -664,3 +691,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 diff --git a/hooks/charmhelpers/core/host.py b/hooks/charmhelpers/core/host.py index 830822a..0d2ab4b 100644 --- a/hooks/charmhelpers/core/host.py +++ b/hooks/charmhelpers/core/host.py @@ -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 diff --git a/hooks/charmhelpers/core/services/base.py b/hooks/charmhelpers/core/services/base.py index c5534e4..2ff5338 100644 --- a/hooks/charmhelpers/core/services/base.py +++ b/hooks/charmhelpers/core/services/base.py @@ -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 diff --git a/hooks/charmhelpers/core/strutils.py b/hooks/charmhelpers/core/strutils.py index efc4402..a2a784a 100644 --- a/hooks/charmhelpers/core/strutils.py +++ b/hooks/charmhelpers/core/strutils.py @@ -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) diff --git a/hooks/charmhelpers/fetch/__init__.py b/hooks/charmhelpers/fetch/__init__.py index 792e629..9a1a251 100644 --- a/hooks/charmhelpers/fetch/__init__.py +++ b/hooks/charmhelpers/fetch/__init__.py @@ -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", "") diff --git a/hooks/swift_context.py b/hooks/swift_context.py index 0c996c2..f9b2519 100644 --- a/hooks/swift_context.py +++ b/hooks/swift_context.py @@ -100,8 +100,14 @@ class SwiftIdentityContext(OSContextGenerator): 'delay_auth_decision': config('delay-auth-decision'), 'node_timeout': config('node-timeout'), 'recoverable_node_timeout': config('recoverable-node-timeout'), + 'log_headers': config('log-headers') } + if config('debug'): + ctxt['log_level'] = 'DEBUG' + else: + ctxt['log_level'] = 'INFO' + # Instead of duplicating code lets use charm-helpers to set signing_dir # TODO(hopem): refactor this context handler to use charm-helpers # code. diff --git a/hooks/swift_hooks.py b/hooks/swift_hooks.py index c269c04..d5218d7 100755 --- a/hooks/swift_hooks.py +++ b/hooks/swift_hooks.py @@ -82,7 +82,6 @@ from charmhelpers.contrib.network.ip import ( format_ipv6_addr, ) from charmhelpers.contrib.openstack.context import ADDRESS_TYPES - from charmhelpers.contrib.charmsupport import nrpe extra_pkgs = [ @@ -147,22 +146,18 @@ def config_changed(): @hooks.hook('identity-service-relation-joined') def keystone_joined(relid=None): - if not is_elected_leader(SWIFT_HA_RES): - return - port = config('bind-port') admin_url = '%s:%s' % (canonical_url(CONFIGS, ADMIN), port) - internal_url = '%s:%s/v1/AUTH_$(tenant_id)s' % \ - (canonical_url(CONFIGS, INTERNAL), port) - public_url = '%s:%s/v1/AUTH_$(tenant_id)s' % \ - (canonical_url(CONFIGS, PUBLIC), port) - relation_set(service='swift', - region=config('region'), - public_url=public_url, - internal_url=internal_url, - admin_url=admin_url, - requested_roles=config('operator-roles'), - relation_id=relid) + internal_url = ('%s:%s/v1/AUTH_$(tenant_id)s' % + (canonical_url(CONFIGS, INTERNAL), port)) + public_url = ('%s:%s/v1/AUTH_$(tenant_id)s' % + (canonical_url(CONFIGS, PUBLIC), port)) + region = config('region') + roles = config('operator-roles') + + relation_set(service='swift', region=region, public_url=public_url, + internal_url=internal_url, admin_url=admin_url, + requested_roles=roles, relation_id=relid) @hooks.hook('identity-service-relation-changed') diff --git a/templates/icehouse/proxy-server.conf b/templates/icehouse/proxy-server.conf index ed7d947..561d457 100644 --- a/templates/icehouse/proxy-server.conf +++ b/templates/icehouse/proxy-server.conf @@ -3,6 +3,12 @@ bind_port = {{ bind_port }} workers = {{ workers }} user = swift bind_ip = {{ bind_host }} +log_name = swift +log_facility = LOG_LOCAL0 +log_level = {{ log_level }} +log_address = /dev/log +log_headers = {{ log_headers }} + {% if ssl %} cert_file = {{ ssl_cert }} key_file = {{ ssl_key }} @@ -10,10 +16,10 @@ key_file = {{ ssl_key }} {% if auth_type == 'keystone' %} [pipeline:main] -pipeline = gatekeeper healthcheck cache swift3 s3token container_sync bulk tempurl slo dlo formpost authtoken keystoneauth staticweb container-quotas account-quotas proxy-server +pipeline = gatekeeper healthcheck proxy-logging cache swift3 s3token container_sync bulk tempurl slo dlo formpost authtoken keystoneauth staticweb container-quotas account-quotas proxy-logging proxy-server {% else %} [pipeline:main] -pipeline = gatekeeper healthcheck cache container_sync bulk tempurl slo dlo formpost tempauth staticweb container-quotas account-quotas proxy-server +pipeline = gatekeeper healthcheck proxy-logging cache container_sync bulk tempurl slo dlo formpost tempauth staticweb container-quotas account-quotas proxy-logging proxy-server {% endif %} [app:proxy-server] @@ -40,6 +46,9 @@ use = egg:swift#account_quotas [filter:container-quotas] use = egg:swift#container_quotas +[filter:proxy-logging] +use = egg:swift#proxy_logging + [filter:staticweb] use = egg:swift#staticweb diff --git a/templates/kilo/proxy-server.conf b/templates/kilo/proxy-server.conf index 24406a3..3b9e9ae 100644 --- a/templates/kilo/proxy-server.conf +++ b/templates/kilo/proxy-server.conf @@ -3,6 +3,12 @@ bind_port = {{ bind_port }} workers = {{ workers }} user = swift bind_ip = {{ bind_host }} +log_name = swift +log_facility = LOG_LOCAL0 +log_level = {{ log_level }} +log_address = /dev/log +log_headers = {{ log_headers }} + {% if ssl %} cert_file = {{ ssl_cert }} key_file = {{ ssl_key }} @@ -10,10 +16,10 @@ key_file = {{ ssl_key }} {% if auth_type == 'keystone' %} [pipeline:main] -pipeline = gatekeeper healthcheck cache swift3 s3token container_sync bulk tempurl slo dlo formpost authtoken keystoneauth staticweb container-quotas account-quotas proxy-server +pipeline = gatekeeper healthcheck proxy-logging cache swift3 s3token container_sync bulk tempurl slo dlo formpost authtoken keystoneauth staticweb container-quotas account-quotas proxy-logging proxy-server {% else %} [pipeline:main] -pipeline = gatekeeper healthcheck cache container_sync bulk tempurl slo dlo formpost tempauth staticweb container-quotas account-quotas proxy-server +pipeline = gatekeeper healthcheck proxy-logging cache container_sync bulk tempurl slo dlo formpost tempauth staticweb container-quotas account-quotas proxy-logging proxy-server {% endif %} [app:proxy-server] @@ -40,6 +46,9 @@ use = egg:swift#account_quotas [filter:container-quotas] use = egg:swift#container_quotas +[filter:proxy-logging] +use = egg:swift#proxy_logging + [filter:staticweb] use = egg:swift#staticweb diff --git a/tests/10-basic-precise-essex b/tests/010-basic-precise-essex similarity index 100% rename from tests/10-basic-precise-essex rename to tests/010-basic-precise-essex diff --git a/tests/14-basic-precise-icehouse b/tests/014-basic-precise-icehouse similarity index 100% rename from tests/14-basic-precise-icehouse rename to tests/014-basic-precise-icehouse diff --git a/tests/15-basic-trusty-icehouse b/tests/015-basic-trusty-icehouse similarity index 100% rename from tests/15-basic-trusty-icehouse rename to tests/015-basic-trusty-icehouse diff --git a/tests/016-basic-trusty-juno b/tests/016-basic-trusty-juno new file mode 100755 index 0000000..4dd2a18 --- /dev/null +++ b/tests/016-basic-trusty-juno @@ -0,0 +1,11 @@ +#!/usr/bin/python + +"""Amulet tests on a basic swift-proxy deployment on trusty-juno.""" + +from basic_deployment import SwiftProxyBasicDeployment + +if __name__ == '__main__': + deployment = SwiftProxyBasicDeployment(series='trusty', + openstack='cloud:trusty-juno', + source='cloud:trusty-updates/juno') + deployment.run_tests() diff --git a/tests/017-basic-trusty-kilo b/tests/017-basic-trusty-kilo new file mode 100644 index 0000000..79c7de4 --- /dev/null +++ b/tests/017-basic-trusty-kilo @@ -0,0 +1,11 @@ +#!/usr/bin/python + +"""Amulet tests on a basic swift-proxy deployment on trusty-kilo.""" + +from basic_deployment import SwiftProxyBasicDeployment + +if __name__ == '__main__': + deployment = SwiftProxyBasicDeployment(series='trusty', + openstack='cloud:trusty-kilo', + source='cloud:trusty-updates/kilo') + deployment.run_tests() diff --git a/tests/018-basic-utopic-juno b/tests/018-basic-utopic-juno new file mode 100755 index 0000000..d8c09ab --- /dev/null +++ b/tests/018-basic-utopic-juno @@ -0,0 +1,9 @@ +#!/usr/bin/python + +"""Amulet tests on a basic swift-proxy deployment on utopic-juno.""" + +from basic_deployment import SwiftProxyBasicDeployment + +if __name__ == '__main__': + deployment = SwiftProxyBasicDeployment(series='utopic') + deployment.run_tests() diff --git a/tests/019-basic-vivid-kilo b/tests/019-basic-vivid-kilo new file mode 100644 index 0000000..30c172b --- /dev/null +++ b/tests/019-basic-vivid-kilo @@ -0,0 +1,9 @@ +#!/usr/bin/python + +"""Amulet tests on a basic swift-proxy deployment on vivid-kilo.""" + +from basic_deployment import SwiftProxyBasicDeployment + +if __name__ == '__main__': + deployment = SwiftProxyBasicDeployment(series='vivid') + deployment.run_tests() diff --git a/tests/11-basic-precise-folsom b/tests/11-basic-precise-folsom deleted file mode 100755 index 82fe6a6..0000000 --- a/tests/11-basic-precise-folsom +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/python - -"""Amulet tests on a basic swift-proxy deployment on precise-folsom.""" - -from basic_deployment import SwiftProxyBasicDeployment - -if __name__ == '__main__': - deployment = SwiftProxyBasicDeployment(series='precise', - openstack='cloud:precise-folsom', - source='cloud:precise-updates/folsom') - deployment.run_tests() diff --git a/tests/12-basic-precise-grizzly b/tests/12-basic-precise-grizzly deleted file mode 100755 index c1a434b..0000000 --- a/tests/12-basic-precise-grizzly +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/python - -"""Amulet tests on a basic swift-proxy deployment on precise-grizzly.""" - -from basic_deployment import SwiftProxyBasicDeployment - -if __name__ == '__main__': - deployment = SwiftProxyBasicDeployment(series='precise', - openstack='cloud:precise-grizzly', - source='cloud:precise-updates/grizzly') - deployment.run_tests() diff --git a/tests/13-basic-precise-havana b/tests/13-basic-precise-havana deleted file mode 100755 index 0a3780a..0000000 --- a/tests/13-basic-precise-havana +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/python - -"""Amulet tests on a basic swift-proxy deployment on precise-havana.""" - -from basic_deployment import SwiftProxyBasicDeployment - -if __name__ == '__main__': - deployment = SwiftProxyBasicDeployment(series='precise', - openstack='cloud:precise-havana', - source='cloud:precise-updates/havana') - deployment.run_tests() diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index c4c9e1d..c80c9ca 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -14,7 +14,7 @@ from charmhelpers.contrib.openstack.amulet.utils import ( ) # Use DEBUG to turn on debug logging -u = OpenStackAmuletUtils(ERROR) +u = OpenStackAmuletUtils(DEBUG) class SwiftProxyBasicDeployment(OpenStackAmuletDeployment): @@ -368,13 +368,19 @@ class SwiftProxyBasicDeployment(OpenStackAmuletDeployment): expected = { 'DEFAULT': { 'bind_port': '8070', - 'user': 'swift' + 'user': 'swift', + 'log_name': 'swift', + 'log_facility': 'LOG_LOCAL0', + 'log_level': 'INFO', + 'log_headers': 'False', + 'log_address': '/dev/log' }, 'pipeline:main': { - 'pipeline': 'gatekeeper healthcheck cache swift3 s3token ' - 'container_sync bulk tempurl slo dlo formpost ' - 'authtoken keystoneauth staticweb ' - 'container-quotas account-quotas proxy-server' + 'pipeline': 'gatekeeper healthcheck proxy-logging cache swift3 ' + 's3token container_sync bulk tempurl slo dlo ' + 'formpost authtoken keystoneauth staticweb ' + 'container-quotas account-quotas proxy-logging ' + 'proxy-server' }, 'app:proxy-server': { 'use': 'egg:swift#proxy', @@ -395,6 +401,7 @@ class SwiftProxyBasicDeployment(OpenStackAmuletDeployment): }, 'filter:account-quotas': {'use': 'egg:swift#account_quotas'}, 'filter:container-quotas': {'use': 'egg:swift#container_quotas'}, + 'filter:proxy-logging': {'use': 'egg:swift#proxy_logging'}, 'filter:staticweb': {'use': 'egg:swift#staticweb'}, 'filter:bulk': {'use': 'egg:swift#bulk'}, 'filter:slo': {'use': 'egg:swift#slo'}, diff --git a/tests/charmhelpers/contrib/amulet/utils.py b/tests/charmhelpers/contrib/amulet/utils.py index 65219d3..f61c2e8 100644 --- a/tests/charmhelpers/contrib/amulet/utils.py +++ b/tests/charmhelpers/contrib/amulet/utils.py @@ -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): diff --git a/tests/charmhelpers/contrib/openstack/amulet/deployment.py b/tests/charmhelpers/contrib/openstack/amulet/deployment.py index 0e0db56..461a702 100644 --- a/tests/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/tests/charmhelpers/contrib/openstack/amulet/deployment.py @@ -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):