Merge "Do not run client relation until clustered if HA"
This commit is contained in:
commit
ea3ea47d0a
@ -426,7 +426,7 @@ def ns_query(address):
|
||||
|
||||
try:
|
||||
answers = dns.resolver.query(address, rtype)
|
||||
except dns.resolver.NXDOMAIN as e:
|
||||
except dns.resolver.NXDOMAIN:
|
||||
return None
|
||||
|
||||
if answers:
|
||||
|
@ -20,6 +20,7 @@ import re
|
||||
import six
|
||||
import time
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
import cinderclient.v1.client as cinder_client
|
||||
import glanceclient.v1.client as glance_client
|
||||
@ -37,6 +38,7 @@ import swiftclient
|
||||
from charmhelpers.contrib.amulet.utils import (
|
||||
AmuletUtils
|
||||
)
|
||||
from charmhelpers.core.decorators import retry_on_exception
|
||||
|
||||
DEBUG = logging.DEBUG
|
||||
ERROR = logging.ERROR
|
||||
@ -303,6 +305,46 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
self.log.debug('Checking if tenant exists ({})...'.format(tenant))
|
||||
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,
|
||||
api_version):
|
||||
"""Iterate over list of sentry and relation tuples and verify that
|
||||
api_version has the expected value.
|
||||
|
||||
:param sentry_relation_pairs: list of sentry, relation name tuples used
|
||||
for monitoring propagation of relation
|
||||
data
|
||||
:param api_version: api_version to expect in relation data
|
||||
:returns: None if successful. Raise on error.
|
||||
"""
|
||||
for (sentry, relation_name) in sentry_relation_pairs:
|
||||
rel = sentry.relation('identity-service',
|
||||
relation_name)
|
||||
self.log.debug('keystone relation data: {}'.format(rel))
|
||||
if rel['api_version'] != str(api_version):
|
||||
raise Exception("api_version not propagated through relation"
|
||||
" data yet ('{}' != '{}')."
|
||||
"".format(rel['api_version'], api_version))
|
||||
|
||||
def keystone_configure_api_version(self, sentry_relation_pairs, deployment,
|
||||
api_version):
|
||||
"""Configure preferred-api-version of keystone in deployment and
|
||||
monitor provided list of relation objects for propagation
|
||||
before returning to caller.
|
||||
|
||||
:param sentry_relation_pairs: list of sentry, relation tuples used for
|
||||
monitoring propagation of relation data
|
||||
:param deployment: deployment to configure
|
||||
:param api_version: value preferred-api-version will be set to
|
||||
:returns: None if successful. Raise on error.
|
||||
"""
|
||||
self.log.debug("Setting keystone preferred-api-version: '{}'"
|
||||
"".format(api_version))
|
||||
|
||||
config = {'preferred-api-version': api_version}
|
||||
deployment.d.configure('keystone', config)
|
||||
self.keystone_wait_for_propagation(sentry_relation_pairs, api_version)
|
||||
|
||||
def authenticate_cinder_admin(self, keystone_sentry, username,
|
||||
password, tenant):
|
||||
"""Authenticates admin user with cinder."""
|
||||
@ -311,6 +353,37 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
ept = "http://{}:5000/v2.0".format(keystone_ip.strip().decode('utf-8'))
|
||||
return cinder_client.Client(username, password, tenant, ept)
|
||||
|
||||
def authenticate_keystone(self, keystone_ip, username, password,
|
||||
api_version=False, admin_port=False,
|
||||
user_domain_name=None, domain_name=None,
|
||||
project_domain_name=None, project_name=None):
|
||||
"""Authenticate with Keystone"""
|
||||
self.log.debug('Authenticating with keystone...')
|
||||
port = 5000
|
||||
if admin_port:
|
||||
port = 35357
|
||||
base_ep = "http://{}:{}".format(keystone_ip.strip().decode('utf-8'),
|
||||
port)
|
||||
if not api_version or api_version == 2:
|
||||
ep = base_ep + "/v2.0"
|
||||
return keystone_client.Client(username=username, password=password,
|
||||
tenant_name=project_name,
|
||||
auth_url=ep)
|
||||
else:
|
||||
ep = base_ep + "/v3"
|
||||
auth = keystone_id_v3.Password(
|
||||
user_domain_name=user_domain_name,
|
||||
username=username,
|
||||
password=password,
|
||||
domain_name=domain_name,
|
||||
project_domain_name=project_domain_name,
|
||||
project_name=project_name,
|
||||
auth_url=ep
|
||||
)
|
||||
return keystone_client_v3.Client(
|
||||
session=keystone_session.Session(auth=auth)
|
||||
)
|
||||
|
||||
def authenticate_keystone_admin(self, keystone_sentry, user, password,
|
||||
tenant=None, api_version=None,
|
||||
keystone_ip=None):
|
||||
@ -319,30 +392,28 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
if not keystone_ip:
|
||||
keystone_ip = keystone_sentry.info['public-address']
|
||||
|
||||
base_ep = "http://{}:35357".format(keystone_ip.strip().decode('utf-8'))
|
||||
if not api_version or api_version == 2:
|
||||
ep = base_ep + "/v2.0"
|
||||
return keystone_client.Client(username=user, password=password,
|
||||
tenant_name=tenant, auth_url=ep)
|
||||
else:
|
||||
ep = base_ep + "/v3"
|
||||
auth = keystone_id_v3.Password(
|
||||
user_domain_name='admin_domain',
|
||||
username=user,
|
||||
password=password,
|
||||
domain_name='admin_domain',
|
||||
auth_url=ep,
|
||||
)
|
||||
sess = keystone_session.Session(auth=auth)
|
||||
return keystone_client_v3.Client(session=sess)
|
||||
user_domain_name = None
|
||||
domain_name = None
|
||||
if api_version == 3:
|
||||
user_domain_name = 'admin_domain'
|
||||
domain_name = user_domain_name
|
||||
|
||||
return self.authenticate_keystone(keystone_ip, user, password,
|
||||
project_name=tenant,
|
||||
api_version=api_version,
|
||||
user_domain_name=user_domain_name,
|
||||
domain_name=domain_name,
|
||||
admin_port=True)
|
||||
|
||||
def authenticate_keystone_user(self, keystone, user, password, tenant):
|
||||
"""Authenticates a regular user with the keystone public endpoint."""
|
||||
self.log.debug('Authenticating keystone user ({})...'.format(user))
|
||||
ep = keystone.service_catalog.url_for(service_type='identity',
|
||||
endpoint_type='publicURL')
|
||||
return keystone_client.Client(username=user, password=password,
|
||||
tenant_name=tenant, auth_url=ep)
|
||||
keystone_ip = urlparse.urlparse(ep).hostname
|
||||
|
||||
return self.authenticate_keystone(keystone_ip, user, password,
|
||||
project_name=tenant)
|
||||
|
||||
def authenticate_glance_admin(self, keystone):
|
||||
"""Authenticates admin user with glance."""
|
||||
|
@ -126,3 +126,14 @@ def assert_charm_supports_dns_ha():
|
||||
status_set('blocked', msg)
|
||||
raise DNSHAException(msg)
|
||||
return True
|
||||
|
||||
|
||||
def expect_ha():
|
||||
""" Determine if the unit expects to be in HA
|
||||
|
||||
Check for VIP or dns-ha settings which indicate the unit should expect to
|
||||
be related to hacluster.
|
||||
|
||||
@returns boolean
|
||||
"""
|
||||
return config('vip') or config('dns-ha')
|
||||
|
@ -40,6 +40,7 @@ from subprocess import (
|
||||
)
|
||||
from charmhelpers.core.hookenv import (
|
||||
config,
|
||||
service_name,
|
||||
local_unit,
|
||||
relation_get,
|
||||
relation_ids,
|
||||
@ -1043,8 +1044,18 @@ class CephBrokerRq(object):
|
||||
self.request_id = str(uuid.uuid1())
|
||||
self.ops = []
|
||||
|
||||
def add_op_request_access_to_group(self, name, namespace=None,
|
||||
permission=None, key_name=None):
|
||||
"""
|
||||
Adds the requested permissions to the current service's Ceph key,
|
||||
allowing the key to access only the specified pools
|
||||
"""
|
||||
self.ops.append({'op': 'add-permissions-to-key', 'group': name,
|
||||
'namespace': namespace, 'name': key_name or service_name(),
|
||||
'group-permission': permission})
|
||||
|
||||
def add_op_create_pool(self, name, replica_count=3, pg_num=None,
|
||||
weight=None):
|
||||
weight=None, group=None, namespace=None):
|
||||
"""Adds an operation to create a pool.
|
||||
|
||||
@param pg_num setting: optional setting. If not provided, this value
|
||||
@ -1058,7 +1069,8 @@ class CephBrokerRq(object):
|
||||
|
||||
self.ops.append({'op': 'create-pool', 'name': name,
|
||||
'replicas': replica_count, 'pg_num': pg_num,
|
||||
'weight': weight})
|
||||
'weight': weight, 'group': group,
|
||||
'group-namespace': namespace})
|
||||
|
||||
def set_ops(self, ops):
|
||||
"""Set request ops to provided value.
|
||||
|
@ -1035,3 +1035,34 @@ def network_get_primary_address(binding):
|
||||
'''
|
||||
cmd = ['network-get', '--primary-address', binding]
|
||||
return subprocess.check_output(cmd).decode('UTF-8').strip()
|
||||
|
||||
|
||||
def add_metric(*args, **kwargs):
|
||||
"""Add metric values. Values may be expressed with keyword arguments. For
|
||||
metric names containing dashes, these may be expressed as one or more
|
||||
'key=value' positional arguments. May only be called from the collect-metrics
|
||||
hook."""
|
||||
_args = ['add-metric']
|
||||
_kvpairs = []
|
||||
_kvpairs.extend(args)
|
||||
_kvpairs.extend(['{}={}'.format(k, v) for k, v in kwargs.items()])
|
||||
_args.extend(sorted(_kvpairs))
|
||||
try:
|
||||
subprocess.check_call(_args)
|
||||
return
|
||||
except EnvironmentError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
log_message = 'add-metric failed: {}'.format(' '.join(_kvpairs))
|
||||
log(log_message, level='INFO')
|
||||
|
||||
|
||||
def meter_status():
|
||||
"""Get the meter status, if running in the meter-status-changed hook."""
|
||||
return os.environ.get('JUJU_METER_STATUS')
|
||||
|
||||
|
||||
def meter_info():
|
||||
"""Get the meter status information, if running in the meter-status-changed
|
||||
hook."""
|
||||
return os.environ.get('JUJU_METER_INFO')
|
||||
|
@ -54,38 +54,138 @@ elif __platform__ == "centos":
|
||||
cmp_pkgrevno,
|
||||
) # flake8: noqa -- ignore F401 for this import
|
||||
|
||||
UPDATEDB_PATH = '/etc/updatedb.conf'
|
||||
|
||||
def service_start(service_name):
|
||||
"""Start a system service"""
|
||||
return service('start', service_name)
|
||||
def service_start(service_name, **kwargs):
|
||||
"""Start a system service.
|
||||
|
||||
The specified service name is managed via the system level init system.
|
||||
Some init systems (e.g. upstart) require that additional arguments be
|
||||
provided in order to directly control service instances whereas other init
|
||||
systems allow for addressing instances of a service directly by name (e.g.
|
||||
systemd).
|
||||
|
||||
The kwargs allow for the additional parameters to be passed to underlying
|
||||
init systems for those systems which require/allow for them. For example,
|
||||
the ceph-osd upstart script requires the id parameter to be passed along
|
||||
in order to identify which running daemon should be reloaded. The follow-
|
||||
ing example stops the ceph-osd service for instance id=4:
|
||||
|
||||
service_stop('ceph-osd', id=4)
|
||||
|
||||
:param service_name: the name of the service to stop
|
||||
:param **kwargs: additional parameters to pass to the init system when
|
||||
managing services. These will be passed as key=value
|
||||
parameters to the init system's commandline. kwargs
|
||||
are ignored for systemd enabled systems.
|
||||
"""
|
||||
return service('start', service_name, **kwargs)
|
||||
|
||||
|
||||
def service_stop(service_name):
|
||||
"""Stop a system service"""
|
||||
return service('stop', service_name)
|
||||
def service_stop(service_name, **kwargs):
|
||||
"""Stop a system service.
|
||||
|
||||
The specified service name is managed via the system level init system.
|
||||
Some init systems (e.g. upstart) require that additional arguments be
|
||||
provided in order to directly control service instances whereas other init
|
||||
systems allow for addressing instances of a service directly by name (e.g.
|
||||
systemd).
|
||||
|
||||
The kwargs allow for the additional parameters to be passed to underlying
|
||||
init systems for those systems which require/allow for them. For example,
|
||||
the ceph-osd upstart script requires the id parameter to be passed along
|
||||
in order to identify which running daemon should be reloaded. The follow-
|
||||
ing example stops the ceph-osd service for instance id=4:
|
||||
|
||||
service_stop('ceph-osd', id=4)
|
||||
|
||||
:param service_name: the name of the service to stop
|
||||
:param **kwargs: additional parameters to pass to the init system when
|
||||
managing services. These will be passed as key=value
|
||||
parameters to the init system's commandline. kwargs
|
||||
are ignored for systemd enabled systems.
|
||||
"""
|
||||
return service('stop', service_name, **kwargs)
|
||||
|
||||
|
||||
def service_restart(service_name):
|
||||
"""Restart a system service"""
|
||||
def service_restart(service_name, **kwargs):
|
||||
"""Restart a system service.
|
||||
|
||||
The specified service name is managed via the system level init system.
|
||||
Some init systems (e.g. upstart) require that additional arguments be
|
||||
provided in order to directly control service instances whereas other init
|
||||
systems allow for addressing instances of a service directly by name (e.g.
|
||||
systemd).
|
||||
|
||||
The kwargs allow for the additional parameters to be passed to underlying
|
||||
init systems for those systems which require/allow for them. For example,
|
||||
the ceph-osd upstart script requires the id parameter to be passed along
|
||||
in order to identify which running daemon should be restarted. The follow-
|
||||
ing example restarts the ceph-osd service for instance id=4:
|
||||
|
||||
service_restart('ceph-osd', id=4)
|
||||
|
||||
:param service_name: the name of the service to restart
|
||||
:param **kwargs: additional parameters to pass to the init system when
|
||||
managing services. These will be passed as key=value
|
||||
parameters to the init system's commandline. kwargs
|
||||
are ignored for init systems not allowing additional
|
||||
parameters via the commandline (systemd).
|
||||
"""
|
||||
return service('restart', service_name)
|
||||
|
||||
|
||||
def service_reload(service_name, restart_on_failure=False):
|
||||
def service_reload(service_name, restart_on_failure=False, **kwargs):
|
||||
"""Reload a system service, optionally falling back to restart if
|
||||
reload fails"""
|
||||
service_result = service('reload', service_name)
|
||||
reload fails.
|
||||
|
||||
The specified service name is managed via the system level init system.
|
||||
Some init systems (e.g. upstart) require that additional arguments be
|
||||
provided in order to directly control service instances whereas other init
|
||||
systems allow for addressing instances of a service directly by name (e.g.
|
||||
systemd).
|
||||
|
||||
The kwargs allow for the additional parameters to be passed to underlying
|
||||
init systems for those systems which require/allow for them. For example,
|
||||
the ceph-osd upstart script requires the id parameter to be passed along
|
||||
in order to identify which running daemon should be reloaded. The follow-
|
||||
ing example restarts the ceph-osd service for instance id=4:
|
||||
|
||||
service_reload('ceph-osd', id=4)
|
||||
|
||||
:param service_name: the name of the service to reload
|
||||
:param restart_on_failure: boolean indicating whether to fallback to a
|
||||
restart if the reload fails.
|
||||
:param **kwargs: additional parameters to pass to the init system when
|
||||
managing services. These will be passed as key=value
|
||||
parameters to the init system's commandline. kwargs
|
||||
are ignored for init systems not allowing additional
|
||||
parameters via the commandline (systemd).
|
||||
"""
|
||||
service_result = service('reload', service_name, **kwargs)
|
||||
if not service_result and restart_on_failure:
|
||||
service_result = service('restart', service_name)
|
||||
service_result = service('restart', service_name, **kwargs)
|
||||
return service_result
|
||||
|
||||
|
||||
def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d"):
|
||||
def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d",
|
||||
**kwargs):
|
||||
"""Pause a system service.
|
||||
|
||||
Stop it, and prevent it from starting again at boot."""
|
||||
Stop it, and prevent it from starting again at boot.
|
||||
|
||||
:param service_name: the name of the service to pause
|
||||
:param init_dir: path to the upstart init directory
|
||||
:param initd_dir: path to the sysv init directory
|
||||
:param **kwargs: additional parameters to pass to the init system when
|
||||
managing services. These will be passed as key=value
|
||||
parameters to the init system's commandline. kwargs
|
||||
are ignored for init systems which do not support
|
||||
key=value arguments via the commandline.
|
||||
"""
|
||||
stopped = True
|
||||
if service_running(service_name):
|
||||
stopped = service_stop(service_name)
|
||||
if service_running(service_name, **kwargs):
|
||||
stopped = service_stop(service_name, **kwargs)
|
||||
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
|
||||
sysv_file = os.path.join(initd_dir, service_name)
|
||||
if init_is_systemd():
|
||||
@ -106,10 +206,19 @@ def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d"):
|
||||
|
||||
|
||||
def service_resume(service_name, init_dir="/etc/init",
|
||||
initd_dir="/etc/init.d"):
|
||||
initd_dir="/etc/init.d", **kwargs):
|
||||
"""Resume a system service.
|
||||
|
||||
Reenable starting again at boot. Start the service"""
|
||||
Reenable starting again at boot. Start the service.
|
||||
|
||||
:param service_name: the name of the service to resume
|
||||
:param init_dir: the path to the init dir
|
||||
:param initd dir: the path to the initd dir
|
||||
:param **kwargs: additional parameters to pass to the init system when
|
||||
managing services. These will be passed as key=value
|
||||
parameters to the init system's commandline. kwargs
|
||||
are ignored for systemd enabled systems.
|
||||
"""
|
||||
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
|
||||
sysv_file = os.path.join(initd_dir, service_name)
|
||||
if init_is_systemd():
|
||||
@ -126,19 +235,28 @@ def service_resume(service_name, init_dir="/etc/init",
|
||||
"Unable to detect {0} as SystemD, Upstart {1} or"
|
||||
" SysV {2}".format(
|
||||
service_name, upstart_file, sysv_file))
|
||||
started = service_running(service_name, **kwargs)
|
||||
|
||||
started = service_running(service_name)
|
||||
if not started:
|
||||
started = service_start(service_name)
|
||||
started = service_start(service_name, **kwargs)
|
||||
return started
|
||||
|
||||
|
||||
def service(action, service_name):
|
||||
"""Control a system service"""
|
||||
def service(action, service_name, **kwargs):
|
||||
"""Control a system service.
|
||||
|
||||
:param action: the action to take on the service
|
||||
:param service_name: the name of the service to perform th action on
|
||||
:param **kwargs: additional params to be passed to the service command in
|
||||
the form of key=value.
|
||||
"""
|
||||
if init_is_systemd():
|
||||
cmd = ['systemctl', action, service_name]
|
||||
else:
|
||||
cmd = ['service', service_name, action]
|
||||
for key, value in six.iteritems(kwargs):
|
||||
parameter = '%s=%s' % (key, value)
|
||||
cmd.append(parameter)
|
||||
return subprocess.call(cmd) == 0
|
||||
|
||||
|
||||
@ -146,15 +264,26 @@ _UPSTART_CONF = "/etc/init/{}.conf"
|
||||
_INIT_D_CONF = "/etc/init.d/{}"
|
||||
|
||||
|
||||
def service_running(service_name):
|
||||
"""Determine whether a system service is running"""
|
||||
def service_running(service_name, **kwargs):
|
||||
"""Determine whether a system service is running.
|
||||
|
||||
:param service_name: the name of the service
|
||||
:param **kwargs: additional args to pass to the service command. This is
|
||||
used to pass additional key=value arguments to the
|
||||
service command line for managing specific instance
|
||||
units (e.g. service ceph-osd status id=2). The kwargs
|
||||
are ignored in systemd services.
|
||||
"""
|
||||
if init_is_systemd():
|
||||
return service('is-active', service_name)
|
||||
else:
|
||||
if os.path.exists(_UPSTART_CONF.format(service_name)):
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
['status', service_name],
|
||||
cmd = ['status', service_name]
|
||||
for key, value in six.iteritems(kwargs):
|
||||
parameter = '%s=%s' % (key, value)
|
||||
cmd.append(parameter)
|
||||
output = subprocess.check_output(cmd,
|
||||
stderr=subprocess.STDOUT).decode('UTF-8')
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
@ -720,6 +849,20 @@ def lchownr(path, owner, group):
|
||||
chownr(path, owner, group, follow_links=False)
|
||||
|
||||
|
||||
def owner(path):
|
||||
"""Returns a tuple containing the username & groupname owning the path.
|
||||
|
||||
:param str path: the string path to retrieve the ownership
|
||||
:return tuple(str, str): A (username, groupname) tuple containing the
|
||||
name of the user and group owning the path.
|
||||
:raises OSError: if the specified path does not exist
|
||||
"""
|
||||
stat = os.stat(path)
|
||||
username = pwd.getpwuid(stat.st_uid)[0]
|
||||
groupname = grp.getgrgid(stat.st_gid)[0]
|
||||
return username, groupname
|
||||
|
||||
|
||||
def get_total_ram():
|
||||
"""The total amount of system RAM in bytes.
|
||||
|
||||
@ -751,3 +894,25 @@ def is_container():
|
||||
else:
|
||||
# Detect using upstart container file marker
|
||||
return os.path.exists(UPSTART_CONTAINER_TYPE)
|
||||
|
||||
|
||||
def add_to_updatedb_prunepath(path, updatedb_path=UPDATEDB_PATH):
|
||||
with open(updatedb_path, 'r+') as f_id:
|
||||
updatedb_text = f_id.read()
|
||||
output = updatedb(updatedb_text, path)
|
||||
f_id.seek(0)
|
||||
f_id.write(output)
|
||||
f_id.truncate()
|
||||
|
||||
|
||||
def updatedb(updatedb_text, new_path):
|
||||
lines = [line for line in updatedb_text.split("\n")]
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("PRUNEPATHS="):
|
||||
paths_line = line.split("=")[1].replace('"', '')
|
||||
paths = paths_line.split(" ")
|
||||
if new_path not in paths:
|
||||
paths.append(new_path)
|
||||
lines[i] = 'PRUNEPATHS="{}"'.format(' '.join(paths))
|
||||
output = "\n".join(lines)
|
||||
return output
|
||||
|
@ -8,12 +8,18 @@ def get_platform():
|
||||
will be returned (which is the name of the module).
|
||||
This string is used to decide which platform module should be imported.
|
||||
"""
|
||||
# linux_distribution is deprecated and will be removed in Python 3.7
|
||||
# Warings *not* disabled, as we certainly need to fix this.
|
||||
tuple_platform = platform.linux_distribution()
|
||||
current_platform = tuple_platform[0]
|
||||
if "Ubuntu" in current_platform:
|
||||
return "ubuntu"
|
||||
elif "CentOS" in current_platform:
|
||||
return "centos"
|
||||
elif "debian" in current_platform:
|
||||
# Stock Python does not detect Ubuntu and instead returns debian.
|
||||
# Or at least it does in some build environments like Travis CI
|
||||
return "ubuntu"
|
||||
else:
|
||||
raise RuntimeError("This module is not supported on {}."
|
||||
.format(current_platform))
|
||||
|
@ -127,10 +127,12 @@ from charmhelpers.contrib.hahelpers.cluster import (
|
||||
get_hacluster_config,
|
||||
peer_units,
|
||||
https,
|
||||
is_clustered,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.openstack.ha.utils import (
|
||||
update_dns_ha_resource_params,
|
||||
expect_ha,
|
||||
)
|
||||
|
||||
from charmhelpers.payload.execd import execd_preinstall
|
||||
@ -431,6 +433,10 @@ def identity_changed(relation_id=None, remote_unit=None):
|
||||
"updates", level=INFO)
|
||||
return
|
||||
|
||||
if expect_ha() and not is_clustered():
|
||||
log("Expected to be HA but no hacluster relation yet", level=INFO)
|
||||
return
|
||||
|
||||
add_service_to_keystone(relation_id, remote_unit)
|
||||
if is_service_present('neutron', 'network'):
|
||||
delete_service_entry('quantum', 'network')
|
||||
@ -474,6 +480,9 @@ def identity_credentials_changed(relation_id=None, remote_unit=None):
|
||||
:param remote_unit: Related unit on the relation
|
||||
"""
|
||||
if is_elected_leader(CLUSTER_RES):
|
||||
if expect_ha() and not is_clustered():
|
||||
log("Expected to be HA but no hacluster relation yet", level=INFO)
|
||||
return
|
||||
if not is_db_ready():
|
||||
log("identity-credentials-relation-changed hook fired before db "
|
||||
"ready - deferring until db ready", level=WARNING)
|
||||
@ -701,6 +710,9 @@ def ha_changed():
|
||||
@hooks.hook('identity-admin-relation-changed')
|
||||
def admin_relation_changed(relation_id=None):
|
||||
# TODO: fixup
|
||||
if expect_ha() and not is_clustered():
|
||||
log("Expected to be HA but no hacluster relation yet", level=INFO)
|
||||
return
|
||||
relation_data = {
|
||||
'service_hostname': resolve_address(ADMIN),
|
||||
'service_port': config('service-port'),
|
||||
|
@ -20,6 +20,7 @@ import re
|
||||
import six
|
||||
import time
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
import cinderclient.v1.client as cinder_client
|
||||
import glanceclient.v1.client as glance_client
|
||||
@ -37,6 +38,7 @@ import swiftclient
|
||||
from charmhelpers.contrib.amulet.utils import (
|
||||
AmuletUtils
|
||||
)
|
||||
from charmhelpers.core.decorators import retry_on_exception
|
||||
|
||||
DEBUG = logging.DEBUG
|
||||
ERROR = logging.ERROR
|
||||
@ -303,6 +305,46 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
self.log.debug('Checking if tenant exists ({})...'.format(tenant))
|
||||
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,
|
||||
api_version):
|
||||
"""Iterate over list of sentry and relation tuples and verify that
|
||||
api_version has the expected value.
|
||||
|
||||
:param sentry_relation_pairs: list of sentry, relation name tuples used
|
||||
for monitoring propagation of relation
|
||||
data
|
||||
:param api_version: api_version to expect in relation data
|
||||
:returns: None if successful. Raise on error.
|
||||
"""
|
||||
for (sentry, relation_name) in sentry_relation_pairs:
|
||||
rel = sentry.relation('identity-service',
|
||||
relation_name)
|
||||
self.log.debug('keystone relation data: {}'.format(rel))
|
||||
if rel['api_version'] != str(api_version):
|
||||
raise Exception("api_version not propagated through relation"
|
||||
" data yet ('{}' != '{}')."
|
||||
"".format(rel['api_version'], api_version))
|
||||
|
||||
def keystone_configure_api_version(self, sentry_relation_pairs, deployment,
|
||||
api_version):
|
||||
"""Configure preferred-api-version of keystone in deployment and
|
||||
monitor provided list of relation objects for propagation
|
||||
before returning to caller.
|
||||
|
||||
:param sentry_relation_pairs: list of sentry, relation tuples used for
|
||||
monitoring propagation of relation data
|
||||
:param deployment: deployment to configure
|
||||
:param api_version: value preferred-api-version will be set to
|
||||
:returns: None if successful. Raise on error.
|
||||
"""
|
||||
self.log.debug("Setting keystone preferred-api-version: '{}'"
|
||||
"".format(api_version))
|
||||
|
||||
config = {'preferred-api-version': api_version}
|
||||
deployment.d.configure('keystone', config)
|
||||
self.keystone_wait_for_propagation(sentry_relation_pairs, api_version)
|
||||
|
||||
def authenticate_cinder_admin(self, keystone_sentry, username,
|
||||
password, tenant):
|
||||
"""Authenticates admin user with cinder."""
|
||||
@ -311,6 +353,37 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
ept = "http://{}:5000/v2.0".format(keystone_ip.strip().decode('utf-8'))
|
||||
return cinder_client.Client(username, password, tenant, ept)
|
||||
|
||||
def authenticate_keystone(self, keystone_ip, username, password,
|
||||
api_version=False, admin_port=False,
|
||||
user_domain_name=None, domain_name=None,
|
||||
project_domain_name=None, project_name=None):
|
||||
"""Authenticate with Keystone"""
|
||||
self.log.debug('Authenticating with keystone...')
|
||||
port = 5000
|
||||
if admin_port:
|
||||
port = 35357
|
||||
base_ep = "http://{}:{}".format(keystone_ip.strip().decode('utf-8'),
|
||||
port)
|
||||
if not api_version or api_version == 2:
|
||||
ep = base_ep + "/v2.0"
|
||||
return keystone_client.Client(username=username, password=password,
|
||||
tenant_name=project_name,
|
||||
auth_url=ep)
|
||||
else:
|
||||
ep = base_ep + "/v3"
|
||||
auth = keystone_id_v3.Password(
|
||||
user_domain_name=user_domain_name,
|
||||
username=username,
|
||||
password=password,
|
||||
domain_name=domain_name,
|
||||
project_domain_name=project_domain_name,
|
||||
project_name=project_name,
|
||||
auth_url=ep
|
||||
)
|
||||
return keystone_client_v3.Client(
|
||||
session=keystone_session.Session(auth=auth)
|
||||
)
|
||||
|
||||
def authenticate_keystone_admin(self, keystone_sentry, user, password,
|
||||
tenant=None, api_version=None,
|
||||
keystone_ip=None):
|
||||
@ -319,30 +392,28 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
if not keystone_ip:
|
||||
keystone_ip = keystone_sentry.info['public-address']
|
||||
|
||||
base_ep = "http://{}:35357".format(keystone_ip.strip().decode('utf-8'))
|
||||
if not api_version or api_version == 2:
|
||||
ep = base_ep + "/v2.0"
|
||||
return keystone_client.Client(username=user, password=password,
|
||||
tenant_name=tenant, auth_url=ep)
|
||||
else:
|
||||
ep = base_ep + "/v3"
|
||||
auth = keystone_id_v3.Password(
|
||||
user_domain_name='admin_domain',
|
||||
username=user,
|
||||
password=password,
|
||||
domain_name='admin_domain',
|
||||
auth_url=ep,
|
||||
)
|
||||
sess = keystone_session.Session(auth=auth)
|
||||
return keystone_client_v3.Client(session=sess)
|
||||
user_domain_name = None
|
||||
domain_name = None
|
||||
if api_version == 3:
|
||||
user_domain_name = 'admin_domain'
|
||||
domain_name = user_domain_name
|
||||
|
||||
return self.authenticate_keystone(keystone_ip, user, password,
|
||||
project_name=tenant,
|
||||
api_version=api_version,
|
||||
user_domain_name=user_domain_name,
|
||||
domain_name=domain_name,
|
||||
admin_port=True)
|
||||
|
||||
def authenticate_keystone_user(self, keystone, user, password, tenant):
|
||||
"""Authenticates a regular user with the keystone public endpoint."""
|
||||
self.log.debug('Authenticating keystone user ({})...'.format(user))
|
||||
ep = keystone.service_catalog.url_for(service_type='identity',
|
||||
endpoint_type='publicURL')
|
||||
return keystone_client.Client(username=user, password=password,
|
||||
tenant_name=tenant, auth_url=ep)
|
||||
keystone_ip = urlparse.urlparse(ep).hostname
|
||||
|
||||
return self.authenticate_keystone(keystone_ip, user, password,
|
||||
project_name=tenant)
|
||||
|
||||
def authenticate_glance_admin(self, keystone):
|
||||
"""Authenticates admin user with glance."""
|
||||
|
@ -1035,3 +1035,34 @@ def network_get_primary_address(binding):
|
||||
'''
|
||||
cmd = ['network-get', '--primary-address', binding]
|
||||
return subprocess.check_output(cmd).decode('UTF-8').strip()
|
||||
|
||||
|
||||
def add_metric(*args, **kwargs):
|
||||
"""Add metric values. Values may be expressed with keyword arguments. For
|
||||
metric names containing dashes, these may be expressed as one or more
|
||||
'key=value' positional arguments. May only be called from the collect-metrics
|
||||
hook."""
|
||||
_args = ['add-metric']
|
||||
_kvpairs = []
|
||||
_kvpairs.extend(args)
|
||||
_kvpairs.extend(['{}={}'.format(k, v) for k, v in kwargs.items()])
|
||||
_args.extend(sorted(_kvpairs))
|
||||
try:
|
||||
subprocess.check_call(_args)
|
||||
return
|
||||
except EnvironmentError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
log_message = 'add-metric failed: {}'.format(' '.join(_kvpairs))
|
||||
log(log_message, level='INFO')
|
||||
|
||||
|
||||
def meter_status():
|
||||
"""Get the meter status, if running in the meter-status-changed hook."""
|
||||
return os.environ.get('JUJU_METER_STATUS')
|
||||
|
||||
|
||||
def meter_info():
|
||||
"""Get the meter status information, if running in the meter-status-changed
|
||||
hook."""
|
||||
return os.environ.get('JUJU_METER_INFO')
|
||||
|
@ -75,9 +75,11 @@ TO_PATCH = [
|
||||
'resolve_address',
|
||||
# charmhelpers.contrib.openstack.ha.utils
|
||||
'update_dns_ha_resource_params',
|
||||
'expect_ha',
|
||||
# charmhelpers.contrib.hahelpers.cluster_utils
|
||||
'is_elected_leader',
|
||||
'get_hacluster_config',
|
||||
'is_clustered',
|
||||
# keystone_utils
|
||||
'restart_map',
|
||||
'register_configs',
|
||||
@ -647,6 +649,7 @@ class KeystoneRelationTests(CharmTestCase):
|
||||
def test_identity_changed_leader(self, mock_send_notifications,
|
||||
mock_hashlib, mock_ensure_ssl_cert_master,
|
||||
mock_log, mock_is_db_initialised):
|
||||
self.expect_ha.return_value = False
|
||||
mock_is_db_initialised.return_value = True
|
||||
self.is_db_ready.return_value = True
|
||||
self.is_service_present.return_value = True
|
||||
@ -671,6 +674,7 @@ class KeystoneRelationTests(CharmTestCase):
|
||||
mock_ensure_ssl_cert_master,
|
||||
mock_log,
|
||||
mock_is_db_initialised):
|
||||
self.expect_ha.return_value = False
|
||||
mock_is_db_initialised.return_value = True
|
||||
self.is_db_ready.return_value = True
|
||||
self.is_service_present.return_value = False
|
||||
|
Loading…
Reference in New Issue
Block a user