Make default func27-smoke xenial-pike
Charm-helpers sync Change-Id: I27dde93ccd2cfadcfe05ad841abba777d11d7318
This commit is contained in:
parent
5368af6302
commit
1c47c4ec9c
|
@ -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,7 +303,12 @@ class NRPE(object):
|
||||||
"command": nrpecheck.command,
|
"command": nrpecheck.command,
|
||||||
}
|
}
|
||||||
|
|
||||||
service('restart', 'nagios-nrpe-server')
|
# 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')
|
||||||
|
|
||||||
monitor_ids = relation_ids("local-monitors") + \
|
monitor_ids = relation_ids("local-monitors") + \
|
||||||
relation_ids("nrpe-external-master")
|
relation_ids("nrpe-external-master")
|
||||||
|
|
|
@ -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):
|
||||||
|
@ -303,19 +310,26 @@ 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_kilo:
|
if self._get_openstack_release() <= self.trusty_juno:
|
||||||
# Kilo or later
|
# Juno or earlier
|
||||||
|
pools = [
|
||||||
|
'data',
|
||||||
|
'metadata',
|
||||||
|
'rbd',
|
||||||
|
'cinder-ceph',
|
||||||
|
'glance'
|
||||||
|
]
|
||||||
|
elif (self.trust_kilo <= self._get_openstack_release() <=
|
||||||
|
self.zesty_ocata):
|
||||||
|
# Kilo through Ocata
|
||||||
pools = [
|
pools = [
|
||||||
'rbd',
|
'rbd',
|
||||||
'cinder-ceph',
|
'cinder-ceph',
|
||||||
'glance'
|
'glance'
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
# Juno or earlier
|
# Pike and later
|
||||||
pools = [
|
pools = [
|
||||||
'data',
|
|
||||||
'metadata',
|
|
||||||
'rbd',
|
|
||||||
'cinder-ceph',
|
'cinder-ceph',
|
||||||
'glance'
|
'glance'
|
||||||
]
|
]
|
||||||
|
|
|
@ -23,6 +23,7 @@ import urllib
|
||||||
import urlparse
|
import urlparse
|
||||||
|
|
||||||
import cinderclient.v1.client as cinder_client
|
import cinderclient.v1.client as cinder_client
|
||||||
|
import cinderclient.v2.client as cinder_clientv2
|
||||||
import glanceclient.v1.client as glance_client
|
import glanceclient.v1.client as glance_client
|
||||||
import heatclient.v1.client as heat_client
|
import heatclient.v1.client as heat_client
|
||||||
from keystoneclient.v2_0 import client as keystone_client
|
from keystoneclient.v2_0 import client as keystone_client
|
||||||
|
@ -42,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
|
||||||
|
@ -310,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
|
||||||
|
@ -326,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))
|
||||||
|
@ -348,15 +347,19 @@ 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,
|
||||||
password, tenant):
|
password, tenant, api_version=2):
|
||||||
"""Authenticates admin user with cinder."""
|
"""Authenticates admin user with cinder."""
|
||||||
# NOTE(beisner): cinder python client doesn't accept tokens.
|
# NOTE(beisner): cinder python client doesn't accept tokens.
|
||||||
keystone_ip = keystone_sentry.info['public-address']
|
keystone_ip = keystone_sentry.info['public-address']
|
||||||
ept = "http://{}:5000/v2.0".format(keystone_ip.strip().decode('utf-8'))
|
ept = "http://{}:5000/v2.0".format(keystone_ip.strip().decode('utf-8'))
|
||||||
return cinder_client.Client(username, password, tenant, ept)
|
_clients = {
|
||||||
|
1: cinder_client.Client,
|
||||||
|
2: cinder_clientv2.Client}
|
||||||
|
return _clients[api_version](username, password, tenant, ept)
|
||||||
|
|
||||||
def authenticate_keystone(self, keystone_ip, username, password,
|
def authenticate_keystone(self, keystone_ip, username, password,
|
||||||
api_version=False, admin_port=False,
|
api_version=False, admin_port=False,
|
||||||
|
@ -624,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
|
||||||
|
@ -674,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)
|
||||||
|
@ -688,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
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import collections
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
|
@ -578,11 +579,14 @@ class HAProxyContext(OSContextGenerator):
|
||||||
laddr = get_address_in_network(config(cfg_opt))
|
laddr = get_address_in_network(config(cfg_opt))
|
||||||
if laddr:
|
if laddr:
|
||||||
netmask = get_netmask_for_address(laddr)
|
netmask = get_netmask_for_address(laddr)
|
||||||
cluster_hosts[laddr] = {'network': "{}/{}".format(laddr,
|
cluster_hosts[laddr] = {
|
||||||
netmask),
|
'network': "{}/{}".format(laddr,
|
||||||
'backends': {l_unit: laddr}}
|
netmask),
|
||||||
|
'backends': collections.OrderedDict([(l_unit,
|
||||||
|
laddr)])
|
||||||
|
}
|
||||||
for rid in relation_ids('cluster'):
|
for rid in relation_ids('cluster'):
|
||||||
for unit in related_units(rid):
|
for unit in sorted(related_units(rid)):
|
||||||
_laddr = relation_get('{}-address'.format(addr_type),
|
_laddr = relation_get('{}-address'.format(addr_type),
|
||||||
rid=rid, unit=unit)
|
rid=rid, unit=unit)
|
||||||
if _laddr:
|
if _laddr:
|
||||||
|
@ -594,10 +598,13 @@ class HAProxyContext(OSContextGenerator):
|
||||||
# match in the frontend
|
# match in the frontend
|
||||||
cluster_hosts[addr] = {}
|
cluster_hosts[addr] = {}
|
||||||
netmask = get_netmask_for_address(addr)
|
netmask = get_netmask_for_address(addr)
|
||||||
cluster_hosts[addr] = {'network': "{}/{}".format(addr, netmask),
|
cluster_hosts[addr] = {
|
||||||
'backends': {l_unit: addr}}
|
'network': "{}/{}".format(addr, netmask),
|
||||||
|
'backends': collections.OrderedDict([(l_unit,
|
||||||
|
addr)])
|
||||||
|
}
|
||||||
for rid in relation_ids('cluster'):
|
for rid in relation_ids('cluster'):
|
||||||
for unit in related_units(rid):
|
for unit in sorted(related_units(rid)):
|
||||||
_laddr = relation_get('private-address',
|
_laddr = relation_get('private-address',
|
||||||
rid=rid, unit=unit)
|
rid=rid, unit=unit)
|
||||||
if _laddr:
|
if _laddr:
|
||||||
|
@ -846,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)
|
||||||
|
@ -999,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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -272,6 +272,8 @@ class OSConfigRenderer(object):
|
||||||
raise OSConfigException
|
raise OSConfigException
|
||||||
|
|
||||||
_out = self.render(config_file)
|
_out = self.render(config_file)
|
||||||
|
if six.PY3:
|
||||||
|
_out = _out.encode('UTF-8')
|
||||||
|
|
||||||
with open(config_file, 'wb') as out:
|
with open(config_file, 'wb') as out:
|
||||||
out.write(_out)
|
out.write(_out)
|
||||||
|
|
|
@ -618,7 +618,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))
|
||||||
|
@ -797,7 +797,7 @@ def git_default_repos(projects_yaml):
|
||||||
service = service_name()
|
service = service_name()
|
||||||
core_project = service
|
core_project = service
|
||||||
|
|
||||||
for default, branch in GIT_DEFAULT_BRANCHES.iteritems():
|
for default, branch in six.iteritems(GIT_DEFAULT_BRANCHES):
|
||||||
if projects_yaml == default:
|
if projects_yaml == default:
|
||||||
|
|
||||||
# add the requirements repo first
|
# add the requirements repo first
|
||||||
|
|
|
@ -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:
|
||||||
msg = "Unable to interpret string value '%s' as bytes" % (value)
|
size = int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])
|
||||||
raise ValueError(msg)
|
else:
|
||||||
return int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])
|
# Assume that value passed in is bytes
|
||||||
|
try:
|
||||||
|
size = int(value)
|
||||||
|
except ValueError:
|
||||||
|
msg = "Unable to interpret string value '%s' as bytes" % (value)
|
||||||
|
raise ValueError(msg)
|
||||||
|
return size
|
||||||
|
|
||||||
|
|
||||||
class BasicStringComparator(object):
|
class BasicStringComparator(object):
|
||||||
|
|
|
@ -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):
|
||||||
|
@ -303,19 +310,26 @@ 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_kilo:
|
if self._get_openstack_release() <= self.trusty_juno:
|
||||||
# Kilo or later
|
# Juno or earlier
|
||||||
|
pools = [
|
||||||
|
'data',
|
||||||
|
'metadata',
|
||||||
|
'rbd',
|
||||||
|
'cinder-ceph',
|
||||||
|
'glance'
|
||||||
|
]
|
||||||
|
elif (self.trust_kilo <= self._get_openstack_release() <=
|
||||||
|
self.zesty_ocata):
|
||||||
|
# Kilo through Ocata
|
||||||
pools = [
|
pools = [
|
||||||
'rbd',
|
'rbd',
|
||||||
'cinder-ceph',
|
'cinder-ceph',
|
||||||
'glance'
|
'glance'
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
# Juno or earlier
|
# Pike and later
|
||||||
pools = [
|
pools = [
|
||||||
'data',
|
|
||||||
'metadata',
|
|
||||||
'rbd',
|
|
||||||
'cinder-ceph',
|
'cinder-ceph',
|
||||||
'glance'
|
'glance'
|
||||||
]
|
]
|
||||||
|
|
|
@ -23,6 +23,7 @@ import urllib
|
||||||
import urlparse
|
import urlparse
|
||||||
|
|
||||||
import cinderclient.v1.client as cinder_client
|
import cinderclient.v1.client as cinder_client
|
||||||
|
import cinderclient.v2.client as cinder_clientv2
|
||||||
import glanceclient.v1.client as glance_client
|
import glanceclient.v1.client as glance_client
|
||||||
import heatclient.v1.client as heat_client
|
import heatclient.v1.client as heat_client
|
||||||
from keystoneclient.v2_0 import client as keystone_client
|
from keystoneclient.v2_0 import client as keystone_client
|
||||||
|
@ -42,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
|
||||||
|
@ -310,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
|
||||||
|
@ -326,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))
|
||||||
|
@ -348,15 +347,19 @@ 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,
|
||||||
password, tenant):
|
password, tenant, api_version=2):
|
||||||
"""Authenticates admin user with cinder."""
|
"""Authenticates admin user with cinder."""
|
||||||
# NOTE(beisner): cinder python client doesn't accept tokens.
|
# NOTE(beisner): cinder python client doesn't accept tokens.
|
||||||
keystone_ip = keystone_sentry.info['public-address']
|
keystone_ip = keystone_sentry.info['public-address']
|
||||||
ept = "http://{}:5000/v2.0".format(keystone_ip.strip().decode('utf-8'))
|
ept = "http://{}:5000/v2.0".format(keystone_ip.strip().decode('utf-8'))
|
||||||
return cinder_client.Client(username, password, tenant, ept)
|
_clients = {
|
||||||
|
1: cinder_client.Client,
|
||||||
|
2: cinder_clientv2.Client}
|
||||||
|
return _clients[api_version](username, password, tenant, ept)
|
||||||
|
|
||||||
def authenticate_keystone(self, keystone_ip, username, password,
|
def authenticate_keystone(self, keystone_ip, username, password,
|
||||||
api_version=False, admin_port=False,
|
api_version=False, admin_port=False,
|
||||||
|
@ -624,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
|
||||||
|
@ -674,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)
|
||||||
|
@ -688,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
|
||||||
|
|
|
@ -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
|
||||||
|
@ -644,18 +645,31 @@ def is_relation_made(relation, keys='private-address'):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _port_op(op_name, port, protocol="TCP"):
|
||||||
|
"""Open or close a service network port"""
|
||||||
|
_args = [op_name]
|
||||||
|
icmp = protocol.upper() == "ICMP"
|
||||||
|
if icmp:
|
||||||
|
_args.append(protocol)
|
||||||
|
else:
|
||||||
|
_args.append('{}/{}'.format(port, protocol))
|
||||||
|
try:
|
||||||
|
subprocess.check_call(_args)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
# Older Juju pre 2.3 doesn't support ICMP
|
||||||
|
# so treat it as a no-op if it fails.
|
||||||
|
if not icmp:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def open_port(port, protocol="TCP"):
|
def open_port(port, protocol="TCP"):
|
||||||
"""Open a service network port"""
|
"""Open a service network port"""
|
||||||
_args = ['open-port']
|
_port_op('open-port', port, protocol)
|
||||||
_args.append('{}/{}'.format(port, protocol))
|
|
||||||
subprocess.check_call(_args)
|
|
||||||
|
|
||||||
|
|
||||||
def close_port(port, protocol="TCP"):
|
def close_port(port, protocol="TCP"):
|
||||||
"""Close a service network port"""
|
"""Close a service network port"""
|
||||||
_args = ['close-port']
|
_port_op('close-port', port, protocol)
|
||||||
_args.append('{}/{}'.format(port, protocol))
|
|
||||||
subprocess.check_call(_args)
|
|
||||||
|
|
||||||
|
|
||||||
def open_ports(start, end, protocol="TCP"):
|
def open_ports(start, end, protocol="TCP"):
|
||||||
|
@ -1101,13 +1115,24 @@ def network_get(endpoint, relation_id=None):
|
||||||
:param endpoint: string. The name of a relation endpoint
|
:param endpoint: string. The name of a relation endpoint
|
||||||
:param relation_id: int. The ID of the relation for the current context.
|
:param relation_id: int. The ID of the relation for the current context.
|
||||||
:return: dict. The loaded YAML output of the network-get query.
|
:return: dict. The loaded YAML output of the network-get query.
|
||||||
:raise: NotImplementedError if run on Juju < 2.0
|
:raise: NotImplementedError if run on Juju < 2.1
|
||||||
"""
|
"""
|
||||||
cmd = ['network-get', endpoint, '--format', 'yaml']
|
cmd = ['network-get', endpoint, '--format', 'yaml']
|
||||||
if relation_id:
|
if relation_id:
|
||||||
cmd.append('-r')
|
cmd.append('-r')
|
||||||
cmd.append(relation_id)
|
cmd.append(relation_id)
|
||||||
response = subprocess.check_output(cmd).decode('UTF-8').strip()
|
try:
|
||||||
|
response = subprocess.check_output(
|
||||||
|
cmd,
|
||||||
|
stderr=subprocess.STDOUT).decode('UTF-8').strip()
|
||||||
|
except CalledProcessError as e:
|
||||||
|
# Early versions of Juju 2.0.x required the --primary-address argument.
|
||||||
|
# We catch that condition here and raise NotImplementedError since
|
||||||
|
# the requested semantics are not available - the caller can then
|
||||||
|
# use the network_get_primary_address() method instead.
|
||||||
|
if '--primary-address is currently required' in e.output.decode('UTF-8'):
|
||||||
|
raise NotImplementedError
|
||||||
|
raise
|
||||||
return yaml.safe_load(response)
|
return yaml.safe_load(response)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1140,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'))
|
||||||
|
|
|
@ -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:
|
||||||
msg = "Unable to interpret string value '%s' as bytes" % (value)
|
size = int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])
|
||||||
raise ValueError(msg)
|
else:
|
||||||
return int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])
|
# Assume that value passed in is bytes
|
||||||
|
try:
|
||||||
|
size = int(value)
|
||||||
|
except ValueError:
|
||||||
|
msg = "Unable to interpret string value '%s' as bytes" % (value)
|
||||||
|
raise ValueError(msg)
|
||||||
|
return size
|
||||||
|
|
||||||
|
|
||||||
class BasicStringComparator(object):
|
class BasicStringComparator(object):
|
||||||
|
|
2
tox.ini
2
tox.ini
|
@ -60,7 +60,7 @@ basepython = python2.7
|
||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
commands =
|
commands =
|
||||||
bundletester -vl DEBUG -r json -o func-results.json gate-basic-xenial-mitaka --no-destroy
|
bundletester -vl DEBUG -r json -o func-results.json gate-basic-xenial-pike --no-destroy
|
||||||
|
|
||||||
[testenv:func27-dfs]
|
[testenv:func27-dfs]
|
||||||
# Charm Functional Test
|
# Charm Functional Test
|
||||||
|
|
Loading…
Reference in New Issue