Enable Zesty-Ocata Amulet Tests

- Turn on Zesty-Ocata Amulet test definitions.
- Standardize test-requirements.txt
- Sync charm helpers for various fixes

Change-Id: I81aba6403759fb973c3c2b0b4b0fab875ec5fc3b
This commit is contained in:
David Ames 2017-04-27 11:12:09 -07:00
parent b29534d990
commit 3a11b9949c
13 changed files with 157 additions and 70 deletions

View File

@ -111,9 +111,9 @@ def get_address_in_network(network, fallback=None, fatal=False):
for iface in netifaces.interfaces(): for iface in netifaces.interfaces():
addresses = netifaces.ifaddresses(iface) addresses = netifaces.ifaddresses(iface)
if network.version == 4 and netifaces.AF_INET in addresses: if network.version == 4 and netifaces.AF_INET in addresses:
addr = addresses[netifaces.AF_INET][0]['addr'] for addr in addresses[netifaces.AF_INET]:
netmask = addresses[netifaces.AF_INET][0]['netmask'] cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask)) addr['netmask']))
if cidr in network: if cidr in network:
return str(cidr.ip) return str(cidr.ip)
@ -239,6 +239,16 @@ def format_ipv6_addr(address):
return None return None
def is_ipv6_disabled():
try:
result = subprocess.check_output(
['sysctl', 'net.ipv6.conf.all.disable_ipv6'],
stderr=subprocess.STDOUT)
return "net.ipv6.conf.all.disable_ipv6 = 1" in result
except subprocess.CalledProcessError:
return True
def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False,
fatal=True, exc_list=None): fatal=True, exc_list=None):
"""Return the assigned IP address for a given interface, if any. """Return the assigned IP address for a given interface, if any.
@ -544,31 +554,38 @@ def assert_charm_supports_ipv6():
"versions less than Trusty 14.04") "versions less than Trusty 14.04")
def get_relation_ip(interface, config_override=None): def get_relation_ip(interface, cidr_network=None):
"""Return this unit's IP for the given relation. """Return this unit's IP for the given interface.
Allow for an arbitrary interface to use with network-get to select an IP. Allow for an arbitrary interface to use with network-get to select an IP.
Handle all address selection options including configuration parameter Handle all address selection options including passed cidr network and
override and IPv6. IPv6.
Usage: get_relation_ip('amqp', config_override='access-network') Usage: get_relation_ip('amqp', cidr_network='10.0.0.0/8')
@param interface: string name of the relation. @param interface: string name of the relation.
@param config_override: string name of the config option for network @param cidr_network: string CIDR Network to select an address from.
override. Supports legacy network override configuration parameters.
@raises Exception if prefer-ipv6 is configured but IPv6 unsupported. @raises Exception if prefer-ipv6 is configured but IPv6 unsupported.
@returns IPv6 or IPv4 address @returns IPv6 or IPv4 address
""" """
# Select the interface address first
# For possible use as a fallback bellow with get_address_in_network
try:
# Get the interface specific IP
address = network_get_primary_address(interface)
except NotImplementedError:
# If network-get is not available
address = get_host_ip(unit_get('private-address'))
fallback = get_host_ip(unit_get('private-address'))
if config('prefer-ipv6'): if config('prefer-ipv6'):
# Currently IPv6 has priority, eventually we want IPv6 to just be
# another network space.
assert_charm_supports_ipv6() assert_charm_supports_ipv6()
return get_ipv6_addr()[0] return get_ipv6_addr()[0]
elif config_override and config(config_override): elif cidr_network:
return get_address_in_network(config(config_override), # If a specific CIDR network is passed get the address from that
fallback) # network.
else: return get_address_in_network(cidr_network, address)
try:
return network_get_primary_address(interface) # Return the interface address
except NotImplementedError: return address
return fallback

View File

@ -547,7 +547,7 @@ class OpenStackAmuletUtils(AmuletUtils):
"""Create the specified instance.""" """Create the specified instance."""
self.log.debug('Creating instance ' self.log.debug('Creating instance '
'({}|{}|{})'.format(instance_name, image_name, flavor)) '({}|{}|{})'.format(instance_name, image_name, flavor))
image = nova.images.find(name=image_name) image = nova.glance.find_image(image_name)
flavor = nova.flavors.find(name=flavor) flavor = nova.flavors.find(name=flavor)
instance = nova.servers.create(name=instance_name, image=image, instance = nova.servers.create(name=instance_name, image=image,
flavor=flavor) flavor=flavor)

View File

@ -60,6 +60,7 @@ from charmhelpers.core.host import (
pwgen, pwgen,
lsb_release, lsb_release,
CompareHostReleases, CompareHostReleases,
is_container,
) )
from charmhelpers.contrib.hahelpers.cluster import ( from charmhelpers.contrib.hahelpers.cluster import (
determine_apache_port, determine_apache_port,
@ -88,6 +89,7 @@ from charmhelpers.contrib.network.ip import (
format_ipv6_addr, format_ipv6_addr,
is_address_in_network, is_address_in_network,
is_bridge_member, is_bridge_member,
is_ipv6_disabled,
) )
from charmhelpers.contrib.openstack.utils import ( from charmhelpers.contrib.openstack.utils import (
config_flags_parser, config_flags_parser,
@ -109,6 +111,7 @@ except ImportError:
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
ADDRESS_TYPES = ['admin', 'internal', 'public'] ADDRESS_TYPES = ['admin', 'internal', 'public']
HAPROXY_RUN_DIR = '/var/run/haproxy/'
def ensure_packages(packages): def ensure_packages(packages):
@ -534,6 +537,8 @@ class HAProxyContext(OSContextGenerator):
"""Provides half a context for the haproxy template, which describes """Provides half a context for the haproxy template, which describes
all peers to be included in the cluster. Each charm needs to include all peers to be included in the cluster. Each charm needs to include
its own context generator that describes the port mapping. its own context generator that describes the port mapping.
:side effect: mkdir is called on HAPROXY_RUN_DIR
""" """
interfaces = ['cluster'] interfaces = ['cluster']
@ -541,6 +546,8 @@ class HAProxyContext(OSContextGenerator):
self.singlenode_mode = singlenode_mode self.singlenode_mode = singlenode_mode
def __call__(self): def __call__(self):
if not os.path.isdir(HAPROXY_RUN_DIR):
mkdir(path=HAPROXY_RUN_DIR)
if not relation_ids('cluster') and not self.singlenode_mode: if not relation_ids('cluster') and not self.singlenode_mode:
return {} return {}
@ -1221,22 +1228,54 @@ class BindHostContext(OSContextGenerator):
return {'bind_host': '0.0.0.0'} return {'bind_host': '0.0.0.0'}
class WorkerConfigContext(OSContextGenerator): MAX_DEFAULT_WORKERS = 4
DEFAULT_MULTIPLIER = 2
@property
def num_cpus(self):
# NOTE: use cpu_count if present (16.04 support)
if hasattr(psutil, 'cpu_count'):
return psutil.cpu_count()
else:
return psutil.NUM_CPUS
def __call__(self): def _calculate_workers():
multiplier = config('worker-multiplier') or 0 '''
count = int(self.num_cpus * multiplier) Determine the number of worker processes based on the CPU
count of the unit containing the application.
Workers will be limited to MAX_DEFAULT_WORKERS in
container environments where no worker-multipler configuration
option been set.
@returns int: number of worker processes to use
'''
multiplier = config('worker-multiplier') or DEFAULT_MULTIPLIER
count = int(_num_cpus() * multiplier)
if multiplier > 0 and count == 0: if multiplier > 0 and count == 0:
count = 1 count = 1
ctxt = {"workers": count}
if config('worker-multiplier') is None and is_container():
# NOTE(jamespage): Limit unconfigured worker-multiplier
# to MAX_DEFAULT_WORKERS to avoid insane
# worker configuration in LXD containers
# on large servers
# Reference: https://pad.lv/1665270
count = min(count, MAX_DEFAULT_WORKERS)
return count
def _num_cpus():
'''
Compatibility wrapper for calculating the number of CPU's
a unit has.
@returns: int: number of CPU cores detected
'''
try:
return psutil.cpu_count()
except AttributeError:
return psutil.NUM_CPUS
class WorkerConfigContext(OSContextGenerator):
def __call__(self):
ctxt = {"workers": _calculate_workers()}
return ctxt return ctxt
@ -1244,7 +1283,7 @@ class WSGIWorkerConfigContext(WorkerConfigContext):
def __init__(self, name=None, script=None, admin_script=None, def __init__(self, name=None, script=None, admin_script=None,
public_script=None, process_weight=1.00, public_script=None, process_weight=1.00,
admin_process_weight=0.75, public_process_weight=0.25): admin_process_weight=0.25, public_process_weight=0.75):
self.service_name = name self.service_name = name
self.user = name self.user = name
self.group = name self.group = name
@ -1256,8 +1295,7 @@ class WSGIWorkerConfigContext(WorkerConfigContext):
self.public_process_weight = public_process_weight self.public_process_weight = public_process_weight
def __call__(self): def __call__(self):
multiplier = config('worker-multiplier') or 1 total_processes = _calculate_workers()
total_processes = self.num_cpus * multiplier
ctxt = { ctxt = {
"service_name": self.service_name, "service_name": self.service_name,
"user": self.user, "user": self.user,
@ -1588,7 +1626,7 @@ class MemcacheContext(OSContextGenerator):
"""Memcache context """Memcache context
This context provides options for configuring a local memcache client and This context provides options for configuring a local memcache client and
server server for both IPv4 and IPv6
""" """
def __init__(self, package=None): def __init__(self, package=None):
@ -1606,6 +1644,17 @@ class MemcacheContext(OSContextGenerator):
# Trusty version of memcached does not support ::1 as a listen # Trusty version of memcached does not support ::1 as a listen
# address so use host file entry instead # address so use host file entry instead
release = lsb_release()['DISTRIB_CODENAME'].lower() release = lsb_release()['DISTRIB_CODENAME'].lower()
if is_ipv6_disabled():
if CompareHostReleases(release) > 'trusty':
ctxt['memcache_server'] = '127.0.0.1'
else:
ctxt['memcache_server'] = 'localhost'
ctxt['memcache_server_formatted'] = '127.0.0.1'
ctxt['memcache_port'] = '11211'
ctxt['memcache_url'] = '{}:{}'.format(
ctxt['memcache_server_formatted'],
ctxt['memcache_port'])
else:
if CompareHostReleases(release) > 'trusty': if CompareHostReleases(release) > 'trusty':
ctxt['memcache_server'] = '::1' ctxt['memcache_server'] = '::1'
else: else:

View File

@ -5,6 +5,8 @@ global
user haproxy user haproxy
group haproxy group haproxy
spread-checks 0 spread-checks 0
stats socket /var/run/haproxy/admin.sock mode 600 level admin
stats timeout 2m
defaults defaults
log global log global
@ -58,6 +60,15 @@ frontend tcp-in_{{ service }}
{% for frontend in frontends -%} {% for frontend in frontends -%}
backend {{ service }}_{{ frontend }} backend {{ service }}_{{ frontend }}
balance leastconn balance leastconn
{% if backend_options -%}
{% if backend_options[service] -%}
{% for option in backend_options[service] -%}
{% for key, value in option.items() -%}
{{ key }} {{ value }}
{% endfor -%}
{% endfor -%}
{% endif -%}
{% endif -%}
{% for unit, address in frontends[frontend]['backends'].items() -%} {% for unit, address in frontends[frontend]['backends'].items() -%}
server {{ unit }} {{ address }}:{{ ports[1] }} check server {{ unit }} {{ address }}:{{ ports[1] }} check
{% endfor %} {% endfor %}

View File

@ -987,13 +987,15 @@ def ensure_ceph_storage(service, pool, rbd_img, sizemb, mount_point,
service_start(svc) service_start(svc)
def ensure_ceph_keyring(service, user=None, group=None, relation='ceph'): def ensure_ceph_keyring(service, user=None, group=None,
relation='ceph', key=None):
"""Ensures a ceph keyring is created for a named service and optionally """Ensures a ceph keyring is created for a named service and optionally
ensures user and group ownership. ensures user and group ownership.
Returns False if no ceph key is available in relation state. @returns boolean: Flag to indicate whether a key was successfully written
to disk based on either relation data or a supplied key
""" """
key = None if not key:
for rid in relation_ids(relation): for rid in relation_ids(relation):
for unit in related_units(rid): for unit in related_units(rid):
key = relation_get('key', rid=rid, unit=unit) key = relation_get('key', rid=rid, unit=unit)

View File

@ -191,7 +191,7 @@ def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d",
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
sysv_file = os.path.join(initd_dir, service_name) sysv_file = os.path.join(initd_dir, service_name)
if init_is_systemd(): if init_is_systemd():
service('disable', service_name) service('mask', service_name)
elif os.path.exists(upstart_file): elif os.path.exists(upstart_file):
override_path = os.path.join( override_path = os.path.join(
init_dir, '{}.override'.format(service_name)) init_dir, '{}.override'.format(service_name))
@ -224,7 +224,7 @@ def service_resume(service_name, init_dir="/etc/init",
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
sysv_file = os.path.join(initd_dir, service_name) sysv_file = os.path.join(initd_dir, service_name)
if init_is_systemd(): if init_is_systemd():
service('enable', service_name) service('unmask', service_name)
elif os.path.exists(upstart_file): elif os.path.exists(upstart_file):
override_path = os.path.join( override_path = os.path.join(
init_dir, '{}.override'.format(service_name)) init_dir, '{}.override'.format(service_name))

View File

@ -9,6 +9,7 @@ tags:
- miscellaneous - miscellaneous
series: series:
- xenial - xenial
- zesty
- trusty - trusty
- yakkety - yakkety
subordinate: true subordinate: true

View File

@ -11,15 +11,17 @@ requests==2.6.0
# Liberty client lower constraints # Liberty client lower constraints
amulet>=1.14.3,<2.0 amulet>=1.14.3,<2.0
bundletester>=0.6.1,<1.0 bundletester>=0.6.1,<1.0
python-ceilometerclient>=1.5.0,<2.0 python-ceilometerclient>=1.5.0
python-cinderclient>=1.4.0,<2.0 python-cinderclient>=1.4.0
python-glanceclient>=1.1.0,<2.0 python-glanceclient>=1.1.0
python-heatclient>=0.8.0,<1.0 python-heatclient>=0.8.0
python-keystoneclient>=1.7.1,<2.0 python-keystoneclient>=1.7.1
python-neutronclient>=3.1.0,<4.0 python-neutronclient>=3.1.0
python-novaclient>=2.30.1,<3.0 python-novaclient>=2.30.1
python-openstackclient>=1.7.0,<2.0 python-openstackclient>=1.7.0
python-swiftclient>=2.6.0,<3.0 python-swiftclient>=2.6.0
pika>=0.10.0,<1.0 pika>=0.10.0,<1.0
distro-info distro-info
# END: Amulet OpenStack Charm Helper Requirements # END: Amulet OpenStack Charm Helper Requirements
# NOTE: workaround for 14.04 pip/tox
pytz

View File

@ -547,7 +547,7 @@ class OpenStackAmuletUtils(AmuletUtils):
"""Create the specified instance.""" """Create the specified instance."""
self.log.debug('Creating instance ' self.log.debug('Creating instance '
'({}|{}|{})'.format(instance_name, image_name, flavor)) '({}|{}|{})'.format(instance_name, image_name, flavor))
image = nova.images.find(name=image_name) image = nova.glance.find_image(image_name)
flavor = nova.flavors.find(name=flavor) flavor = nova.flavors.find(name=flavor)
instance = nova.servers.create(name=instance_name, image=image, instance = nova.servers.create(name=instance_name, image=image,
flavor=flavor) flavor=flavor)

View File

@ -191,7 +191,7 @@ def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d",
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
sysv_file = os.path.join(initd_dir, service_name) sysv_file = os.path.join(initd_dir, service_name)
if init_is_systemd(): if init_is_systemd():
service('disable', service_name) service('mask', service_name)
elif os.path.exists(upstart_file): elif os.path.exists(upstart_file):
override_path = os.path.join( override_path = os.path.join(
init_dir, '{}.override'.format(service_name)) init_dir, '{}.override'.format(service_name))
@ -224,7 +224,7 @@ def service_resume(service_name, init_dir="/etc/init",
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
sysv_file = os.path.join(initd_dir, service_name) sysv_file = os.path.join(initd_dir, service_name)
if init_is_systemd(): if init_is_systemd():
service('enable', service_name) service('unmask', service_name)
elif os.path.exists(upstart_file): elif os.path.exists(upstart_file):
override_path = os.path.join( override_path = os.path.join(
init_dir, '{}.override'.format(service_name)) init_dir, '{}.override'.format(service_name))

0
tests/gate-basic-xenial-ocata Normal file → Executable file
View File

0
tests/gate-basic-zesty-ocata Normal file → Executable file
View File

View File

@ -14,13 +14,18 @@ install_command =
pip install --allow-unverified python-apt {opts} {packages} pip install --allow-unverified python-apt {opts} {packages}
commands = ostestr {posargs} commands = ostestr {posargs}
whitelist_externals = juju whitelist_externals = juju
passenv = HOME TERM AMULET_* CS_API_* passenv = HOME TERM AMULET_* CS_API_URL
[testenv:py27] [testenv:py27]
basepython = python2.7 basepython = python2.7
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
[testenv:py35]
basepython = python3.5
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:pep8] [testenv:pep8]
basepython = python2.7 basepython = python2.7
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt