charmhelpers sync to fix py3 contexts

Change-Id: I5253c5b0cf39434c7bec2fb4d6f00647417418d3
This commit is contained in:
Chris MacNaughton 2017-11-21 11:48:52 +01:00
parent 59158c417f
commit 96f91fe2bd
13 changed files with 197 additions and 45 deletions

View File

@ -30,6 +30,7 @@ import yaml
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
config, config,
hook_name,
local_unit, local_unit,
log, log,
relation_ids, relation_ids,
@ -302,6 +303,11 @@ class NRPE(object):
"command": nrpecheck.command, "command": nrpecheck.command,
} }
# update-status hooks are configured to firing every 5 minutes by
# default. When nagios-nrpe-server is restarted, the nagios server
# reports checks failing causing unneccessary alerts. Let's not restart
# on update-status hooks.
if not hook_name() == 'update-status':
service('restart', 'nagios-nrpe-server') service('restart', 'nagios-nrpe-server')
monitor_ids = relation_ids("local-monitors") + \ monitor_ids = relation_ids("local-monitors") + \

View File

@ -90,6 +90,6 @@ def install_ca_cert(ca_cert):
log("CA cert is the same as installed version", level=INFO) log("CA cert is the same as installed version", level=INFO)
else: else:
log("Installing new CA cert", level=INFO) log("Installing new CA cert", level=INFO)
with open(cert_file, 'w') as crt: with open(cert_file, 'wb') as crt:
crt.write(ca_cert) crt.write(ca_cert)
subprocess.check_call(['update-ca-certificates', '--fresh']) subprocess.check_call(['update-ca-certificates', '--fresh'])

View File

@ -250,7 +250,14 @@ class OpenStackAmuletDeployment(AmuletDeployment):
self.log.debug('Waiting up to {}s for extended status on services: ' self.log.debug('Waiting up to {}s for extended status on services: '
'{}'.format(timeout, services)) '{}'.format(timeout, services))
service_messages = {service: message for service in services} service_messages = {service: message for service in services}
# Check for idleness
self.d.sentry.wait()
# Check for error states and bail early
self.d.sentry.wait_for_status(self.d.juju_env, services)
# Check for ready messages
self.d.sentry.wait_for_messages(service_messages, timeout=timeout) self.d.sentry.wait_for_messages(service_messages, timeout=timeout)
self.log.info('OK') self.log.info('OK')
def _get_openstack_release(self): def _get_openstack_release(self):
@ -263,7 +270,8 @@ class OpenStackAmuletDeployment(AmuletDeployment):
(self.trusty_icehouse, self.trusty_kilo, self.trusty_liberty, (self.trusty_icehouse, self.trusty_kilo, self.trusty_liberty,
self.trusty_mitaka, self.xenial_mitaka, self.xenial_newton, self.trusty_mitaka, self.xenial_mitaka, self.xenial_newton,
self.yakkety_newton, self.xenial_ocata, self.zesty_ocata, self.yakkety_newton, self.xenial_ocata, self.zesty_ocata,
self.xenial_pike, self.artful_pike) = range(11) self.xenial_pike, self.artful_pike, self.xenial_queens,
self.bionic_queens,) = range(13)
releases = { releases = {
('trusty', None): self.trusty_icehouse, ('trusty', None): self.trusty_icehouse,
@ -274,9 +282,11 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('xenial', 'cloud:xenial-newton'): self.xenial_newton, ('xenial', 'cloud:xenial-newton'): self.xenial_newton,
('xenial', 'cloud:xenial-ocata'): self.xenial_ocata, ('xenial', 'cloud:xenial-ocata'): self.xenial_ocata,
('xenial', 'cloud:xenial-pike'): self.xenial_pike, ('xenial', 'cloud:xenial-pike'): self.xenial_pike,
('xenial', 'cloud:xenial-queens'): self.xenial_queens,
('yakkety', None): self.yakkety_newton, ('yakkety', None): self.yakkety_newton,
('zesty', None): self.zesty_ocata, ('zesty', None): self.zesty_ocata,
('artful', None): self.artful_pike, ('artful', None): self.artful_pike,
('bionic', None): self.bionic_queens,
} }
return releases[(self.series, self.openstack)] return releases[(self.series, self.openstack)]
@ -291,6 +301,7 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('yakkety', 'newton'), ('yakkety', 'newton'),
('zesty', 'ocata'), ('zesty', 'ocata'),
('artful', 'pike'), ('artful', 'pike'),
('bionic', 'queens'),
]) ])
if self.openstack: if self.openstack:
os_origin = self.openstack.split(':')[1] os_origin = self.openstack.split(':')[1]
@ -303,8 +314,8 @@ class OpenStackAmuletDeployment(AmuletDeployment):
test scenario, based on OpenStack release and whether ceph radosgw test scenario, based on OpenStack release and whether ceph radosgw
is flagged as present or not.""" is flagged as present or not."""
if self._get_openstack_release() <= self.trusty_juno: if self._get_openstack_release() == self.trusty_icehouse:
# Juno or earlier # Icehouse
pools = [ pools = [
'data', 'data',
'metadata', 'metadata',
@ -312,7 +323,7 @@ class OpenStackAmuletDeployment(AmuletDeployment):
'cinder-ceph', 'cinder-ceph',
'glance' 'glance'
] ]
elif (self.trust_kilo <= self._get_openstack_release() <= elif (self.trusty_kilo <= self._get_openstack_release() <=
self.zesty_ocata): self.zesty_ocata):
# Kilo through Ocata # Kilo through Ocata
pools = [ pools = [

View File

@ -43,7 +43,6 @@ import swiftclient
from charmhelpers.contrib.amulet.utils import ( from charmhelpers.contrib.amulet.utils import (
AmuletUtils AmuletUtils
) )
from charmhelpers.core.decorators import retry_on_exception
from charmhelpers.core.host import CompareHostReleases from charmhelpers.core.host import CompareHostReleases
DEBUG = logging.DEBUG DEBUG = logging.DEBUG
@ -311,7 +310,6 @@ class OpenStackAmuletUtils(AmuletUtils):
self.log.debug('Checking if tenant exists ({})...'.format(tenant)) self.log.debug('Checking if tenant exists ({})...'.format(tenant))
return tenant in [t.name for t in keystone.tenants.list()] return tenant in [t.name for t in keystone.tenants.list()]
@retry_on_exception(5, base_delay=10)
def keystone_wait_for_propagation(self, sentry_relation_pairs, def keystone_wait_for_propagation(self, sentry_relation_pairs,
api_version): api_version):
"""Iterate over list of sentry and relation tuples and verify that """Iterate over list of sentry and relation tuples and verify that
@ -327,7 +325,7 @@ class OpenStackAmuletUtils(AmuletUtils):
rel = sentry.relation('identity-service', rel = sentry.relation('identity-service',
relation_name) relation_name)
self.log.debug('keystone relation data: {}'.format(rel)) self.log.debug('keystone relation data: {}'.format(rel))
if rel['api_version'] != str(api_version): if rel.get('api_version') != str(api_version):
raise Exception("api_version not propagated through relation" raise Exception("api_version not propagated through relation"
" data yet ('{}' != '{}')." " data yet ('{}' != '{}')."
"".format(rel['api_version'], api_version)) "".format(rel['api_version'], api_version))
@ -349,6 +347,7 @@ class OpenStackAmuletUtils(AmuletUtils):
config = {'preferred-api-version': api_version} config = {'preferred-api-version': api_version}
deployment.d.configure('keystone', config) deployment.d.configure('keystone', config)
deployment._auto_wait_for_status()
self.keystone_wait_for_propagation(sentry_relation_pairs, api_version) self.keystone_wait_for_propagation(sentry_relation_pairs, api_version)
def authenticate_cinder_admin(self, keystone_sentry, username, def authenticate_cinder_admin(self, keystone_sentry, username,
@ -628,6 +627,18 @@ class OpenStackAmuletUtils(AmuletUtils):
_keypair = nova.keypairs.create(name=keypair_name) _keypair = nova.keypairs.create(name=keypair_name)
return _keypair return _keypair
def _get_cinder_obj_name(self, cinder_object):
"""Retrieve name of cinder object.
:param cinder_object: cinder snapshot or volume object
:returns: str cinder object name
"""
# v1 objects store name in 'display_name' attr but v2+ use 'name'
try:
return cinder_object.display_name
except AttributeError:
return cinder_object.name
def create_cinder_volume(self, cinder, vol_name="demo-vol", vol_size=1, def create_cinder_volume(self, cinder, vol_name="demo-vol", vol_size=1,
img_id=None, src_vol_id=None, snap_id=None): img_id=None, src_vol_id=None, snap_id=None):
"""Create cinder volume, optionally from a glance image, OR """Create cinder volume, optionally from a glance image, OR
@ -678,6 +689,13 @@ class OpenStackAmuletUtils(AmuletUtils):
source_volid=src_vol_id, source_volid=src_vol_id,
snapshot_id=snap_id) snapshot_id=snap_id)
vol_id = vol_new.id vol_id = vol_new.id
except TypeError:
vol_new = cinder.volumes.create(name=vol_name,
imageRef=img_id,
size=vol_size,
source_volid=src_vol_id,
snapshot_id=snap_id)
vol_id = vol_new.id
except Exception as e: except Exception as e:
msg = 'Failed to create volume: {}'.format(e) msg = 'Failed to create volume: {}'.format(e)
amulet.raise_status(amulet.FAIL, msg=msg) amulet.raise_status(amulet.FAIL, msg=msg)
@ -692,7 +710,7 @@ class OpenStackAmuletUtils(AmuletUtils):
# Re-validate new volume # Re-validate new volume
self.log.debug('Validating volume attributes...') self.log.debug('Validating volume attributes...')
val_vol_name = cinder.volumes.get(vol_id).display_name val_vol_name = self._get_cinder_obj_name(cinder.volumes.get(vol_id))
val_vol_boot = cinder.volumes.get(vol_id).bootable val_vol_boot = cinder.volumes.get(vol_id).bootable
val_vol_stat = cinder.volumes.get(vol_id).status val_vol_stat = cinder.volumes.get(vol_id).status
val_vol_size = cinder.volumes.get(vol_id).size val_vol_size = cinder.volumes.get(vol_id).size

View File

@ -293,7 +293,7 @@ class PostgresqlDBContext(OSContextGenerator):
def db_ssl(rdata, ctxt, ssl_dir): def db_ssl(rdata, ctxt, ssl_dir):
if 'ssl_ca' in rdata and ssl_dir: if 'ssl_ca' in rdata and ssl_dir:
ca_path = os.path.join(ssl_dir, 'db-client.ca') ca_path = os.path.join(ssl_dir, 'db-client.ca')
with open(ca_path, 'w') as fh: with open(ca_path, 'wb') as fh:
fh.write(b64decode(rdata['ssl_ca'])) fh.write(b64decode(rdata['ssl_ca']))
ctxt['database_ssl_ca'] = ca_path ctxt['database_ssl_ca'] = ca_path
@ -308,12 +308,12 @@ def db_ssl(rdata, ctxt, ssl_dir):
log("Waiting 1m for ssl client cert validity", level=INFO) log("Waiting 1m for ssl client cert validity", level=INFO)
time.sleep(60) time.sleep(60)
with open(cert_path, 'w') as fh: with open(cert_path, 'wb') as fh:
fh.write(b64decode(rdata['ssl_cert'])) fh.write(b64decode(rdata['ssl_cert']))
ctxt['database_ssl_cert'] = cert_path ctxt['database_ssl_cert'] = cert_path
key_path = os.path.join(ssl_dir, 'db-client.key') key_path = os.path.join(ssl_dir, 'db-client.key')
with open(key_path, 'w') as fh: with open(key_path, 'wb') as fh:
fh.write(b64decode(rdata['ssl_key'])) fh.write(b64decode(rdata['ssl_key']))
ctxt['database_ssl_key'] = key_path ctxt['database_ssl_key'] = key_path
@ -459,7 +459,7 @@ class AMQPContext(OSContextGenerator):
ca_path = os.path.join( ca_path = os.path.join(
self.ssl_dir, 'rabbit-client-ca.pem') self.ssl_dir, 'rabbit-client-ca.pem')
with open(ca_path, 'w') as fh: with open(ca_path, 'wb') as fh:
fh.write(b64decode(ctxt['rabbit_ssl_ca'])) fh.write(b64decode(ctxt['rabbit_ssl_ca']))
ctxt['rabbit_ssl_ca'] = ca_path ctxt['rabbit_ssl_ca'] = ca_path
@ -853,15 +853,6 @@ class NeutronContext(OSContextGenerator):
for pkgs in self.packages: for pkgs in self.packages:
ensure_packages(pkgs) ensure_packages(pkgs)
def _save_flag_file(self):
if self.network_manager == 'quantum':
_file = '/etc/nova/quantum_plugin.conf'
else:
_file = '/etc/nova/neutron_plugin.conf'
with open(_file, 'wb') as out:
out.write(self.plugin + '\n')
def ovs_ctxt(self): def ovs_ctxt(self):
driver = neutron_plugin_attribute(self.plugin, 'driver', driver = neutron_plugin_attribute(self.plugin, 'driver',
self.network_manager) self.network_manager)
@ -1006,7 +997,6 @@ class NeutronContext(OSContextGenerator):
flags = config_flags_parser(alchemy_flags) flags = config_flags_parser(alchemy_flags)
ctxt['neutron_alchemy_flags'] = flags ctxt['neutron_alchemy_flags'] = flags
self._save_flag_file()
return ctxt return ctxt

View File

@ -18,7 +18,7 @@ rbd default features = {{ rbd_features }}
[client] [client]
{% if rbd_client_cache_settings -%} {% if rbd_client_cache_settings -%}
{% for key, value in rbd_client_cache_settings.iteritems() -%} {% for key, value in rbd_client_cache_settings.items() -%}
{{ key }} = {{ value }} {{ key }} = {{ value }}
{% endfor -%} {% endfor -%}
{%- endif %} {%- endif %}

View File

@ -140,6 +140,7 @@ UBUNTU_OPENSTACK_RELEASE = OrderedDict([
('yakkety', 'newton'), ('yakkety', 'newton'),
('zesty', 'ocata'), ('zesty', 'ocata'),
('artful', 'pike'), ('artful', 'pike'),
('bionic', 'queens'),
]) ])
@ -157,6 +158,7 @@ OPENSTACK_CODENAMES = OrderedDict([
('2016.2', 'newton'), ('2016.2', 'newton'),
('2017.1', 'ocata'), ('2017.1', 'ocata'),
('2017.2', 'pike'), ('2017.2', 'pike'),
('2018.1', 'queens'),
]) ])
# The ugly duckling - must list releases oldest to newest # The ugly duckling - must list releases oldest to newest
@ -187,6 +189,8 @@ SWIFT_CODENAMES = OrderedDict([
['2.11.0', '2.12.0', '2.13.0']), ['2.11.0', '2.12.0', '2.13.0']),
('pike', ('pike',
['2.13.0', '2.15.0']), ['2.13.0', '2.15.0']),
('queens',
['2.16.0']),
]) ])
# >= Liberty version->codename mapping # >= Liberty version->codename mapping
@ -412,6 +416,8 @@ def get_os_codename_package(package, fatal=True):
cmd = ['snap', 'list', package] cmd = ['snap', 'list', package]
try: try:
out = subprocess.check_output(cmd) out = subprocess.check_output(cmd)
if six.PY3:
out = out.decode('UTF-8')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
return None return None
lines = out.split('\n') lines = out.split('\n')
@ -618,7 +624,7 @@ def save_script_rc(script_path="scripts/scriptrc", **env_vars):
juju_rc_path = "%s/%s" % (charm_dir(), script_path) juju_rc_path = "%s/%s" % (charm_dir(), script_path)
if not os.path.exists(os.path.dirname(juju_rc_path)): if not os.path.exists(os.path.dirname(juju_rc_path)):
os.mkdir(os.path.dirname(juju_rc_path)) os.mkdir(os.path.dirname(juju_rc_path))
with open(juju_rc_path, 'wb') as rc_script: with open(juju_rc_path, 'wt') as rc_script:
rc_script.write( rc_script.write(
"#!/bin/bash\n") "#!/bin/bash\n")
[rc_script.write('export %s=%s\n' % (u, p)) [rc_script.write('export %s=%s\n' % (u, p))

View File

@ -22,6 +22,7 @@ from __future__ import print_function
import copy import copy
from distutils.version import LooseVersion from distutils.version import LooseVersion
from functools import wraps from functools import wraps
from collections import namedtuple
import glob import glob
import os import os
import json import json
@ -1164,3 +1165,42 @@ def meter_info():
"""Get the meter status information, if running in the meter-status-changed """Get the meter status information, if running in the meter-status-changed
hook.""" hook."""
return os.environ.get('JUJU_METER_INFO') return os.environ.get('JUJU_METER_INFO')
def iter_units_for_relation_name(relation_name):
"""Iterate through all units in a relation
Generator that iterates through all the units in a relation and yields
a named tuple with rid and unit field names.
Usage:
data = [(u.rid, u.unit)
for u in iter_units_for_relation_name(relation_name)]
:param relation_name: string relation name
:yield: Named Tuple with rid and unit field names
"""
RelatedUnit = namedtuple('RelatedUnit', 'rid, unit')
for rid in relation_ids(relation_name):
for unit in related_units(rid):
yield RelatedUnit(rid, unit)
def ingress_address(rid=None, unit=None):
"""
Retrieve the ingress-address from a relation when available. Otherwise,
return the private-address. This function is to be used on the consuming
side of the relation.
Usage:
addresses = [ingress_address(rid=u.rid, unit=u.unit)
for u in iter_units_for_relation_name(relation_name)]
:param rid: string relation id
:param unit: string unit name
:side effect: calls relation_get
:return: string IP address
"""
settings = relation_get(rid=rid, unit=unit)
return (settings.get('ingress-address') or
settings.get('private-address'))

View File

@ -61,13 +61,19 @@ def bytes_from_string(value):
if isinstance(value, six.string_types): if isinstance(value, six.string_types):
value = six.text_type(value) value = six.text_type(value)
else: else:
msg = "Unable to interpret non-string value '%s' as boolean" % (value) msg = "Unable to interpret non-string value '%s' as bytes" % (value)
raise ValueError(msg) raise ValueError(msg)
matches = re.match("([0-9]+)([a-zA-Z]+)", value) matches = re.match("([0-9]+)([a-zA-Z]+)", value)
if not matches: if matches:
size = int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])
else:
# Assume that value passed in is bytes
try:
size = int(value)
except ValueError:
msg = "Unable to interpret string value '%s' as bytes" % (value) msg = "Unable to interpret string value '%s' as bytes" % (value)
raise ValueError(msg) raise ValueError(msg)
return int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)]) return size
class BasicStringComparator(object): class BasicStringComparator(object):

View File

@ -250,7 +250,14 @@ class OpenStackAmuletDeployment(AmuletDeployment):
self.log.debug('Waiting up to {}s for extended status on services: ' self.log.debug('Waiting up to {}s for extended status on services: '
'{}'.format(timeout, services)) '{}'.format(timeout, services))
service_messages = {service: message for service in services} service_messages = {service: message for service in services}
# Check for idleness
self.d.sentry.wait()
# Check for error states and bail early
self.d.sentry.wait_for_status(self.d.juju_env, services)
# Check for ready messages
self.d.sentry.wait_for_messages(service_messages, timeout=timeout) self.d.sentry.wait_for_messages(service_messages, timeout=timeout)
self.log.info('OK') self.log.info('OK')
def _get_openstack_release(self): def _get_openstack_release(self):
@ -263,7 +270,8 @@ class OpenStackAmuletDeployment(AmuletDeployment):
(self.trusty_icehouse, self.trusty_kilo, self.trusty_liberty, (self.trusty_icehouse, self.trusty_kilo, self.trusty_liberty,
self.trusty_mitaka, self.xenial_mitaka, self.xenial_newton, self.trusty_mitaka, self.xenial_mitaka, self.xenial_newton,
self.yakkety_newton, self.xenial_ocata, self.zesty_ocata, self.yakkety_newton, self.xenial_ocata, self.zesty_ocata,
self.xenial_pike, self.artful_pike) = range(11) self.xenial_pike, self.artful_pike, self.xenial_queens,
self.bionic_queens,) = range(13)
releases = { releases = {
('trusty', None): self.trusty_icehouse, ('trusty', None): self.trusty_icehouse,
@ -274,9 +282,11 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('xenial', 'cloud:xenial-newton'): self.xenial_newton, ('xenial', 'cloud:xenial-newton'): self.xenial_newton,
('xenial', 'cloud:xenial-ocata'): self.xenial_ocata, ('xenial', 'cloud:xenial-ocata'): self.xenial_ocata,
('xenial', 'cloud:xenial-pike'): self.xenial_pike, ('xenial', 'cloud:xenial-pike'): self.xenial_pike,
('xenial', 'cloud:xenial-queens'): self.xenial_queens,
('yakkety', None): self.yakkety_newton, ('yakkety', None): self.yakkety_newton,
('zesty', None): self.zesty_ocata, ('zesty', None): self.zesty_ocata,
('artful', None): self.artful_pike, ('artful', None): self.artful_pike,
('bionic', None): self.bionic_queens,
} }
return releases[(self.series, self.openstack)] return releases[(self.series, self.openstack)]
@ -291,6 +301,7 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('yakkety', 'newton'), ('yakkety', 'newton'),
('zesty', 'ocata'), ('zesty', 'ocata'),
('artful', 'pike'), ('artful', 'pike'),
('bionic', 'queens'),
]) ])
if self.openstack: if self.openstack:
os_origin = self.openstack.split(':')[1] os_origin = self.openstack.split(':')[1]
@ -303,8 +314,8 @@ class OpenStackAmuletDeployment(AmuletDeployment):
test scenario, based on OpenStack release and whether ceph radosgw test scenario, based on OpenStack release and whether ceph radosgw
is flagged as present or not.""" is flagged as present or not."""
if self._get_openstack_release() <= self.trusty_juno: if self._get_openstack_release() == self.trusty_icehouse:
# Juno or earlier # Icehouse
pools = [ pools = [
'data', 'data',
'metadata', 'metadata',
@ -312,7 +323,7 @@ class OpenStackAmuletDeployment(AmuletDeployment):
'cinder-ceph', 'cinder-ceph',
'glance' 'glance'
] ]
elif (self.trust_kilo <= self._get_openstack_release() <= elif (self.trusty_kilo <= self._get_openstack_release() <=
self.zesty_ocata): self.zesty_ocata):
# Kilo through Ocata # Kilo through Ocata
pools = [ pools = [

View File

@ -43,7 +43,6 @@ import swiftclient
from charmhelpers.contrib.amulet.utils import ( from charmhelpers.contrib.amulet.utils import (
AmuletUtils AmuletUtils
) )
from charmhelpers.core.decorators import retry_on_exception
from charmhelpers.core.host import CompareHostReleases from charmhelpers.core.host import CompareHostReleases
DEBUG = logging.DEBUG DEBUG = logging.DEBUG
@ -311,7 +310,6 @@ class OpenStackAmuletUtils(AmuletUtils):
self.log.debug('Checking if tenant exists ({})...'.format(tenant)) self.log.debug('Checking if tenant exists ({})...'.format(tenant))
return tenant in [t.name for t in keystone.tenants.list()] return tenant in [t.name for t in keystone.tenants.list()]
@retry_on_exception(5, base_delay=10)
def keystone_wait_for_propagation(self, sentry_relation_pairs, def keystone_wait_for_propagation(self, sentry_relation_pairs,
api_version): api_version):
"""Iterate over list of sentry and relation tuples and verify that """Iterate over list of sentry and relation tuples and verify that
@ -327,7 +325,7 @@ class OpenStackAmuletUtils(AmuletUtils):
rel = sentry.relation('identity-service', rel = sentry.relation('identity-service',
relation_name) relation_name)
self.log.debug('keystone relation data: {}'.format(rel)) self.log.debug('keystone relation data: {}'.format(rel))
if rel['api_version'] != str(api_version): if rel.get('api_version') != str(api_version):
raise Exception("api_version not propagated through relation" raise Exception("api_version not propagated through relation"
" data yet ('{}' != '{}')." " data yet ('{}' != '{}')."
"".format(rel['api_version'], api_version)) "".format(rel['api_version'], api_version))
@ -349,6 +347,7 @@ class OpenStackAmuletUtils(AmuletUtils):
config = {'preferred-api-version': api_version} config = {'preferred-api-version': api_version}
deployment.d.configure('keystone', config) deployment.d.configure('keystone', config)
deployment._auto_wait_for_status()
self.keystone_wait_for_propagation(sentry_relation_pairs, api_version) self.keystone_wait_for_propagation(sentry_relation_pairs, api_version)
def authenticate_cinder_admin(self, keystone_sentry, username, def authenticate_cinder_admin(self, keystone_sentry, username,
@ -628,6 +627,18 @@ class OpenStackAmuletUtils(AmuletUtils):
_keypair = nova.keypairs.create(name=keypair_name) _keypair = nova.keypairs.create(name=keypair_name)
return _keypair return _keypair
def _get_cinder_obj_name(self, cinder_object):
"""Retrieve name of cinder object.
:param cinder_object: cinder snapshot or volume object
:returns: str cinder object name
"""
# v1 objects store name in 'display_name' attr but v2+ use 'name'
try:
return cinder_object.display_name
except AttributeError:
return cinder_object.name
def create_cinder_volume(self, cinder, vol_name="demo-vol", vol_size=1, def create_cinder_volume(self, cinder, vol_name="demo-vol", vol_size=1,
img_id=None, src_vol_id=None, snap_id=None): img_id=None, src_vol_id=None, snap_id=None):
"""Create cinder volume, optionally from a glance image, OR """Create cinder volume, optionally from a glance image, OR
@ -678,6 +689,13 @@ class OpenStackAmuletUtils(AmuletUtils):
source_volid=src_vol_id, source_volid=src_vol_id,
snapshot_id=snap_id) snapshot_id=snap_id)
vol_id = vol_new.id vol_id = vol_new.id
except TypeError:
vol_new = cinder.volumes.create(name=vol_name,
imageRef=img_id,
size=vol_size,
source_volid=src_vol_id,
snapshot_id=snap_id)
vol_id = vol_new.id
except Exception as e: except Exception as e:
msg = 'Failed to create volume: {}'.format(e) msg = 'Failed to create volume: {}'.format(e)
amulet.raise_status(amulet.FAIL, msg=msg) amulet.raise_status(amulet.FAIL, msg=msg)
@ -692,7 +710,7 @@ class OpenStackAmuletUtils(AmuletUtils):
# Re-validate new volume # Re-validate new volume
self.log.debug('Validating volume attributes...') self.log.debug('Validating volume attributes...')
val_vol_name = cinder.volumes.get(vol_id).display_name val_vol_name = self._get_cinder_obj_name(cinder.volumes.get(vol_id))
val_vol_boot = cinder.volumes.get(vol_id).bootable val_vol_boot = cinder.volumes.get(vol_id).bootable
val_vol_stat = cinder.volumes.get(vol_id).status val_vol_stat = cinder.volumes.get(vol_id).status
val_vol_size = cinder.volumes.get(vol_id).size val_vol_size = cinder.volumes.get(vol_id).size

View File

@ -22,6 +22,7 @@ from __future__ import print_function
import copy import copy
from distutils.version import LooseVersion from distutils.version import LooseVersion
from functools import wraps from functools import wraps
from collections import namedtuple
import glob import glob
import os import os
import json import json
@ -1164,3 +1165,42 @@ def meter_info():
"""Get the meter status information, if running in the meter-status-changed """Get the meter status information, if running in the meter-status-changed
hook.""" hook."""
return os.environ.get('JUJU_METER_INFO') return os.environ.get('JUJU_METER_INFO')
def iter_units_for_relation_name(relation_name):
"""Iterate through all units in a relation
Generator that iterates through all the units in a relation and yields
a named tuple with rid and unit field names.
Usage:
data = [(u.rid, u.unit)
for u in iter_units_for_relation_name(relation_name)]
:param relation_name: string relation name
:yield: Named Tuple with rid and unit field names
"""
RelatedUnit = namedtuple('RelatedUnit', 'rid, unit')
for rid in relation_ids(relation_name):
for unit in related_units(rid):
yield RelatedUnit(rid, unit)
def ingress_address(rid=None, unit=None):
"""
Retrieve the ingress-address from a relation when available. Otherwise,
return the private-address. This function is to be used on the consuming
side of the relation.
Usage:
addresses = [ingress_address(rid=u.rid, unit=u.unit)
for u in iter_units_for_relation_name(relation_name)]
:param rid: string relation id
:param unit: string unit name
:side effect: calls relation_get
:return: string IP address
"""
settings = relation_get(rid=rid, unit=unit)
return (settings.get('ingress-address') or
settings.get('private-address'))

View File

@ -61,13 +61,19 @@ def bytes_from_string(value):
if isinstance(value, six.string_types): if isinstance(value, six.string_types):
value = six.text_type(value) value = six.text_type(value)
else: else:
msg = "Unable to interpret non-string value '%s' as boolean" % (value) msg = "Unable to interpret non-string value '%s' as bytes" % (value)
raise ValueError(msg) raise ValueError(msg)
matches = re.match("([0-9]+)([a-zA-Z]+)", value) matches = re.match("([0-9]+)([a-zA-Z]+)", value)
if not matches: if matches:
size = int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])
else:
# Assume that value passed in is bytes
try:
size = int(value)
except ValueError:
msg = "Unable to interpret string value '%s' as bytes" % (value) msg = "Unable to interpret string value '%s' as bytes" % (value)
raise ValueError(msg) raise ValueError(msg)
return int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)]) return size
class BasicStringComparator(object): class BasicStringComparator(object):