2858f1b02d
Switch to using systemd configurations to manage radosgw instances; the radosgw init script is obsolete and will be removed at some point in time, and the newer style of managing radosgw daemons is inline with current best-practice. This changeset also changes the way cephx keys are issues; before all rgw instances shared a key, now a key is issued per host. The key is named 'rgw.`hostname`' to identify the application and host using the key. Existing deployments using the radosgw init script will be switched to use the new systemd named units; this occurs once the new key for the unit has been presented by the ceph-mon cluster over the mon relation. A small period of outage will occur as the radosgw init based daemon is stopped and disabled prior to the start of the new systemd based radosgw unit. This commit also includes a resync for charmhelpers to pickup support for '@' in NRPE service check names. Change-Id: Ic0d634e619185931633712cb3e3685051a28749d Depends-On: I289b75a2935184817b424c5eceead16235c3f53b Closes-Bug: 1808140
225 lines
7.3 KiB
Python
225 lines
7.3 KiB
Python
# Copyright 2016 Canonical Ltd
|
|
#
|
|
# 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 re
|
|
import socket
|
|
import tempfile
|
|
import shutil
|
|
|
|
from charmhelpers.contrib.openstack import context
|
|
from charmhelpers.contrib.hahelpers.cluster import (
|
|
determine_api_port,
|
|
determine_apache_port,
|
|
)
|
|
from charmhelpers.core.host import cmp_pkgrevno
|
|
from charmhelpers.core.hookenv import (
|
|
DEBUG,
|
|
WARNING,
|
|
config,
|
|
log,
|
|
related_units,
|
|
relation_get,
|
|
relation_ids,
|
|
unit_public_ip,
|
|
)
|
|
from charmhelpers.contrib.network.ip import (
|
|
format_ipv6_addr,
|
|
get_ipv6_addr,
|
|
)
|
|
from charmhelpers.contrib.storage.linux.ceph import CephConfContext
|
|
|
|
|
|
class ApacheSSLContext(context.ApacheSSLContext):
|
|
interfaces = ['https']
|
|
service_namespace = 'ceph-radosgw'
|
|
|
|
def __call__(self):
|
|
self.external_ports = [config('port')]
|
|
return super(ApacheSSLContext, self).__call__()
|
|
|
|
|
|
class HAProxyContext(context.HAProxyContext):
|
|
|
|
def __call__(self):
|
|
ctxt = super(HAProxyContext, self).__call__()
|
|
port = config('port')
|
|
|
|
# Apache ports
|
|
a_cephradosgw_api = determine_apache_port(port, singlenode_mode=True)
|
|
|
|
port_mapping = {
|
|
'cephradosgw-server': [port, a_cephradosgw_api]
|
|
}
|
|
|
|
ctxt['cephradosgw_bind_port'] = determine_api_port(
|
|
port,
|
|
singlenode_mode=True,
|
|
)
|
|
|
|
# for haproxy.conf
|
|
ctxt['service_ports'] = port_mapping
|
|
return ctxt
|
|
|
|
|
|
class IdentityServiceContext(context.IdentityServiceContext):
|
|
interfaces = ['identity-service']
|
|
|
|
def __call__(self):
|
|
ctxt = super(IdentityServiceContext, self).__call__()
|
|
if not ctxt:
|
|
return
|
|
|
|
ctxt['admin_token'] = None
|
|
for relid in relation_ids('identity-service'):
|
|
for unit in related_units(relid):
|
|
if not ctxt.get('admin_token'):
|
|
ctxt['admin_token'] = \
|
|
relation_get('admin_token', unit, relid)
|
|
|
|
if cmp_pkgrevno('radosgw', "10.2.0") >= 0:
|
|
ctxt['auth_keystone_v3_supported'] = True
|
|
|
|
if (not ctxt.get('admin_domain_id') and
|
|
float(ctxt.get('api_version', '2.0')) < 3):
|
|
ctxt.pop('admin_domain_id')
|
|
|
|
ctxt['auth_type'] = 'keystone'
|
|
ctxt['user_roles'] = config('operator-roles')
|
|
ctxt['cache_size'] = config('cache-size')
|
|
ctxt['revocation_check_interval'] = config('revocation-check-interval')
|
|
if self.context_complete(ctxt):
|
|
return ctxt
|
|
return {}
|
|
|
|
|
|
def ensure_host_resolvable_v6(hostname):
|
|
"""Ensure that we can resolve our hostname to an IPv6 address by adding it
|
|
to /etc/hosts if it is not already resolvable.
|
|
"""
|
|
try:
|
|
socket.getaddrinfo(hostname, None, socket.AF_INET6)
|
|
except socket.gaierror:
|
|
log("Host '%s' is not ipv6 resolvable - adding to /etc/hosts" %
|
|
hostname, level=DEBUG)
|
|
else:
|
|
log("Host '%s' appears to be ipv6 resolvable" % (hostname),
|
|
level=DEBUG)
|
|
return
|
|
|
|
# This must be the backend address used by haproxy
|
|
host_addr = get_ipv6_addr(exc_list=[config('vip')])[0]
|
|
dtmp = tempfile.mkdtemp()
|
|
try:
|
|
tmp_hosts = os.path.join(dtmp, 'hosts')
|
|
shutil.copy('/etc/hosts', tmp_hosts)
|
|
with open(tmp_hosts, 'a+') as fd:
|
|
lines = fd.readlines()
|
|
for line in lines:
|
|
key = "^%s\s+" % (host_addr)
|
|
if re.search(key, line):
|
|
break
|
|
else:
|
|
fd.write("%s\t%s\n" % (host_addr, hostname))
|
|
|
|
os.rename(tmp_hosts, '/etc/hosts')
|
|
finally:
|
|
shutil.rmtree(dtmp)
|
|
|
|
|
|
class MonContext(context.CephContext):
|
|
interfaces = ['ceph-radosgw']
|
|
|
|
def __call__(self):
|
|
if not relation_ids('mon'):
|
|
return {}
|
|
|
|
host = socket.gethostname()
|
|
systemd_rgw = False
|
|
|
|
mon_hosts = []
|
|
auths = []
|
|
|
|
for rid in relation_ids('mon'):
|
|
for unit in related_units(rid):
|
|
_auth = relation_get('auth', rid=rid, unit=unit)
|
|
if _auth:
|
|
auths.append(_auth)
|
|
|
|
ceph_pub_addr = relation_get('ceph-public-address', rid=rid,
|
|
unit=unit)
|
|
unit_priv_addr = relation_get('private-address', rid=rid,
|
|
unit=unit)
|
|
ceph_addr = ceph_pub_addr or unit_priv_addr
|
|
ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr
|
|
if ceph_addr:
|
|
mon_hosts.append(ceph_addr)
|
|
if relation_get('rgw.{}_key'.format(host), rid=rid, unit=unit):
|
|
systemd_rgw = True
|
|
|
|
if len(set(auths)) != 1:
|
|
e = ("Inconsistent or absent auth returned by mon units. Setting "
|
|
"auth_supported to 'none'")
|
|
log(e, level=WARNING)
|
|
auth = 'none'
|
|
else:
|
|
auth = auths[0]
|
|
|
|
# /etc/init.d/radosgw mandates that a dns name is used for this
|
|
# parameter so ensure that address is resolvable
|
|
if config('prefer-ipv6'):
|
|
ensure_host_resolvable_v6(host)
|
|
|
|
port = determine_api_port(config('port'), singlenode_mode=True)
|
|
if config('prefer-ipv6'):
|
|
port = "[::]:%s" % (port)
|
|
|
|
mon_hosts.sort()
|
|
ctxt = {
|
|
'auth_supported': auth,
|
|
'mon_hosts': ' '.join(mon_hosts),
|
|
'hostname': host,
|
|
'old_auth': cmp_pkgrevno('radosgw', "0.51") < 0,
|
|
'systemd_rgw': systemd_rgw,
|
|
'use_syslog': str(config('use-syslog')).lower(),
|
|
'loglevel': config('loglevel'),
|
|
'port': port,
|
|
'ipv6': config('prefer-ipv6'),
|
|
# The public unit IP is only used in case the authentication is
|
|
# *Not* keystone - in which case it is used to make sure the
|
|
# storage endpoint returned by the built-in auth is the HAproxy
|
|
# (since it defaults to the port the service runs on, and that is
|
|
# not available externally). ~tribaal
|
|
'unit_public_ip': unit_public_ip(),
|
|
}
|
|
|
|
certs_path = '/var/lib/ceph/nss'
|
|
paths = [os.path.join(certs_path, 'ca.pem'),
|
|
os.path.join(certs_path, 'signing_certificate.pem')]
|
|
if all([os.path.isfile(p) for p in paths]):
|
|
ctxt['cms'] = True
|
|
|
|
# NOTE(dosaboy): these sections must correspond to what is supported in
|
|
# the config template.
|
|
sections = ['global', 'client.radosgw.gateway']
|
|
user_provided = CephConfContext(permitted_sections=sections)()
|
|
user_provided = {k.replace('.', '_'): user_provided[k]
|
|
for k in user_provided}
|
|
ctxt.update(user_provided)
|
|
|
|
if self.context_complete(ctxt):
|
|
return ctxt
|
|
|
|
return {}
|