Resync charm-helpers for support to change passwords
Change-Id: I1337096eb32250285e4a4a5d2b1975f91993816a Partial-Bug: 1436093
This commit is contained in:
parent
5a03ac57e7
commit
62ed38b7b2
|
@ -57,6 +57,19 @@ except ImportError:
|
|||
import MySQLdb
|
||||
|
||||
|
||||
# NOTE(freyes): Due to skip-name-resolve root@$HOSTNAME account fails when
|
||||
# using SET PASSWORD so using UPDATE against the mysql.user table is needed,
|
||||
# but changes to this table are not replicated across the cluster, so this
|
||||
# update needs to run in all the nodes.
|
||||
# More info at http://galeracluster.com/documentation-webpages/userchanges.html
|
||||
SQL_UPDATE_PASSWD = ("UPDATE mysql.user SET password = PASSWORD( %s ) "
|
||||
"WHERE user = %s;")
|
||||
|
||||
|
||||
class MySQLSetPasswordError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MySQLHelper(object):
|
||||
|
||||
def __init__(self, rpasswdf_template, upasswdf_template, host='localhost',
|
||||
|
@ -70,6 +83,7 @@ class MySQLHelper(object):
|
|||
self.migrate_passwd_to_leader_storage = migrate_passwd_to_leader_storage
|
||||
# If we migrate we have the option to delete local copy of root passwd
|
||||
self.delete_ondisk_passwd_file = delete_ondisk_passwd_file
|
||||
self.connection = None
|
||||
|
||||
def connect(self, user='root', password=None):
|
||||
log("Opening db connection for %s@%s" % (user, self.host), level=DEBUG)
|
||||
|
@ -268,6 +282,67 @@ class MySQLHelper(object):
|
|||
"""Retrieve or generate mysql root password for service units."""
|
||||
return self.get_mysql_password(username=None, password=password)
|
||||
|
||||
def set_mysql_password(self, username, password):
|
||||
"""Update a mysql password for the provided username changing the
|
||||
leader settings
|
||||
|
||||
To update root's password pass `None` in the username
|
||||
"""
|
||||
|
||||
if username is None:
|
||||
username = 'root'
|
||||
|
||||
# get root password via leader-get, it may be that in the past (when
|
||||
# changes to root-password were not supported) the user changed the
|
||||
# password, so leader-get is more reliable source than
|
||||
# config.previous('root-password').
|
||||
rel_username = None if username == 'root' else username
|
||||
cur_passwd = self.get_mysql_password(rel_username)
|
||||
|
||||
# password that needs to be set
|
||||
new_passwd = password
|
||||
|
||||
# update password for all users (e.g. root@localhost, root@::1, etc)
|
||||
try:
|
||||
self.connect(user=username, password=cur_passwd)
|
||||
cursor = self.connection.cursor()
|
||||
except MySQLdb.OperationalError as ex:
|
||||
raise MySQLSetPasswordError(('Cannot connect using password in '
|
||||
'leader settings (%s)') % ex, ex)
|
||||
|
||||
try:
|
||||
cursor.execute(SQL_UPDATE_PASSWD, (new_passwd, username))
|
||||
cursor.execute('FLUSH PRIVILEGES;')
|
||||
self.connection.commit()
|
||||
except MySQLdb.OperationalError as ex:
|
||||
raise MySQLSetPasswordError('Cannot update password: %s' % str(ex),
|
||||
ex)
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
# check the password was changed
|
||||
try:
|
||||
self.connect(user=username, password=new_passwd)
|
||||
self.execute('select 1;')
|
||||
except MySQLdb.OperationalError as ex:
|
||||
raise MySQLSetPasswordError(('Cannot connect using new password: '
|
||||
'%s') % str(ex), ex)
|
||||
|
||||
if not is_leader():
|
||||
log('Only the leader can set a new password in the relation',
|
||||
level=DEBUG)
|
||||
return
|
||||
|
||||
for key in self.passwd_keys(rel_username):
|
||||
_password = leader_get(key)
|
||||
if _password:
|
||||
log('Updating password for %s (%s)' % (key, rel_username),
|
||||
level=DEBUG)
|
||||
leader_set(settings={key: new_passwd})
|
||||
|
||||
def set_mysql_root_password(self, password):
|
||||
self.set_mysql_password('root', password)
|
||||
|
||||
def normalize_address(self, hostname):
|
||||
"""Ensure that address returned is an IP address (i.e. not fqdn)"""
|
||||
if config_get('prefer-ipv6'):
|
||||
|
|
|
@ -243,11 +243,13 @@ def is_ipv6_disabled():
|
|||
try:
|
||||
result = subprocess.check_output(
|
||||
['sysctl', 'net.ipv6.conf.all.disable_ipv6'],
|
||||
stderr=subprocess.STDOUT)
|
||||
return "net.ipv6.conf.all.disable_ipv6 = 1" in result
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True)
|
||||
except subprocess.CalledProcessError:
|
||||
return True
|
||||
|
||||
return "net.ipv6.conf.all.disable_ipv6 = 1" in result
|
||||
|
||||
|
||||
def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False,
|
||||
fatal=True, exc_list=None):
|
||||
|
|
|
@ -51,6 +51,7 @@ from charmhelpers.core.hookenv import (
|
|||
status_set,
|
||||
hook_name,
|
||||
application_version_set,
|
||||
cached,
|
||||
)
|
||||
|
||||
from charmhelpers.core.strutils import BasicStringComparator
|
||||
|
@ -90,6 +91,13 @@ from charmhelpers.fetch import (
|
|||
GPGKeyError,
|
||||
get_upstream_version
|
||||
)
|
||||
|
||||
from charmhelpers.fetch.snap import (
|
||||
snap_install,
|
||||
snap_refresh,
|
||||
SNAP_CHANNELS,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
|
||||
from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device
|
||||
from charmhelpers.contrib.openstack.exceptions import OSContextError
|
||||
|
@ -327,8 +335,10 @@ def get_os_codename_install_source(src):
|
|||
return ca_rel
|
||||
|
||||
# Best guess match based on deb string provided
|
||||
if src.startswith('deb') or src.startswith('ppa'):
|
||||
for k, v in six.iteritems(OPENSTACK_CODENAMES):
|
||||
if (src.startswith('deb') or
|
||||
src.startswith('ppa') or
|
||||
src.startswith('snap')):
|
||||
for v in OPENSTACK_CODENAMES.values():
|
||||
if v in src:
|
||||
return v
|
||||
|
||||
|
@ -397,6 +407,19 @@ def get_swift_codename(version):
|
|||
|
||||
def get_os_codename_package(package, fatal=True):
|
||||
'''Derive OpenStack release codename from an installed package.'''
|
||||
|
||||
if snap_install_requested():
|
||||
cmd = ['snap', 'list', package]
|
||||
try:
|
||||
out = subprocess.check_output(cmd)
|
||||
except subprocess.CalledProcessError as e:
|
||||
return None
|
||||
lines = out.split('\n')
|
||||
for line in lines:
|
||||
if package in line:
|
||||
# Second item in list is Version
|
||||
return line.split()[1]
|
||||
|
||||
import apt_pkg as apt
|
||||
|
||||
cache = apt_cache()
|
||||
|
@ -613,6 +636,9 @@ def openstack_upgrade_available(package):
|
|||
import apt_pkg as apt
|
||||
src = config('openstack-origin')
|
||||
cur_vers = get_os_version_package(package)
|
||||
if not cur_vers:
|
||||
# The package has not been installed yet do not attempt upgrade
|
||||
return False
|
||||
if "swift" in package:
|
||||
codename = get_os_codename_install_source(src)
|
||||
avail_vers = get_os_version_codename_swift(codename)
|
||||
|
@ -1863,6 +1889,30 @@ def pausable_restart_on_change(restart_map, stopstart=False,
|
|||
return wrap
|
||||
|
||||
|
||||
def ordered(orderme):
|
||||
"""Converts the provided dictionary into a collections.OrderedDict.
|
||||
|
||||
The items in the returned OrderedDict will be inserted based on the
|
||||
natural sort order of the keys. Nested dictionaries will also be sorted
|
||||
in order to ensure fully predictable ordering.
|
||||
|
||||
:param orderme: the dict to order
|
||||
:return: collections.OrderedDict
|
||||
:raises: ValueError: if `orderme` isn't a dict instance.
|
||||
"""
|
||||
if not isinstance(orderme, dict):
|
||||
raise ValueError('argument must be a dict type')
|
||||
|
||||
result = OrderedDict()
|
||||
for k, v in sorted(six.iteritems(orderme), key=lambda x: x[0]):
|
||||
if isinstance(v, dict):
|
||||
result[k] = ordered(v)
|
||||
else:
|
||||
result[k] = v
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def config_flags_parser(config_flags):
|
||||
"""Parses config flags string into dict.
|
||||
|
||||
|
@ -1874,15 +1924,13 @@ def config_flags_parser(config_flags):
|
|||
example, a string in the format of 'key1=value1, key2=value2' will
|
||||
return a dict of:
|
||||
|
||||
{'key1': 'value1',
|
||||
'key2': 'value2'}.
|
||||
{'key1': 'value1', 'key2': 'value2'}.
|
||||
|
||||
2. A string in the above format, but supporting a comma-delimited list
|
||||
of values for the same key. For example, a string in the format of
|
||||
'key1=value1, key2=value3,value4,value5' will return a dict of:
|
||||
|
||||
{'key1', 'value1',
|
||||
'key2', 'value2,value3,value4'}
|
||||
{'key1': 'value1', 'key2': 'value2,value3,value4'}
|
||||
|
||||
3. A string containing a colon character (:) prior to an equal
|
||||
character (=) will be treated as yaml and parsed as such. This can be
|
||||
|
@ -1902,7 +1950,7 @@ def config_flags_parser(config_flags):
|
|||
equals = config_flags.find('=')
|
||||
if colon > 0:
|
||||
if colon < equals or equals < 0:
|
||||
return yaml.safe_load(config_flags)
|
||||
return ordered(yaml.safe_load(config_flags))
|
||||
|
||||
if config_flags.find('==') >= 0:
|
||||
juju_log("config_flags is not in expected format (key=value)",
|
||||
|
@ -1915,7 +1963,7 @@ def config_flags_parser(config_flags):
|
|||
# split on '='.
|
||||
split = config_flags.strip(' =').split('=')
|
||||
limit = len(split)
|
||||
flags = {}
|
||||
flags = OrderedDict()
|
||||
for i in range(0, limit - 1):
|
||||
current = split[i]
|
||||
next = split[i + 1]
|
||||
|
@ -1982,3 +2030,84 @@ def token_cache_pkgs(source=None, release=None):
|
|||
if enable_memcache(source=source, release=release):
|
||||
packages.extend(['memcached', 'python-memcache'])
|
||||
return packages
|
||||
|
||||
|
||||
def update_json_file(filename, items):
|
||||
"""Updates the json `filename` with a given dict.
|
||||
:param filename: json filename (i.e.: /etc/glance/policy.json)
|
||||
:param items: dict of items to update
|
||||
"""
|
||||
with open(filename) as fd:
|
||||
policy = json.load(fd)
|
||||
policy.update(items)
|
||||
with open(filename, "w") as fd:
|
||||
fd.write(json.dumps(policy, indent=4))
|
||||
|
||||
|
||||
@cached
|
||||
def snap_install_requested():
|
||||
""" Determine if installing from snaps
|
||||
|
||||
If openstack-origin is of the form snap:channel-series-release
|
||||
and channel is in SNAPS_CHANNELS return True.
|
||||
"""
|
||||
origin = config('openstack-origin')
|
||||
if not origin.startswith('snap:'):
|
||||
return False
|
||||
|
||||
_src = origin[5:]
|
||||
channel, series, release = _src.split('-')
|
||||
if channel.lower() in SNAP_CHANNELS:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_snaps_install_info_from_origin(snaps, src, mode='classic'):
|
||||
"""Generate a dictionary of snap install information from origin
|
||||
|
||||
@param snaps: List of snaps
|
||||
@param src: String of openstack-origin or source of the form
|
||||
snap:channel-series-track
|
||||
@param mode: String classic, devmode or jailmode
|
||||
@returns: Dictionary of snaps with channels and modes
|
||||
"""
|
||||
|
||||
if not src.startswith('snap:'):
|
||||
juju_log("Snap source is not a snap origin", 'WARN')
|
||||
return {}
|
||||
|
||||
_src = src[5:]
|
||||
_channel, _series, _release = _src.split('-')
|
||||
channel = '--channel={}/{}'.format(_release, _channel)
|
||||
|
||||
return {snap: {'channel': channel, 'mode': mode}
|
||||
for snap in snaps}
|
||||
|
||||
|
||||
def install_os_snaps(snaps, refresh=False):
|
||||
"""Install OpenStack snaps from channel and with mode
|
||||
|
||||
@param snaps: Dictionary of snaps with channels and modes of the form:
|
||||
{'snap_name': {'channel': 'snap_channel',
|
||||
'mode': 'snap_mode'}}
|
||||
Where channel a snapstore channel and mode is --classic, --devmode or
|
||||
--jailmode.
|
||||
@param post_snap_install: Callback function to run after snaps have been
|
||||
installed
|
||||
"""
|
||||
|
||||
def _ensure_flag(flag):
|
||||
if flag.startswith('--'):
|
||||
return flag
|
||||
return '--{}'.format(flag)
|
||||
|
||||
if refresh:
|
||||
for snap in snaps.keys():
|
||||
snap_refresh(snap,
|
||||
_ensure_flag(snaps[snap]['channel']),
|
||||
_ensure_flag(snaps[snap]['mode']))
|
||||
else:
|
||||
for snap in snaps.keys():
|
||||
snap_install(snap,
|
||||
_ensure_flag(snaps[snap]['channel']),
|
||||
_ensure_flag(snaps[snap]['mode']))
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# Copyright 2017 Canonical Limited.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import os
|
||||
import json
|
||||
|
||||
from charmhelpers.core.hookenv import log
|
||||
|
||||
stats_intervals = ['stats_day', 'stats_five_minute',
|
||||
'stats_hour', 'stats_total']
|
||||
|
||||
SYSFS = '/sys'
|
||||
|
||||
|
||||
class Bcache(object):
|
||||
"""Bcache behaviour
|
||||
"""
|
||||
|
||||
def __init__(self, cachepath):
|
||||
self.cachepath = cachepath
|
||||
|
||||
@classmethod
|
||||
def fromdevice(cls, devname):
|
||||
return cls('{}/block/{}/bcache'.format(SYSFS, devname))
|
||||
|
||||
def __str__(self):
|
||||
return self.cachepath
|
||||
|
||||
def get_stats(self, interval):
|
||||
"""Get cache stats
|
||||
"""
|
||||
intervaldir = 'stats_{}'.format(interval)
|
||||
path = "{}/{}".format(self.cachepath, intervaldir)
|
||||
out = dict()
|
||||
for elem in os.listdir(path):
|
||||
out[elem] = open('{}/{}'.format(path, elem)).read().strip()
|
||||
return out
|
||||
|
||||
|
||||
def get_bcache_fs():
|
||||
"""Return all cache sets
|
||||
"""
|
||||
cachesetroot = "{}/fs/bcache".format(SYSFS)
|
||||
try:
|
||||
dirs = os.listdir(cachesetroot)
|
||||
except OSError:
|
||||
log("No bcache fs found")
|
||||
return []
|
||||
cacheset = set([Bcache('{}/{}'.format(cachesetroot, d)) for d in dirs if not d.startswith('register')])
|
||||
return cacheset
|
||||
|
||||
|
||||
def get_stats_action(cachespec, interval):
|
||||
"""Action for getting bcache statistics for a given cachespec.
|
||||
Cachespec can either be a device name, eg. 'sdb', which will retrieve
|
||||
cache stats for the given device, or 'global', which will retrieve stats
|
||||
for all cachesets
|
||||
"""
|
||||
if cachespec == 'global':
|
||||
caches = get_bcache_fs()
|
||||
else:
|
||||
caches = [Bcache.fromdevice(cachespec)]
|
||||
res = dict((c.cachepath, c.get_stats(interval)) for c in caches)
|
||||
return json.dumps(res, indent=4, separators=(',', ': '))
|
|
@ -63,6 +63,7 @@ from charmhelpers.core.host import (
|
|||
from charmhelpers.fetch import (
|
||||
apt_install,
|
||||
)
|
||||
from charmhelpers.core.unitdata import kv
|
||||
|
||||
from charmhelpers.core.kernel import modprobe
|
||||
from charmhelpers.contrib.openstack.utils import config_flags_parser
|
||||
|
@ -1314,6 +1315,47 @@ def send_request_if_needed(request, relation='ceph'):
|
|||
relation_set(relation_id=rid, broker_req=request.request)
|
||||
|
||||
|
||||
def is_broker_action_done(action, rid=None, unit=None):
|
||||
"""Check whether broker action has completed yet.
|
||||
|
||||
@param action: name of action to be performed
|
||||
@returns True if action complete otherwise False
|
||||
"""
|
||||
rdata = relation_get(rid, unit) or {}
|
||||
broker_rsp = rdata.get(get_broker_rsp_key())
|
||||
if not broker_rsp:
|
||||
return False
|
||||
|
||||
rsp = CephBrokerRsp(broker_rsp)
|
||||
unit_name = local_unit().partition('/')[2]
|
||||
key = "unit_{}_ceph_broker_action.{}".format(unit_name, action)
|
||||
kvstore = kv()
|
||||
val = kvstore.get(key=key)
|
||||
if val and val == rsp.request_id:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def mark_broker_action_done(action, rid=None, unit=None):
|
||||
"""Mark action as having been completed.
|
||||
|
||||
@param action: name of action to be performed
|
||||
@returns None
|
||||
"""
|
||||
rdata = relation_get(rid, unit) or {}
|
||||
broker_rsp = rdata.get(get_broker_rsp_key())
|
||||
if not broker_rsp:
|
||||
return
|
||||
|
||||
rsp = CephBrokerRsp(broker_rsp)
|
||||
unit_name = local_unit().partition('/')[2]
|
||||
key = "unit_{}_ceph_broker_action.{}".format(unit_name, action)
|
||||
kvstore = kv()
|
||||
kvstore.set(key=key, value=rsp.request_id)
|
||||
kvstore.flush()
|
||||
|
||||
|
||||
class CephConfContext(object):
|
||||
"""Ceph config (ceph.conf) context.
|
||||
|
||||
|
@ -1330,7 +1372,7 @@ class CephConfContext(object):
|
|||
return {}
|
||||
|
||||
conf = config_flags_parser(conf)
|
||||
if type(conf) != dict:
|
||||
if not isinstance(conf, dict):
|
||||
log("Provided config-flags is not a dictionary - ignoring",
|
||||
level=WARNING)
|
||||
return {}
|
||||
|
|
|
@ -18,15 +18,23 @@ If writing reactive charms, use the snap layer:
|
|||
https://lists.ubuntu.com/archives/snapcraft/2016-September/001114.html
|
||||
"""
|
||||
import subprocess
|
||||
from os import environ
|
||||
import os
|
||||
from time import sleep
|
||||
from charmhelpers.core.hookenv import log
|
||||
|
||||
__author__ = 'Joseph Borg <joseph.borg@canonical.com>'
|
||||
|
||||
SNAP_NO_LOCK = 1 # The return code for "couldn't acquire lock" in Snap (hopefully this will be improved).
|
||||
# The return code for "couldn't acquire lock" in Snap
|
||||
# (hopefully this will be improved).
|
||||
SNAP_NO_LOCK = 1
|
||||
SNAP_NO_LOCK_RETRY_DELAY = 10 # Wait X seconds between Snap lock checks.
|
||||
SNAP_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times.
|
||||
SNAP_CHANNELS = [
|
||||
'edge',
|
||||
'beta',
|
||||
'candidate',
|
||||
'stable',
|
||||
]
|
||||
|
||||
|
||||
class CouldNotAcquireLockException(Exception):
|
||||
|
@ -47,13 +55,17 @@ def _snap_exec(commands):
|
|||
|
||||
while return_code is None or return_code == SNAP_NO_LOCK:
|
||||
try:
|
||||
return_code = subprocess.check_call(['snap'] + commands, env=environ)
|
||||
return_code = subprocess.check_call(['snap'] + commands,
|
||||
env=os.environ)
|
||||
except subprocess.CalledProcessError as e:
|
||||
retry_count += + 1
|
||||
if retry_count > SNAP_NO_LOCK_RETRY_COUNT:
|
||||
raise CouldNotAcquireLockException('Could not aquire lock after %s attempts' % SNAP_NO_LOCK_RETRY_COUNT)
|
||||
raise CouldNotAcquireLockException(
|
||||
'Could not aquire lock after {} attempts'
|
||||
.format(SNAP_NO_LOCK_RETRY_COUNT))
|
||||
return_code = e.returncode
|
||||
log('Snap failed to acquire lock, trying again in %s seconds.' % SNAP_NO_LOCK_RETRY_DELAY, level='WARN')
|
||||
log('Snap failed to acquire lock, trying again in {} seconds.'
|
||||
.format(SNAP_NO_LOCK_RETRY_DELAY, level='WARN'))
|
||||
sleep(SNAP_NO_LOCK_RETRY_DELAY)
|
||||
|
||||
return return_code
|
||||
|
|
|
@ -139,7 +139,7 @@ CLOUD_ARCHIVE_POCKETS = {
|
|||
'xenial-updates/ocata': 'xenial-updates/ocata',
|
||||
'ocata/proposed': 'xenial-proposed/ocata',
|
||||
'xenial-ocata/proposed': 'xenial-proposed/ocata',
|
||||
'xenial-ocata/newton': 'xenial-proposed/ocata',
|
||||
'xenial-proposed/ocata': 'xenial-proposed/ocata',
|
||||
# Pike
|
||||
'pike': 'xenial-updates/pike',
|
||||
'xenial-pike': 'xenial-updates/pike',
|
||||
|
@ -147,7 +147,7 @@ CLOUD_ARCHIVE_POCKETS = {
|
|||
'xenial-updates/pike': 'xenial-updates/pike',
|
||||
'pike/proposed': 'xenial-proposed/pike',
|
||||
'xenial-pike/proposed': 'xenial-proposed/pike',
|
||||
'xenial-pike/newton': 'xenial-proposed/pike',
|
||||
'xenial-proposed/pike': 'xenial-proposed/pike',
|
||||
# Queens
|
||||
'queens': 'xenial-updates/queens',
|
||||
'xenial-queens': 'xenial-updates/queens',
|
||||
|
@ -155,13 +155,13 @@ CLOUD_ARCHIVE_POCKETS = {
|
|||
'xenial-updates/queens': 'xenial-updates/queens',
|
||||
'queens/proposed': 'xenial-proposed/queens',
|
||||
'xenial-queens/proposed': 'xenial-proposed/queens',
|
||||
'xenial-queens/newton': 'xenial-proposed/queens',
|
||||
'xenial-proposed/queens': 'xenial-proposed/queens',
|
||||
}
|
||||
|
||||
|
||||
APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT.
|
||||
CMD_RETRY_DELAY = 10 # Wait 10 seconds between command retries.
|
||||
CMD_RETRY_COUNT = 30 # Retry a failing fatal command X times.
|
||||
CMD_RETRY_COUNT = 3 # Retry a failing fatal command X times.
|
||||
|
||||
|
||||
def filter_installed_packages(packages):
|
||||
|
@ -364,6 +364,7 @@ def add_source(source, key=None, fail_invalid=False):
|
|||
(r"^cloud:(.*)-(.*)\/staging$", _add_cloud_staging),
|
||||
(r"^cloud:(.*)-(.*)$", _add_cloud_distro_check),
|
||||
(r"^cloud:(.*)$", _add_cloud_pocket),
|
||||
(r"^snap:.*-(.*)-(.*)$", _add_cloud_distro_check),
|
||||
])
|
||||
if source is None:
|
||||
source = ''
|
||||
|
|
|
@ -25,9 +25,12 @@ import urlparse
|
|||
import cinderclient.v1.client as cinder_client
|
||||
import glanceclient.v1.client as glance_client
|
||||
import heatclient.v1.client as heat_client
|
||||
import keystoneclient.v2_0 as keystone_client
|
||||
from keystoneclient.auth.identity import v3 as keystone_id_v3
|
||||
from keystoneclient import session as keystone_session
|
||||
from keystoneclient.v2_0 import client as keystone_client
|
||||
from keystoneauth1.identity import (
|
||||
v3,
|
||||
v2,
|
||||
)
|
||||
from keystoneauth1 import session as keystone_session
|
||||
from keystoneclient.v3 import client as keystone_client_v3
|
||||
from novaclient import exceptions
|
||||
|
||||
|
@ -368,12 +371,20 @@ class OpenStackAmuletUtils(AmuletUtils):
|
|||
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)
|
||||
auth = v2.Password(
|
||||
username=username,
|
||||
password=password,
|
||||
tenant_name=project_name,
|
||||
auth_url=ep
|
||||
)
|
||||
sess = keystone_session.Session(auth=auth)
|
||||
client = keystone_client.Client(session=sess)
|
||||
# This populates the client.service_catalog
|
||||
client.auth_ref = auth.get_access(sess)
|
||||
return client
|
||||
else:
|
||||
ep = base_ep + "/v3"
|
||||
auth = keystone_id_v3.Password(
|
||||
auth = v3.Password(
|
||||
user_domain_name=user_domain_name,
|
||||
username=username,
|
||||
password=password,
|
||||
|
@ -382,36 +393,45 @@ class OpenStackAmuletUtils(AmuletUtils):
|
|||
project_name=project_name,
|
||||
auth_url=ep
|
||||
)
|
||||
return keystone_client_v3.Client(
|
||||
session=keystone_session.Session(auth=auth)
|
||||
)
|
||||
sess = keystone_session.Session(auth=auth)
|
||||
client = keystone_client_v3.Client(session=sess)
|
||||
# This populates the client.service_catalog
|
||||
client.auth_ref = auth.get_access(sess)
|
||||
return client
|
||||
|
||||
def authenticate_keystone_admin(self, keystone_sentry, user, password,
|
||||
tenant=None, api_version=None,
|
||||
keystone_ip=None):
|
||||
keystone_ip=None, user_domain_name=None,
|
||||
project_domain_name=None,
|
||||
project_name=None):
|
||||
"""Authenticates admin user with the keystone admin endpoint."""
|
||||
self.log.debug('Authenticating keystone admin...')
|
||||
if not keystone_ip:
|
||||
keystone_ip = keystone_sentry.info['public-address']
|
||||
|
||||
user_domain_name = None
|
||||
domain_name = None
|
||||
if api_version == 3:
|
||||
# To support backward compatibility usage of this function
|
||||
if not project_name:
|
||||
project_name = tenant
|
||||
if api_version == 3 and not user_domain_name:
|
||||
user_domain_name = 'admin_domain'
|
||||
domain_name = user_domain_name
|
||||
if api_version == 3 and not project_domain_name:
|
||||
project_domain_name = 'admin_domain'
|
||||
if api_version == 3 and not project_name:
|
||||
project_name = 'admin'
|
||||
|
||||
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)
|
||||
return self.authenticate_keystone(
|
||||
keystone_ip, user, password,
|
||||
api_version=api_version,
|
||||
user_domain_name=user_domain_name,
|
||||
project_domain_name=project_domain_name,
|
||||
project_name=project_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')
|
||||
interface='publicURL')
|
||||
keystone_ip = urlparse.urlparse(ep).hostname
|
||||
|
||||
return self.authenticate_keystone(keystone_ip, user, password,
|
||||
|
@ -421,22 +441,32 @@ class OpenStackAmuletUtils(AmuletUtils):
|
|||
"""Authenticates admin user with glance."""
|
||||
self.log.debug('Authenticating glance admin...')
|
||||
ep = keystone.service_catalog.url_for(service_type='image',
|
||||
endpoint_type='adminURL')
|
||||
return glance_client.Client(ep, token=keystone.auth_token)
|
||||
interface='adminURL')
|
||||
if keystone.session:
|
||||
return glance_client.Client(ep, session=keystone.session)
|
||||
else:
|
||||
return glance_client.Client(ep, token=keystone.auth_token)
|
||||
|
||||
def authenticate_heat_admin(self, keystone):
|
||||
"""Authenticates the admin user with heat."""
|
||||
self.log.debug('Authenticating heat admin...')
|
||||
ep = keystone.service_catalog.url_for(service_type='orchestration',
|
||||
endpoint_type='publicURL')
|
||||
return heat_client.Client(endpoint=ep, token=keystone.auth_token)
|
||||
interface='publicURL')
|
||||
if keystone.session:
|
||||
return heat_client.Client(endpoint=ep, session=keystone.session)
|
||||
else:
|
||||
return heat_client.Client(endpoint=ep, token=keystone.auth_token)
|
||||
|
||||
def authenticate_nova_user(self, keystone, user, password, tenant):
|
||||
"""Authenticates a regular user with nova-api."""
|
||||
self.log.debug('Authenticating nova user ({})...'.format(user))
|
||||
ep = keystone.service_catalog.url_for(service_type='identity',
|
||||
endpoint_type='publicURL')
|
||||
if novaclient.__version__[0] >= "7":
|
||||
interface='publicURL')
|
||||
if keystone.session:
|
||||
return nova_client.Client(NOVA_CLIENT_VERSION,
|
||||
session=keystone.session,
|
||||
auth_url=ep)
|
||||
elif novaclient.__version__[0] >= "7":
|
||||
return nova_client.Client(NOVA_CLIENT_VERSION,
|
||||
username=user, password=password,
|
||||
project_name=tenant, auth_url=ep)
|
||||
|
@ -449,12 +479,15 @@ class OpenStackAmuletUtils(AmuletUtils):
|
|||
"""Authenticates a regular user with swift api."""
|
||||
self.log.debug('Authenticating swift user ({})...'.format(user))
|
||||
ep = keystone.service_catalog.url_for(service_type='identity',
|
||||
endpoint_type='publicURL')
|
||||
return swiftclient.Connection(authurl=ep,
|
||||
user=user,
|
||||
key=password,
|
||||
tenant_name=tenant,
|
||||
auth_version='2.0')
|
||||
interface='publicURL')
|
||||
if keystone.session:
|
||||
return swiftclient.Connection(session=keystone.session)
|
||||
else:
|
||||
return swiftclient.Connection(authurl=ep,
|
||||
user=user,
|
||||
key=password,
|
||||
tenant_name=tenant,
|
||||
auth_version='2.0')
|
||||
|
||||
def create_flavor(self, nova, name, ram, vcpus, disk, flavorid="auto",
|
||||
ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True):
|
||||
|
|
Loading…
Reference in New Issue