Add Ipv6 support
Adds support for configuring the Rados Gateway to use IPv6 addresses and networks. This can be enabled by setting prefer-ipv6=True. Change-Id: I801fab14accd8c3498ea5468d135f34f159717cb Closes-Bug: 1513524
This commit is contained in:
parent
ea4b4200f1
commit
379f5d78a5
12
config.yaml
12
config.yaml
@ -153,3 +153,15 @@ options:
|
|||||||
description: |
|
description: |
|
||||||
Connect timeout configuration in ms for haproxy, used in HA
|
Connect timeout configuration in ms for haproxy, used in HA
|
||||||
configurations. If not provided, default value of 5000ms is used.
|
configurations. If not provided, default value of 5000ms is used.
|
||||||
|
prefer-ipv6:
|
||||||
|
type: boolean
|
||||||
|
default: False
|
||||||
|
description: |
|
||||||
|
If True enables IPv6 support. The charm will expect network interfaces
|
||||||
|
to be configured with an IPv6 address. If set to False (default) IPv4
|
||||||
|
is expected.
|
||||||
|
.
|
||||||
|
NOTE: these charms do not currently support IPv6 privacy extension. In
|
||||||
|
order for this charm to function correctly, the privacy extension must be
|
||||||
|
disabled and a non-temporary address must be configured/available on
|
||||||
|
your network interface.
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import tempfile
|
||||||
|
import glob
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from charmhelpers.contrib.openstack import context
|
from charmhelpers.contrib.openstack import context
|
||||||
from charmhelpers.contrib.hahelpers.cluster import (
|
from charmhelpers.contrib.hahelpers.cluster import (
|
||||||
determine_api_port,
|
determine_api_port,
|
||||||
@ -5,17 +13,69 @@ from charmhelpers.contrib.hahelpers.cluster import (
|
|||||||
)
|
)
|
||||||
from charmhelpers.core.host import cmp_pkgrevno
|
from charmhelpers.core.host import cmp_pkgrevno
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
|
DEBUG,
|
||||||
WARNING,
|
WARNING,
|
||||||
config,
|
config,
|
||||||
log,
|
log,
|
||||||
relation_ids,
|
relation_ids,
|
||||||
related_units,
|
related_units,
|
||||||
relation_get,
|
relation_get,
|
||||||
unit_get,
|
status_set,
|
||||||
)
|
)
|
||||||
import os
|
from charmhelpers.contrib.network.ip import (
|
||||||
import socket
|
format_ipv6_addr,
|
||||||
import dns.resolver
|
get_host_ip,
|
||||||
|
get_ipv6_addr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_apache_24():
|
||||||
|
if os.path.exists('/etc/apache2/conf-available'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class ApacheContext(context.OSContextGenerator):
|
||||||
|
interfaces = ['http']
|
||||||
|
service_namespace = 'ceph-radosgw'
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
ctxt = {}
|
||||||
|
if config('use-embedded-webserver'):
|
||||||
|
log("Skipping ApacheContext since we are using the embedded "
|
||||||
|
"webserver")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
status_set('maintenance', 'configuring apache')
|
||||||
|
|
||||||
|
src = 'files/www/*'
|
||||||
|
dst = '/var/www/'
|
||||||
|
log("Installing www scripts", level=DEBUG)
|
||||||
|
try:
|
||||||
|
for x in glob.glob(src):
|
||||||
|
shutil.copy(x, dst)
|
||||||
|
except IOError as e:
|
||||||
|
log("Error copying files from '%s' to '%s': %s" % (src, dst, e),
|
||||||
|
level=WARNING)
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.check_call(['a2enmod', 'fastcgi'])
|
||||||
|
subprocess.check_call(['a2enmod', 'rewrite'])
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
log("Error enabling apache modules - %s" % e, level=WARNING)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if is_apache_24():
|
||||||
|
subprocess.check_call(['a2dissite', '000-default'])
|
||||||
|
else:
|
||||||
|
subprocess.check_call(['a2dissite', 'default'])
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
log("Error disabling apache sites - %s" % e, level=WARNING)
|
||||||
|
|
||||||
|
ctxt['hostname'] = socket.gethostname()
|
||||||
|
ctxt['port'] = determine_api_port(config('port'), singlenode_mode=True)
|
||||||
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
class HAProxyContext(context.HAProxyContext):
|
class HAProxyContext(context.HAProxyContext):
|
||||||
@ -66,24 +126,60 @@ class IdentityServiceContext(context.IdentityServiceContext):
|
|||||||
return {}
|
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.OSContextGenerator):
|
class MonContext(context.OSContextGenerator):
|
||||||
interfaces = ['ceph-radosgw']
|
interfaces = ['ceph-radosgw']
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
if not relation_ids('mon'):
|
if not relation_ids('mon'):
|
||||||
return {}
|
return {}
|
||||||
hosts = []
|
mon_hosts = []
|
||||||
auths = []
|
auths = []
|
||||||
for relid in relation_ids('mon'):
|
for relid in relation_ids('mon'):
|
||||||
for unit in related_units(relid):
|
for unit in related_units(relid):
|
||||||
ceph_public_addr = relation_get('ceph-public-address', unit,
|
ceph_public_addr = relation_get('ceph-public-address', unit,
|
||||||
relid)
|
relid)
|
||||||
if ceph_public_addr:
|
if ceph_public_addr:
|
||||||
host_ip = self.get_host_ip(ceph_public_addr)
|
host_ip = format_ipv6_addr(ceph_public_addr) or \
|
||||||
hosts.append('{}:6789'.format(host_ip))
|
get_host_ip(ceph_public_addr)
|
||||||
|
mon_hosts.append('{}:6789'.format(host_ip))
|
||||||
_auth = relation_get('auth', unit, relid)
|
_auth = relation_get('auth', unit, relid)
|
||||||
if _auth:
|
if _auth:
|
||||||
auths.append(_auth)
|
auths.append(_auth)
|
||||||
|
|
||||||
if len(set(auths)) != 1:
|
if len(set(auths)) != 1:
|
||||||
e = ("Inconsistent or absent auth returned by mon units. Setting "
|
e = ("Inconsistent or absent auth returned by mon units. Setting "
|
||||||
"auth_supported to 'none'")
|
"auth_supported to 'none'")
|
||||||
@ -91,17 +187,28 @@ class MonContext(context.OSContextGenerator):
|
|||||||
auth = 'none'
|
auth = 'none'
|
||||||
else:
|
else:
|
||||||
auth = auths[0]
|
auth = auths[0]
|
||||||
hosts.sort()
|
|
||||||
|
# /etc/init.d/radosgw mandates that a dns name is used for this
|
||||||
|
# parameter so ensure that address is resolvable
|
||||||
|
host = socket.gethostname()
|
||||||
|
if config('prefer-ipv6'):
|
||||||
|
ensure_host_resolvable_v6(host)
|
||||||
|
|
||||||
|
port = determine_apache_port(config('port'), singlenode_mode=True)
|
||||||
|
if config('prefer-ipv6'):
|
||||||
|
port = "[::]:%s" % (port)
|
||||||
|
|
||||||
|
mon_hosts.sort()
|
||||||
ctxt = {
|
ctxt = {
|
||||||
'auth_supported': auth,
|
'auth_supported': auth,
|
||||||
'mon_hosts': ' '.join(hosts),
|
'mon_hosts': ' '.join(mon_hosts),
|
||||||
'hostname': socket.gethostname(),
|
'hostname': host,
|
||||||
'old_auth': cmp_pkgrevno('radosgw', "0.51") < 0,
|
'old_auth': cmp_pkgrevno('radosgw', "0.51") < 0,
|
||||||
'use_syslog': str(config('use-syslog')).lower(),
|
'use_syslog': str(config('use-syslog')).lower(),
|
||||||
'embedded_webserver': config('use-embedded-webserver'),
|
'embedded_webserver': config('use-embedded-webserver'),
|
||||||
'loglevel': config('loglevel'),
|
'loglevel': config('loglevel'),
|
||||||
'port': determine_apache_port(config('port'),
|
'port': port,
|
||||||
singlenode_mode=True)
|
'ipv6': config('prefer-ipv6')
|
||||||
}
|
}
|
||||||
|
|
||||||
certs_path = '/var/lib/ceph/nss'
|
certs_path = '/var/lib/ceph/nss'
|
||||||
@ -121,17 +228,3 @@ class MonContext(context.OSContextGenerator):
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def get_host_ip(self, hostname=None):
|
|
||||||
try:
|
|
||||||
if not hostname:
|
|
||||||
hostname = unit_get('private-address')
|
|
||||||
# Test to see if already an IPv4 address
|
|
||||||
socket.inet_aton(hostname)
|
|
||||||
return hostname
|
|
||||||
except socket.error:
|
|
||||||
# This may throw an NXDOMAIN exception; in which case
|
|
||||||
# things are badly broken so just let it kill the hook
|
|
||||||
answers = dns.resolver.query(hostname, 'A')
|
|
||||||
if answers:
|
|
||||||
return answers[0].address
|
|
||||||
|
156
hooks/hooks.py
156
hooks/hooks.py
@ -1,17 +1,16 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2012 Canonical Ltd.
|
# Copyright 2016 Canonical Ltd.
|
||||||
#
|
#
|
||||||
# Authors:
|
# Authors:
|
||||||
# James Page <james.page@ubuntu.com>
|
# James Page <james.page@ubuntu.com>
|
||||||
|
# Edward Hope-Morley <edward.hope-morley@canonical.com>
|
||||||
#
|
#
|
||||||
|
|
||||||
import shutil
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import glob
|
|
||||||
import os
|
|
||||||
import ceph
|
import ceph
|
||||||
|
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
@ -39,27 +38,17 @@ from charmhelpers.core.host import (
|
|||||||
lsb_release,
|
lsb_release,
|
||||||
restart_on_change,
|
restart_on_change,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.hahelpers.cluster import (
|
|
||||||
determine_apache_port,
|
|
||||||
)
|
|
||||||
from utils import (
|
|
||||||
render_template,
|
|
||||||
enable_pocket,
|
|
||||||
is_apache_24,
|
|
||||||
CEPHRG_HA_RES,
|
|
||||||
register_configs,
|
|
||||||
REQUIRED_INTERFACES,
|
|
||||||
check_optional_relations,
|
|
||||||
)
|
|
||||||
from charmhelpers.payload.execd import execd_preinstall
|
from charmhelpers.payload.execd import execd_preinstall
|
||||||
from charmhelpers.core.host import (
|
from charmhelpers.core.host import (
|
||||||
cmp_pkgrevno,
|
cmp_pkgrevno,
|
||||||
mkdir,
|
mkdir,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.contrib.network.ip import (
|
from charmhelpers.contrib.network.ip import (
|
||||||
|
format_ipv6_addr,
|
||||||
|
get_ipv6_addr,
|
||||||
get_iface_for_address,
|
get_iface_for_address,
|
||||||
get_netmask_for_address,
|
get_netmask_for_address,
|
||||||
|
is_ipv6,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.ip import (
|
from charmhelpers.contrib.openstack.ip import (
|
||||||
canonical_url,
|
canonical_url,
|
||||||
@ -72,18 +61,17 @@ from charmhelpers.contrib.storage.linux.ceph import (
|
|||||||
send_request_if_needed,
|
send_request_if_needed,
|
||||||
is_request_complete,
|
is_request_complete,
|
||||||
)
|
)
|
||||||
|
from utils import (
|
||||||
APACHE_PORTS_CONF = '/etc/apache2/ports.conf'
|
enable_pocket,
|
||||||
|
CEPHRG_HA_RES,
|
||||||
|
register_configs,
|
||||||
|
REQUIRED_INTERFACES,
|
||||||
|
check_optional_relations,
|
||||||
|
setup_ipv6,
|
||||||
|
)
|
||||||
|
|
||||||
hooks = Hooks()
|
hooks = Hooks()
|
||||||
CONFIGS = register_configs()
|
CONFIGS = register_configs()
|
||||||
|
|
||||||
|
|
||||||
def install_www_scripts():
|
|
||||||
for x in glob.glob('files/www/*'):
|
|
||||||
shutil.copy(x, '/var/www/')
|
|
||||||
|
|
||||||
|
|
||||||
NSS_DIR = '/var/lib/ceph/nss'
|
NSS_DIR = '/var/lib/ceph/nss'
|
||||||
|
|
||||||
|
|
||||||
@ -145,43 +133,6 @@ def install():
|
|||||||
os.makedirs('/etc/ceph')
|
os.makedirs('/etc/ceph')
|
||||||
|
|
||||||
|
|
||||||
def emit_apacheconf():
|
|
||||||
apachecontext = {
|
|
||||||
"hostname": unit_get('private-address'),
|
|
||||||
"port": determine_apache_port(config('port'), singlenode_mode=True)
|
|
||||||
}
|
|
||||||
site_conf = '/etc/apache2/sites-available/rgw'
|
|
||||||
if is_apache_24():
|
|
||||||
site_conf = '/etc/apache2/sites-available/rgw.conf'
|
|
||||||
with open(site_conf, 'w') as apacheconf:
|
|
||||||
apacheconf.write(render_template('rgw', apachecontext))
|
|
||||||
|
|
||||||
|
|
||||||
def apache_sites():
|
|
||||||
if is_apache_24():
|
|
||||||
subprocess.check_call(['a2dissite', '000-default'])
|
|
||||||
else:
|
|
||||||
subprocess.check_call(['a2dissite', 'default'])
|
|
||||||
subprocess.check_call(['a2ensite', 'rgw'])
|
|
||||||
|
|
||||||
|
|
||||||
def apache_modules():
|
|
||||||
subprocess.check_call(['a2enmod', 'fastcgi'])
|
|
||||||
subprocess.check_call(['a2enmod', 'rewrite'])
|
|
||||||
|
|
||||||
|
|
||||||
def apache_reload():
|
|
||||||
subprocess.call(['service', 'apache2', 'reload'])
|
|
||||||
|
|
||||||
|
|
||||||
def apache_ports():
|
|
||||||
portscontext = {
|
|
||||||
"port": determine_apache_port(config('port'), singlenode_mode=True)
|
|
||||||
}
|
|
||||||
with open(APACHE_PORTS_CONF, 'w') as portsconf:
|
|
||||||
portsconf.write(render_template('ports.conf', portscontext))
|
|
||||||
|
|
||||||
|
|
||||||
def setup_keystone_certs(unit=None, rid=None):
|
def setup_keystone_certs(unit=None, rid=None):
|
||||||
"""
|
"""
|
||||||
Get CA and signing certs from Keystone used to decrypt revoked token list.
|
Get CA and signing certs from Keystone used to decrypt revoked token list.
|
||||||
@ -213,6 +164,9 @@ def setup_keystone_certs(unit=None, rid=None):
|
|||||||
for key in required_keys:
|
for key in required_keys:
|
||||||
settings[key] = rdata.get(key)
|
settings[key] = rdata.get(key)
|
||||||
|
|
||||||
|
if is_ipv6(settings.get('auth_host')):
|
||||||
|
settings['auth_host'] = format_ipv6_addr(settings.get('auth_host'))
|
||||||
|
|
||||||
if not all(settings.values()):
|
if not all(settings.values()):
|
||||||
log("Missing relation settings (%s) - skipping cert setup" %
|
log("Missing relation settings (%s) - skipping cert setup" %
|
||||||
(', '.join([k for k in settings.keys() if not settings[k]])),
|
(', '.join([k for k in settings.keys() if not settings[k]])),
|
||||||
@ -288,19 +242,29 @@ def setup_keystone_certs(unit=None, rid=None):
|
|||||||
'/etc/haproxy/haproxy.cfg': ['haproxy']})
|
'/etc/haproxy/haproxy.cfg': ['haproxy']})
|
||||||
def config_changed():
|
def config_changed():
|
||||||
install_packages()
|
install_packages()
|
||||||
CONFIGS.write_all()
|
|
||||||
if not config('use-embedded-webserver'):
|
if config('prefer-ipv6'):
|
||||||
status_set('maintenance', 'configuring apache')
|
status_set('maintenance', 'configuring ipv6')
|
||||||
emit_apacheconf()
|
setup_ipv6()
|
||||||
install_www_scripts()
|
|
||||||
apache_sites()
|
|
||||||
apache_modules()
|
|
||||||
apache_ports()
|
|
||||||
apache_reload()
|
|
||||||
|
|
||||||
for r_id in relation_ids('identity-service'):
|
for r_id in relation_ids('identity-service'):
|
||||||
identity_changed(relid=r_id)
|
identity_changed(relid=r_id)
|
||||||
|
|
||||||
|
for r_id in relation_ids('cluster'):
|
||||||
|
cluster_joined(rid=r_id)
|
||||||
|
|
||||||
|
CONFIGS.write_all()
|
||||||
|
|
||||||
|
if not config('use-embedded-webserver'):
|
||||||
|
try:
|
||||||
|
subprocess.check_call(['a2ensite', 'rgw'])
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
log("Error enabling apache module 'rgw' - %s" % e, level=WARNING)
|
||||||
|
|
||||||
|
# Ensure started but do a soft reload
|
||||||
|
subprocess.call(['service', 'apache2', 'start'])
|
||||||
|
subprocess.call(['service', 'apache2', 'reload'])
|
||||||
|
|
||||||
|
|
||||||
@hooks.hook('mon-relation-departed',
|
@hooks.hook('mon-relation-departed',
|
||||||
'mon-relation-changed')
|
'mon-relation-changed')
|
||||||
@ -373,8 +337,18 @@ def identity_changed(relid=None):
|
|||||||
restart()
|
restart()
|
||||||
|
|
||||||
|
|
||||||
@hooks.hook('cluster-relation-changed',
|
@hooks.hook('cluster-relation-joined')
|
||||||
'cluster-relation-joined')
|
@restart_on_change({'/etc/haproxy/haproxy.cfg': ['haproxy']})
|
||||||
|
def cluster_joined(rid=None):
|
||||||
|
settings = {}
|
||||||
|
if config('prefer-ipv6'):
|
||||||
|
private_addr = get_ipv6_addr(exc_list=[config('vip')])[0]
|
||||||
|
settings['private-address'] = private_addr
|
||||||
|
|
||||||
|
relation_set(relation_id=rid, **settings)
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('cluster-relation-changed')
|
||||||
@restart_on_change({'/etc/haproxy/haproxy.cfg': ['haproxy']})
|
@restart_on_change({'/etc/haproxy/haproxy.cfg': ['haproxy']})
|
||||||
def cluster_changed():
|
def cluster_changed():
|
||||||
CONFIGS.write_all()
|
CONFIGS.write_all()
|
||||||
@ -384,17 +358,12 @@ def cluster_changed():
|
|||||||
|
|
||||||
@hooks.hook('ha-relation-joined')
|
@hooks.hook('ha-relation-joined')
|
||||||
def ha_relation_joined():
|
def ha_relation_joined():
|
||||||
# Obtain the config values necessary for the cluster config. These
|
|
||||||
# include multicast port and interface to bind to.
|
|
||||||
corosync_bindiface = config('ha-bindiface')
|
|
||||||
corosync_mcastport = config('ha-mcastport')
|
|
||||||
vip = config('vip')
|
vip = config('vip')
|
||||||
if not vip:
|
if not vip:
|
||||||
log('Unable to configure hacluster as vip not provided',
|
log('Unable to configure hacluster as vip not provided', level=ERROR)
|
||||||
level=ERROR)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Obtain resources
|
# Obtain resources
|
||||||
# SWIFT_HA_RES = 'grp_swift_vips'
|
|
||||||
resources = {
|
resources = {
|
||||||
'res_cephrg_haproxy': 'lsb:haproxy'
|
'res_cephrg_haproxy': 'lsb:haproxy'
|
||||||
}
|
}
|
||||||
@ -404,15 +373,25 @@ def ha_relation_joined():
|
|||||||
|
|
||||||
vip_group = []
|
vip_group = []
|
||||||
for vip in vip.split():
|
for vip in vip.split():
|
||||||
|
if is_ipv6(vip):
|
||||||
|
res_rgw_vip = 'ocf:heartbeat:IPv6addr'
|
||||||
|
vip_params = 'ipv6addr'
|
||||||
|
else:
|
||||||
|
res_rgw_vip = 'ocf:heartbeat:IPaddr2'
|
||||||
|
vip_params = 'ip'
|
||||||
|
|
||||||
iface = get_iface_for_address(vip)
|
iface = get_iface_for_address(vip)
|
||||||
|
netmask = get_netmask_for_address(vip)
|
||||||
|
|
||||||
if iface is not None:
|
if iface is not None:
|
||||||
vip_key = 'res_cephrg_{}_vip'.format(iface)
|
vip_key = 'res_cephrg_{}_vip'.format(iface)
|
||||||
resources[vip_key] = 'ocf:heartbeat:IPaddr2'
|
resources[vip_key] = res_rgw_vip
|
||||||
resource_params[vip_key] = (
|
resource_params[vip_key] = (
|
||||||
'params ip="{vip}" cidr_netmask="{netmask}"'
|
'params {ip}="{vip}" cidr_netmask="{netmask}"'
|
||||||
' nic="{iface}"'.format(vip=vip,
|
' nic="{iface}"'.format(ip=vip_params,
|
||||||
|
vip=vip,
|
||||||
iface=iface,
|
iface=iface,
|
||||||
netmask=get_netmask_for_address(vip))
|
netmask=netmask)
|
||||||
)
|
)
|
||||||
vip_group.append(vip_key)
|
vip_group.append(vip_key)
|
||||||
|
|
||||||
@ -426,6 +405,11 @@ def ha_relation_joined():
|
|||||||
'cl_cephrg_haproxy': 'res_cephrg_haproxy'
|
'cl_cephrg_haproxy': 'res_cephrg_haproxy'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Obtain the config values necessary for the cluster config. These
|
||||||
|
# include multicast port and interface to bind to.
|
||||||
|
corosync_bindiface = config('ha-bindiface')
|
||||||
|
corosync_mcastport = config('ha-mcastport')
|
||||||
|
|
||||||
relation_set(init_services=init_services,
|
relation_set(init_services=init_services,
|
||||||
corosync_bindiface=corosync_bindiface,
|
corosync_bindiface=corosync_bindiface,
|
||||||
corosync_mcastport=corosync_mcastport,
|
corosync_mcastport=corosync_mcastport,
|
||||||
|
@ -1,27 +1,45 @@
|
|||||||
#
|
#
|
||||||
# Copyright 2012 Canonical Ltd.
|
# Copyright 2016 Canonical Ltd.
|
||||||
#
|
#
|
||||||
# Authors:
|
# Authors:
|
||||||
# James Page <james.page@ubuntu.com>
|
# James Page <james.page@ubuntu.com>
|
||||||
# Paul Collins <paul.collins@canonical.com>
|
# Paul Collins <paul.collins@canonical.com>
|
||||||
|
# Edward Hope-Morley <edward.hope-morley@canonical.com>
|
||||||
#
|
#
|
||||||
|
|
||||||
import socket
|
|
||||||
import re
|
|
||||||
import os
|
import os
|
||||||
import dns.resolver
|
import re
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from charmhelpers.core.hookenv import unit_get, relation_ids, status_get
|
|
||||||
from charmhelpers.contrib.openstack import context, templating
|
|
||||||
from charmhelpers.contrib.openstack.utils import set_os_workload_status
|
|
||||||
from charmhelpers.contrib.hahelpers.cluster import get_hacluster_config
|
|
||||||
from charmhelpers.core.host import cmp_pkgrevno
|
|
||||||
from charmhelpers.fetch import filter_installed_packages
|
|
||||||
|
|
||||||
import ceph_radosgw_context
|
import ceph_radosgw_context
|
||||||
|
|
||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
relation_ids,
|
||||||
|
status_get,
|
||||||
|
)
|
||||||
|
from charmhelpers.contrib.openstack import (
|
||||||
|
context,
|
||||||
|
templating,
|
||||||
|
)
|
||||||
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
|
os_release,
|
||||||
|
set_os_workload_status,
|
||||||
|
)
|
||||||
|
from charmhelpers.contrib.hahelpers.cluster import get_hacluster_config
|
||||||
|
from charmhelpers.core.host import (
|
||||||
|
cmp_pkgrevno,
|
||||||
|
lsb_release,
|
||||||
|
)
|
||||||
|
from charmhelpers.fetch import (
|
||||||
|
apt_install,
|
||||||
|
apt_update,
|
||||||
|
add_source,
|
||||||
|
filter_installed_packages,
|
||||||
|
)
|
||||||
|
|
||||||
# The interface is said to be satisfied if anyone of the interfaces in the
|
# The interface is said to be satisfied if anyone of the interfaces in the
|
||||||
# list has a complete context.
|
# list has a complete context.
|
||||||
REQUIRED_INTERFACES = {
|
REQUIRED_INTERFACES = {
|
||||||
@ -32,6 +50,9 @@ TEMPLATES_DIR = 'templates'
|
|||||||
TEMPLATES = 'templates/'
|
TEMPLATES = 'templates/'
|
||||||
HAPROXY_CONF = '/etc/haproxy/haproxy.cfg'
|
HAPROXY_CONF = '/etc/haproxy/haproxy.cfg'
|
||||||
CEPH_CONF = '/etc/ceph/ceph.conf'
|
CEPH_CONF = '/etc/ceph/ceph.conf'
|
||||||
|
APACHE_CONF = '/etc/apache2/sites-available/rgw'
|
||||||
|
APACHE_24_CONF = '/etc/apache2/sites-available/rgw.conf'
|
||||||
|
APACHE_PORTS_CONF = '/etc/apache2/ports.conf'
|
||||||
|
|
||||||
BASE_RESOURCE_MAP = OrderedDict([
|
BASE_RESOURCE_MAP = OrderedDict([
|
||||||
(HAPROXY_CONF, {
|
(HAPROXY_CONF, {
|
||||||
@ -39,6 +60,18 @@ BASE_RESOURCE_MAP = OrderedDict([
|
|||||||
ceph_radosgw_context.HAProxyContext()],
|
ceph_radosgw_context.HAProxyContext()],
|
||||||
'services': ['haproxy'],
|
'services': ['haproxy'],
|
||||||
}),
|
}),
|
||||||
|
(APACHE_CONF, {
|
||||||
|
'contexts': [ceph_radosgw_context.ApacheContext()],
|
||||||
|
'services': ['apache2'],
|
||||||
|
}),
|
||||||
|
(APACHE_24_CONF, {
|
||||||
|
'contexts': [ceph_radosgw_context.ApacheContext()],
|
||||||
|
'services': ['apache2'],
|
||||||
|
}),
|
||||||
|
(APACHE_PORTS_CONF, {
|
||||||
|
'contexts': [ceph_radosgw_context.ApacheContext()],
|
||||||
|
'services': ['apache2'],
|
||||||
|
}),
|
||||||
(CEPH_CONF, {
|
(CEPH_CONF, {
|
||||||
'contexts': [ceph_radosgw_context.MonContext()],
|
'contexts': [ceph_radosgw_context.MonContext()],
|
||||||
'services': ['radosgw'],
|
'services': ['radosgw'],
|
||||||
@ -51,6 +84,11 @@ def resource_map():
|
|||||||
Dynamically generate a map of resources that will be managed for a single
|
Dynamically generate a map of resources that will be managed for a single
|
||||||
hook execution.
|
hook execution.
|
||||||
'''
|
'''
|
||||||
|
if os.path.exists('/etc/apache2/conf-available'):
|
||||||
|
BASE_RESOURCE_MAP.pop(APACHE_CONF)
|
||||||
|
else:
|
||||||
|
BASE_RESOURCE_MAP.pop(APACHE_24_CONF)
|
||||||
|
|
||||||
resource_map = deepcopy(BASE_RESOURCE_MAP)
|
resource_map = deepcopy(BASE_RESOURCE_MAP)
|
||||||
return resource_map
|
return resource_map
|
||||||
|
|
||||||
@ -92,28 +130,6 @@ def enable_pocket(pocket):
|
|||||||
sources.write(line)
|
sources.write(line)
|
||||||
|
|
||||||
|
|
||||||
def get_host_ip(hostname=None):
|
|
||||||
try:
|
|
||||||
if not hostname:
|
|
||||||
hostname = unit_get('private-address')
|
|
||||||
# Test to see if already an IPv4 address
|
|
||||||
socket.inet_aton(hostname)
|
|
||||||
return hostname
|
|
||||||
except socket.error:
|
|
||||||
# This may throw an NXDOMAIN exception; in which case
|
|
||||||
# things are badly broken so just let it kill the hook
|
|
||||||
answers = dns.resolver.query(hostname, 'A')
|
|
||||||
if answers:
|
|
||||||
return answers[0].address
|
|
||||||
|
|
||||||
|
|
||||||
def is_apache_24():
|
|
||||||
if os.path.exists('/etc/apache2/conf-available'):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def check_optional_relations(configs):
|
def check_optional_relations(configs):
|
||||||
required_interfaces = {}
|
required_interfaces = {}
|
||||||
if relation_ids('ha'):
|
if relation_ids('ha'):
|
||||||
@ -132,3 +148,18 @@ def check_optional_relations(configs):
|
|||||||
return status_get()
|
return status_get()
|
||||||
else:
|
else:
|
||||||
return 'unknown', 'No optional relations'
|
return 'unknown', 'No optional relations'
|
||||||
|
|
||||||
|
|
||||||
|
def setup_ipv6():
|
||||||
|
ubuntu_rel = lsb_release()['DISTRIB_CODENAME'].lower()
|
||||||
|
if ubuntu_rel < "trusty":
|
||||||
|
raise Exception("IPv6 is not supported in the charms for Ubuntu "
|
||||||
|
"versions less than Trusty 14.04")
|
||||||
|
|
||||||
|
# Need haproxy >= 1.5.3 for ipv6 so for Trusty if we are <= Kilo we need to
|
||||||
|
# use trusty-backports otherwise we can use the UCA.
|
||||||
|
if ubuntu_rel == 'trusty' and os_release('ceph-common') < 'liberty':
|
||||||
|
add_source('deb http://archive.ubuntu.com/ubuntu trusty-backports '
|
||||||
|
'main')
|
||||||
|
apt_update(fatal=True)
|
||||||
|
apt_install('haproxy/trusty-backports', fatal=True)
|
||||||
|
@ -11,6 +11,9 @@ log to syslog = {{ use_syslog }}
|
|||||||
err to syslog = {{ use_syslog }}
|
err to syslog = {{ use_syslog }}
|
||||||
clog to syslog = {{ use_syslog }}
|
clog to syslog = {{ use_syslog }}
|
||||||
debug rgw = {{ loglevel }}/5
|
debug rgw = {{ loglevel }}/5
|
||||||
|
{% if ipv6 -%}
|
||||||
|
ms bind ipv6 = true
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
[client.radosgw.gateway]
|
[client.radosgw.gateway]
|
||||||
host = {{ hostname }}
|
host = {{ hostname }}
|
||||||
|
25
templates/rgw.conf
Normal file
25
templates/rgw.conf
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<IfModule mod_fastcgi.c>
|
||||||
|
FastCgiExternalServer /var/www/s3gw.fcgi -socket /tmp/radosgw.sock
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
<VirtualHost *:{{ port }}>
|
||||||
|
ServerName {{ hostname }}
|
||||||
|
ServerAdmin ceph@ubuntu.com
|
||||||
|
DocumentRoot /var/www
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteRule ^/([a-zA-Z0-9-_.]*)([/]?.*) /s3gw.fcgi?page=$1¶ms=$2&%{QUERY_STRING} [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||||||
|
<IfModule mod_fastcgi.c>
|
||||||
|
<Directory /var/www>
|
||||||
|
Options +ExecCGI
|
||||||
|
AllowOverride All
|
||||||
|
SetHandler fastcgi-script
|
||||||
|
Order allow,deny
|
||||||
|
Allow from all
|
||||||
|
AuthBasicAuthoritative Off
|
||||||
|
</Directory>
|
||||||
|
</IfModule>
|
||||||
|
AllowEncodedSlashes On
|
||||||
|
ErrorLog /var/log/apache2/error.log
|
||||||
|
CustomLog /var/log/apache2/access.log combined
|
||||||
|
ServerSignature Off
|
||||||
|
</VirtualHost>
|
@ -13,6 +13,7 @@ TO_PATCH = [
|
|||||||
'related_units',
|
'related_units',
|
||||||
'cmp_pkgrevno',
|
'cmp_pkgrevno',
|
||||||
'socket',
|
'socket',
|
||||||
|
'is_apache_24',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -147,8 +148,9 @@ class MonContextTest(CharmTestCase):
|
|||||||
super(MonContextTest, self).setUp(context, TO_PATCH)
|
super(MonContextTest, self).setUp(context, TO_PATCH)
|
||||||
self.config.side_effect = self.test_config.get
|
self.config.side_effect = self.test_config.get
|
||||||
|
|
||||||
def test_ctxt(self):
|
@patch.object(context, 'ensure_host_resolvable_v6')
|
||||||
self.socket.gethostname.return_value = '10.0.0.10'
|
def test_ctxt(self, mock_ensure_rsv_v6):
|
||||||
|
self.socket.gethostname.return_value = 'testhost'
|
||||||
mon_ctxt = context.MonContext()
|
mon_ctxt = context.MonContext()
|
||||||
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
|
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
|
||||||
|
|
||||||
@ -157,6 +159,7 @@ class MonContextTest(CharmTestCase):
|
|||||||
return addresses.pop()
|
return addresses.pop()
|
||||||
elif attr == 'auth':
|
elif attr == 'auth':
|
||||||
return 'cephx'
|
return 'cephx'
|
||||||
|
|
||||||
self.relation_get.side_effect = _relation_get
|
self.relation_get.side_effect = _relation_get
|
||||||
self.relation_ids.return_value = ['mon:6']
|
self.relation_ids.return_value = ['mon:6']
|
||||||
self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
|
self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
|
||||||
@ -164,17 +167,26 @@ class MonContextTest(CharmTestCase):
|
|||||||
'auth_supported': 'cephx',
|
'auth_supported': 'cephx',
|
||||||
'embedded_webserver': False,
|
'embedded_webserver': False,
|
||||||
'disable_100_continue': True,
|
'disable_100_continue': True,
|
||||||
'hostname': '10.0.0.10',
|
'hostname': 'testhost',
|
||||||
'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
|
'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
|
||||||
'old_auth': False,
|
'old_auth': False,
|
||||||
'use_syslog': 'false',
|
'use_syslog': 'false',
|
||||||
'loglevel': 1,
|
'loglevel': 1,
|
||||||
'port': 70
|
'port': 70,
|
||||||
|
'ipv6': False
|
||||||
}
|
}
|
||||||
self.assertEqual(expect, mon_ctxt())
|
self.assertEqual(expect, mon_ctxt())
|
||||||
|
self.assertFalse(mock_ensure_rsv_v6.called)
|
||||||
|
|
||||||
|
self.test_config.set('prefer-ipv6', True)
|
||||||
|
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
|
||||||
|
expect['ipv6'] = True
|
||||||
|
expect['port'] = "[::]:%s" % (70)
|
||||||
|
self.assertEqual(expect, mon_ctxt())
|
||||||
|
self.assertTrue(mock_ensure_rsv_v6.called)
|
||||||
|
|
||||||
def test_ctxt_missing_data(self):
|
def test_ctxt_missing_data(self):
|
||||||
self.socket.gethostname.return_value = '10.0.0.10'
|
self.socket.gethostname.return_value = 'testhost'
|
||||||
mon_ctxt = context.MonContext()
|
mon_ctxt = context.MonContext()
|
||||||
self.relation_get.return_value = None
|
self.relation_get.return_value = None
|
||||||
self.relation_ids.return_value = ['mon:6']
|
self.relation_ids.return_value = ['mon:6']
|
||||||
@ -182,7 +194,7 @@ class MonContextTest(CharmTestCase):
|
|||||||
self.assertEqual({}, mon_ctxt())
|
self.assertEqual({}, mon_ctxt())
|
||||||
|
|
||||||
def test_ctxt_inconsistent_auths(self):
|
def test_ctxt_inconsistent_auths(self):
|
||||||
self.socket.gethostname.return_value = '10.0.0.10'
|
self.socket.gethostname.return_value = 'testhost'
|
||||||
mon_ctxt = context.MonContext()
|
mon_ctxt = context.MonContext()
|
||||||
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
|
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
|
||||||
auths = ['cephx', 'cephy', 'cephz']
|
auths = ['cephx', 'cephy', 'cephz']
|
||||||
@ -199,17 +211,18 @@ class MonContextTest(CharmTestCase):
|
|||||||
'auth_supported': 'none',
|
'auth_supported': 'none',
|
||||||
'embedded_webserver': False,
|
'embedded_webserver': False,
|
||||||
'disable_100_continue': True,
|
'disable_100_continue': True,
|
||||||
'hostname': '10.0.0.10',
|
'hostname': 'testhost',
|
||||||
'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
|
'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
|
||||||
'old_auth': False,
|
'old_auth': False,
|
||||||
'use_syslog': 'false',
|
'use_syslog': 'false',
|
||||||
'loglevel': 1,
|
'loglevel': 1,
|
||||||
'port': 70
|
'port': 70,
|
||||||
|
'ipv6': False
|
||||||
}
|
}
|
||||||
self.assertEqual(expect, mon_ctxt())
|
self.assertEqual(expect, mon_ctxt())
|
||||||
|
|
||||||
def test_ctxt_consistent_auths(self):
|
def test_ctxt_consistent_auths(self):
|
||||||
self.socket.gethostname.return_value = '10.0.0.10'
|
self.socket.gethostname.return_value = 'testhost'
|
||||||
mon_ctxt = context.MonContext()
|
mon_ctxt = context.MonContext()
|
||||||
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
|
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
|
||||||
auths = ['cephx', 'cephx', 'cephx']
|
auths = ['cephx', 'cephx', 'cephx']
|
||||||
@ -226,11 +239,19 @@ class MonContextTest(CharmTestCase):
|
|||||||
'auth_supported': 'cephx',
|
'auth_supported': 'cephx',
|
||||||
'embedded_webserver': False,
|
'embedded_webserver': False,
|
||||||
'disable_100_continue': True,
|
'disable_100_continue': True,
|
||||||
'hostname': '10.0.0.10',
|
'hostname': 'testhost',
|
||||||
'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
|
'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
|
||||||
'old_auth': False,
|
'old_auth': False,
|
||||||
'use_syslog': 'false',
|
'use_syslog': 'false',
|
||||||
'loglevel': 1,
|
'loglevel': 1,
|
||||||
'port': 70
|
'port': 70,
|
||||||
|
'ipv6': False
|
||||||
}
|
}
|
||||||
self.assertEqual(expect, mon_ctxt())
|
self.assertEqual(expect, mon_ctxt())
|
||||||
|
|
||||||
|
|
||||||
|
class ApacheContextTest(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ApacheContextTest, self).setUp(context, TO_PATCH)
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
@ -6,7 +6,6 @@ from mock import (
|
|||||||
|
|
||||||
from test_utils import (
|
from test_utils import (
|
||||||
CharmTestCase,
|
CharmTestCase,
|
||||||
patch_open
|
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.ip import PUBLIC
|
from charmhelpers.contrib.openstack.ip import PUBLIC
|
||||||
|
|
||||||
@ -34,8 +33,6 @@ TO_PATCH = [
|
|||||||
'enable_pocket',
|
'enable_pocket',
|
||||||
'get_iface_for_address',
|
'get_iface_for_address',
|
||||||
'get_netmask_for_address',
|
'get_netmask_for_address',
|
||||||
'glob',
|
|
||||||
'is_apache_24',
|
|
||||||
'log',
|
'log',
|
||||||
'lsb_release',
|
'lsb_release',
|
||||||
'open_port',
|
'open_port',
|
||||||
@ -44,8 +41,6 @@ TO_PATCH = [
|
|||||||
'relation_set',
|
'relation_set',
|
||||||
'relation_get',
|
'relation_get',
|
||||||
'related_units',
|
'related_units',
|
||||||
'render_template',
|
|
||||||
'shutil',
|
|
||||||
'status_set',
|
'status_set',
|
||||||
'subprocess',
|
'subprocess',
|
||||||
'sys',
|
'sys',
|
||||||
@ -62,11 +57,6 @@ class CephRadosGWTests(CharmTestCase):
|
|||||||
self.test_config.set('key', 'secretkey')
|
self.test_config.set('key', 'secretkey')
|
||||||
self.test_config.set('use-syslog', False)
|
self.test_config.set('use-syslog', False)
|
||||||
|
|
||||||
def test_install_www_scripts(self):
|
|
||||||
self.glob.glob.return_value = ['files/www/bob']
|
|
||||||
ceph_hooks.install_www_scripts()
|
|
||||||
self.shutil.copy.assert_called_with('files/www/bob', '/var/www/')
|
|
||||||
|
|
||||||
def test_install_ceph_optimised_packages(self):
|
def test_install_ceph_optimised_packages(self):
|
||||||
self.lsb_release.return_value = {'DISTRIB_CODENAME': 'vivid'}
|
self.lsb_release.return_value = {'DISTRIB_CODENAME': 'vivid'}
|
||||||
fastcgi_source = (
|
fastcgi_source = (
|
||||||
@ -122,69 +112,12 @@ class CephRadosGWTests(CharmTestCase):
|
|||||||
self.enable_pocket.assert_called_with('multiverse')
|
self.enable_pocket.assert_called_with('multiverse')
|
||||||
self.os.makedirs.called_with('/var/lib/ceph/nss')
|
self.os.makedirs.called_with('/var/lib/ceph/nss')
|
||||||
|
|
||||||
def test_emit_apacheconf(self):
|
|
||||||
self.is_apache_24.return_value = True
|
|
||||||
self.unit_get.return_value = '10.0.0.1'
|
|
||||||
apachecontext = {
|
|
||||||
"hostname": '10.0.0.1',
|
|
||||||
"port": 70,
|
|
||||||
}
|
|
||||||
vhost_file = '/etc/apache2/sites-available/rgw.conf'
|
|
||||||
with patch_open() as (_open, _file):
|
|
||||||
ceph_hooks.emit_apacheconf()
|
|
||||||
_open.assert_called_with(vhost_file, 'w')
|
|
||||||
self.render_template.assert_called_with('rgw', apachecontext)
|
|
||||||
|
|
||||||
def test_apache_sites24(self):
|
|
||||||
self.is_apache_24.return_value = True
|
|
||||||
ceph_hooks.apache_sites()
|
|
||||||
calls = [
|
|
||||||
call(['a2dissite', '000-default']),
|
|
||||||
call(['a2ensite', 'rgw']),
|
|
||||||
]
|
|
||||||
self.subprocess.check_call.assert_has_calls(calls)
|
|
||||||
|
|
||||||
def test_apache_sites22(self):
|
|
||||||
self.is_apache_24.return_value = False
|
|
||||||
ceph_hooks.apache_sites()
|
|
||||||
calls = [
|
|
||||||
call(['a2dissite', 'default']),
|
|
||||||
call(['a2ensite', 'rgw']),
|
|
||||||
]
|
|
||||||
self.subprocess.check_call.assert_has_calls(calls)
|
|
||||||
|
|
||||||
def test_apache_modules(self):
|
|
||||||
ceph_hooks.apache_modules()
|
|
||||||
calls = [
|
|
||||||
call(['a2enmod', 'fastcgi']),
|
|
||||||
call(['a2enmod', 'rewrite']),
|
|
||||||
]
|
|
||||||
self.subprocess.check_call.assert_has_calls(calls)
|
|
||||||
|
|
||||||
def test_apache_reload(self):
|
|
||||||
ceph_hooks.apache_reload()
|
|
||||||
calls = [
|
|
||||||
call(['service', 'apache2', 'reload']),
|
|
||||||
]
|
|
||||||
self.subprocess.call.assert_has_calls(calls)
|
|
||||||
|
|
||||||
@patch.object(ceph_hooks, 'apache_ports', lambda *args: True)
|
|
||||||
@patch.object(ceph_hooks, 'mkdir', lambda *args: None)
|
@patch.object(ceph_hooks, 'mkdir', lambda *args: None)
|
||||||
def test_config_changed(self):
|
def test_config_changed(self):
|
||||||
_install_packages = self.patch('install_packages')
|
_install_packages = self.patch('install_packages')
|
||||||
_emit_apacheconf = self.patch('emit_apacheconf')
|
|
||||||
_install_www_scripts = self.patch('install_www_scripts')
|
|
||||||
_apache_sites = self.patch('apache_sites')
|
|
||||||
_apache_modules = self.patch('apache_modules')
|
|
||||||
_apache_reload = self.patch('apache_reload')
|
|
||||||
ceph_hooks.config_changed()
|
ceph_hooks.config_changed()
|
||||||
self.assertTrue(_install_packages.called)
|
self.assertTrue(_install_packages.called)
|
||||||
self.CONFIGS.write_all.assert_called_with()
|
self.CONFIGS.write_all.assert_called_with()
|
||||||
self.assertTrue(_emit_apacheconf.called)
|
|
||||||
self.assertTrue(_install_www_scripts.called)
|
|
||||||
self.assertTrue(_apache_sites.called)
|
|
||||||
self.assertTrue(_apache_modules.called)
|
|
||||||
self.assertTrue(_apache_reload.called)
|
|
||||||
|
|
||||||
@patch.object(ceph_hooks, 'is_request_complete',
|
@patch.object(ceph_hooks, 'is_request_complete',
|
||||||
lambda *args, **kwargs: True)
|
lambda *args, **kwargs: True)
|
||||||
|
Loading…
Reference in New Issue
Block a user