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:
Edward Hope-Morley 2016-03-02 12:23:53 +00:00
parent ea4b4200f1
commit 379f5d78a5
8 changed files with 324 additions and 222 deletions

View File

@ -153,3 +153,15 @@ options:
description: |
Connect timeout configuration in ms for haproxy, used in HA
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.

View File

@ -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.hahelpers.cluster import (
determine_api_port,
@ -5,17 +13,69 @@ from charmhelpers.contrib.hahelpers.cluster import (
)
from charmhelpers.core.host import cmp_pkgrevno
from charmhelpers.core.hookenv import (
DEBUG,
WARNING,
config,
log,
relation_ids,
related_units,
relation_get,
unit_get,
status_set,
)
import os
import socket
import dns.resolver
from charmhelpers.contrib.network.ip import (
format_ipv6_addr,
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):
@ -66,24 +126,60 @@ class IdentityServiceContext(context.IdentityServiceContext):
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):
interfaces = ['ceph-radosgw']
def __call__(self):
if not relation_ids('mon'):
return {}
hosts = []
mon_hosts = []
auths = []
for relid in relation_ids('mon'):
for unit in related_units(relid):
ceph_public_addr = relation_get('ceph-public-address', unit,
relid)
if ceph_public_addr:
host_ip = self.get_host_ip(ceph_public_addr)
hosts.append('{}:6789'.format(host_ip))
host_ip = format_ipv6_addr(ceph_public_addr) or \
get_host_ip(ceph_public_addr)
mon_hosts.append('{}:6789'.format(host_ip))
_auth = relation_get('auth', unit, relid)
if _auth:
auths.append(_auth)
if len(set(auths)) != 1:
e = ("Inconsistent or absent auth returned by mon units. Setting "
"auth_supported to 'none'")
@ -91,17 +187,28 @@ class MonContext(context.OSContextGenerator):
auth = 'none'
else:
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 = {
'auth_supported': auth,
'mon_hosts': ' '.join(hosts),
'hostname': socket.gethostname(),
'mon_hosts': ' '.join(mon_hosts),
'hostname': host,
'old_auth': cmp_pkgrevno('radosgw', "0.51") < 0,
'use_syslog': str(config('use-syslog')).lower(),
'embedded_webserver': config('use-embedded-webserver'),
'loglevel': config('loglevel'),
'port': determine_apache_port(config('port'),
singlenode_mode=True)
'port': port,
'ipv6': config('prefer-ipv6')
}
certs_path = '/var/lib/ceph/nss'
@ -121,17 +228,3 @@ class MonContext(context.OSContextGenerator):
return ctxt
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

View File

@ -1,17 +1,16 @@
#!/usr/bin/python
#
# Copyright 2012 Canonical Ltd.
# Copyright 2016 Canonical Ltd.
#
# Authors:
# James Page <james.page@ubuntu.com>
# Edward Hope-Morley <edward.hope-morley@canonical.com>
#
import shutil
import os
import subprocess
import sys
import glob
import os
import ceph
from charmhelpers.core.hookenv import (
@ -39,27 +38,17 @@ from charmhelpers.core.host import (
lsb_release,
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.core.host import (
cmp_pkgrevno,
mkdir,
)
from charmhelpers.contrib.network.ip import (
format_ipv6_addr,
get_ipv6_addr,
get_iface_for_address,
get_netmask_for_address,
is_ipv6,
)
from charmhelpers.contrib.openstack.ip import (
canonical_url,
@ -72,18 +61,17 @@ from charmhelpers.contrib.storage.linux.ceph import (
send_request_if_needed,
is_request_complete,
)
APACHE_PORTS_CONF = '/etc/apache2/ports.conf'
from utils import (
enable_pocket,
CEPHRG_HA_RES,
register_configs,
REQUIRED_INTERFACES,
check_optional_relations,
setup_ipv6,
)
hooks = Hooks()
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'
@ -145,43 +133,6 @@ def install():
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):
"""
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:
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()):
log("Missing relation settings (%s) - skipping cert setup" %
(', '.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']})
def config_changed():
install_packages()
CONFIGS.write_all()
if not config('use-embedded-webserver'):
status_set('maintenance', 'configuring apache')
emit_apacheconf()
install_www_scripts()
apache_sites()
apache_modules()
apache_ports()
apache_reload()
if config('prefer-ipv6'):
status_set('maintenance', 'configuring ipv6')
setup_ipv6()
for r_id in relation_ids('identity-service'):
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',
'mon-relation-changed')
@ -373,8 +337,18 @@ def identity_changed(relid=None):
restart()
@hooks.hook('cluster-relation-changed',
'cluster-relation-joined')
@hooks.hook('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']})
def cluster_changed():
CONFIGS.write_all()
@ -384,17 +358,12 @@ def cluster_changed():
@hooks.hook('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')
if not vip:
log('Unable to configure hacluster as vip not provided',
level=ERROR)
log('Unable to configure hacluster as vip not provided', level=ERROR)
sys.exit(1)
# Obtain resources
# SWIFT_HA_RES = 'grp_swift_vips'
resources = {
'res_cephrg_haproxy': 'lsb:haproxy'
}
@ -404,15 +373,25 @@ def ha_relation_joined():
vip_group = []
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)
netmask = get_netmask_for_address(vip)
if iface is not None:
vip_key = 'res_cephrg_{}_vip'.format(iface)
resources[vip_key] = 'ocf:heartbeat:IPaddr2'
resources[vip_key] = res_rgw_vip
resource_params[vip_key] = (
'params ip="{vip}" cidr_netmask="{netmask}"'
' nic="{iface}"'.format(vip=vip,
'params {ip}="{vip}" cidr_netmask="{netmask}"'
' nic="{iface}"'.format(ip=vip_params,
vip=vip,
iface=iface,
netmask=get_netmask_for_address(vip))
netmask=netmask)
)
vip_group.append(vip_key)
@ -426,6 +405,11 @@ def ha_relation_joined():
'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,
corosync_bindiface=corosync_bindiface,
corosync_mcastport=corosync_mcastport,

View File

@ -1,27 +1,45 @@
#
# Copyright 2012 Canonical Ltd.
# Copyright 2016 Canonical Ltd.
#
# Authors:
# James Page <james.page@ubuntu.com>
# Paul Collins <paul.collins@canonical.com>
# Edward Hope-Morley <edward.hope-morley@canonical.com>
#
import socket
import re
import os
import dns.resolver
import re
import jinja2
from copy import deepcopy
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
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
# list has a complete context.
REQUIRED_INTERFACES = {
@ -32,6 +50,9 @@ TEMPLATES_DIR = 'templates'
TEMPLATES = 'templates/'
HAPROXY_CONF = '/etc/haproxy/haproxy.cfg'
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([
(HAPROXY_CONF, {
@ -39,6 +60,18 @@ BASE_RESOURCE_MAP = OrderedDict([
ceph_radosgw_context.HAProxyContext()],
'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, {
'contexts': [ceph_radosgw_context.MonContext()],
'services': ['radosgw'],
@ -51,6 +84,11 @@ def resource_map():
Dynamically generate a map of resources that will be managed for a single
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)
return resource_map
@ -92,28 +130,6 @@ def enable_pocket(pocket):
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):
required_interfaces = {}
if relation_ids('ha'):
@ -132,3 +148,18 @@ def check_optional_relations(configs):
return status_get()
else:
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)

View File

@ -11,6 +11,9 @@ log to syslog = {{ use_syslog }}
err to syslog = {{ use_syslog }}
clog to syslog = {{ use_syslog }}
debug rgw = {{ loglevel }}/5
{% if ipv6 -%}
ms bind ipv6 = true
{% endif %}
[client.radosgw.gateway]
host = {{ hostname }}

25
templates/rgw.conf Normal file
View 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&params=$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>

View File

@ -13,6 +13,7 @@ TO_PATCH = [
'related_units',
'cmp_pkgrevno',
'socket',
'is_apache_24',
]
@ -147,8 +148,9 @@ class MonContextTest(CharmTestCase):
super(MonContextTest, self).setUp(context, TO_PATCH)
self.config.side_effect = self.test_config.get
def test_ctxt(self):
self.socket.gethostname.return_value = '10.0.0.10'
@patch.object(context, 'ensure_host_resolvable_v6')
def test_ctxt(self, mock_ensure_rsv_v6):
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
@ -157,6 +159,7 @@ class MonContextTest(CharmTestCase):
return addresses.pop()
elif attr == 'auth':
return 'cephx'
self.relation_get.side_effect = _relation_get
self.relation_ids.return_value = ['mon:6']
self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
@ -164,17 +167,26 @@ class MonContextTest(CharmTestCase):
'auth_supported': 'cephx',
'embedded_webserver': False,
'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',
'old_auth': False,
'use_syslog': 'false',
'loglevel': 1,
'port': 70
'port': 70,
'ipv6': False
}
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):
self.socket.gethostname.return_value = '10.0.0.10'
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
self.relation_get.return_value = None
self.relation_ids.return_value = ['mon:6']
@ -182,7 +194,7 @@ class MonContextTest(CharmTestCase):
self.assertEqual({}, mon_ctxt())
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()
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
auths = ['cephx', 'cephy', 'cephz']
@ -199,17 +211,18 @@ class MonContextTest(CharmTestCase):
'auth_supported': 'none',
'embedded_webserver': False,
'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',
'old_auth': False,
'use_syslog': 'false',
'loglevel': 1,
'port': 70
'port': 70,
'ipv6': False
}
self.assertEqual(expect, mon_ctxt())
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()
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
auths = ['cephx', 'cephx', 'cephx']
@ -226,11 +239,19 @@ class MonContextTest(CharmTestCase):
'auth_supported': 'cephx',
'embedded_webserver': False,
'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',
'old_auth': False,
'use_syslog': 'false',
'loglevel': 1,
'port': 70
'port': 70,
'ipv6': False
}
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

View File

@ -6,7 +6,6 @@ from mock import (
from test_utils import (
CharmTestCase,
patch_open
)
from charmhelpers.contrib.openstack.ip import PUBLIC
@ -34,8 +33,6 @@ TO_PATCH = [
'enable_pocket',
'get_iface_for_address',
'get_netmask_for_address',
'glob',
'is_apache_24',
'log',
'lsb_release',
'open_port',
@ -44,8 +41,6 @@ TO_PATCH = [
'relation_set',
'relation_get',
'related_units',
'render_template',
'shutil',
'status_set',
'subprocess',
'sys',
@ -62,11 +57,6 @@ class CephRadosGWTests(CharmTestCase):
self.test_config.set('key', 'secretkey')
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):
self.lsb_release.return_value = {'DISTRIB_CODENAME': 'vivid'}
fastcgi_source = (
@ -122,69 +112,12 @@ class CephRadosGWTests(CharmTestCase):
self.enable_pocket.assert_called_with('multiverse')
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)
def test_config_changed(self):
_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()
self.assertTrue(_install_packages.called)
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',
lambda *args, **kwargs: True)