diff --git a/.project b/.project
new file mode 100644
index 00000000..5a0efccc
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ openstack-dashboard
+
+
+
+
+
+ org.python.pydev.PyDevBuilder
+
+
+
+
+
+ org.python.pydev.pythonNature
+
+
diff --git a/.pydevproject b/.pydevproject
new file mode 100644
index 00000000..ee88aa82
--- /dev/null
+++ b/.pydevproject
@@ -0,0 +1,9 @@
+
+
+python 2.7
+Default
+
+/openstack-dashboard/hooks
+/openstack-dashboard/unit_tests
+
+
diff --git a/charm-helpers-sync.yaml b/charm-helpers-sync.yaml
index 32f99bf5..ffc1471b 100644
--- a/charm-helpers-sync.yaml
+++ b/charm-helpers-sync.yaml
@@ -6,4 +6,5 @@ include:
- contrib.openstack
- contrib.storage.linux
- contrib.hahelpers
+ - contrib.storage
- payload.execd
diff --git a/config.yaml b/config.yaml
index 045713ad..83d55772 100644
--- a/config.yaml
+++ b/config.yaml
@@ -26,6 +26,12 @@ options:
description: |
Default role for Horizon operations that will be created in
Keystone upon introduction of an identity-service relation.
+ use-syslog:
+ type: boolean
+ default: False
+ description: |
+ By default, all services will log into their corresponding log files.
+ Setting this to True will force all services to log to the syslog.
vip:
type: string
description: "Virtual IP to use to front openstack dashboard ha configuration"
@@ -80,4 +86,4 @@ options:
description: Use Ubuntu theme for the dashboard.
secret:
type: string
- descriptions: Secret for Horizon to use when securing internal data; set this when using multiple dashboard units.
+ description: Secret for Horizon to use when securing internal data; set this when using multiple dashboard units.
diff --git a/hooks/charmhelpers/contrib/hahelpers/cluster.py b/hooks/charmhelpers/contrib/hahelpers/cluster.py
index 074855f4..bf832f7d 100644
--- a/hooks/charmhelpers/contrib/hahelpers/cluster.py
+++ b/hooks/charmhelpers/contrib/hahelpers/cluster.py
@@ -126,17 +126,17 @@ def determine_api_port(public_port):
return public_port - (i * 10)
-def determine_haproxy_port(public_port):
+def determine_apache_port(public_port):
'''
- Description: Determine correct proxy listening port based on public IP +
- existence of HTTPS reverse proxy.
+ Description: Determine correct apache listening port based on public IP +
+ state of the cluster.
public_port: int: standard public port for given service
returns: int: the correct listening port for the HAProxy service
'''
i = 0
- if https():
+ if len(peer_units()) > 0 or is_clustered():
i += 1
return public_port - (i * 10)
diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py
index 8a982ffa..81ac12b6 100644
--- a/hooks/charmhelpers/contrib/openstack/context.py
+++ b/hooks/charmhelpers/contrib/openstack/context.py
@@ -23,15 +23,12 @@ from charmhelpers.core.hookenv import (
unit_get,
unit_private_ip,
ERROR,
- WARNING,
)
from charmhelpers.contrib.hahelpers.cluster import (
+ determine_apache_port,
determine_api_port,
- determine_haproxy_port,
https,
- is_clustered,
- peer_units,
)
from charmhelpers.contrib.hahelpers.apache import (
@@ -68,6 +65,43 @@ def context_complete(ctxt):
return True
+def config_flags_parser(config_flags):
+ if config_flags.find('==') >= 0:
+ log("config_flags is not in expected format (key=value)",
+ level=ERROR)
+ raise OSContextError
+ # strip the following from each value.
+ post_strippers = ' ,'
+ # we strip any leading/trailing '=' or ' ' from the string then
+ # split on '='.
+ split = config_flags.strip(' =').split('=')
+ limit = len(split)
+ flags = {}
+ for i in xrange(0, limit - 1):
+ current = split[i]
+ next = split[i + 1]
+ vindex = next.rfind(',')
+ if (i == limit - 2) or (vindex < 0):
+ value = next
+ else:
+ value = next[:vindex]
+
+ if i == 0:
+ key = current
+ else:
+ # if this not the first entry, expect an embedded key.
+ index = current.rfind(',')
+ if index < 0:
+ log("invalid config value(s) at index %s" % (i),
+ level=ERROR)
+ raise OSContextError
+ key = current[index + 1:]
+
+ # Add to collection.
+ flags[key.strip(post_strippers)] = value.rstrip(post_strippers)
+ return flags
+
+
class OSContextGenerator(object):
interfaces = []
@@ -182,10 +216,17 @@ class AMQPContext(OSContextGenerator):
# Sufficient information found = break out!
break
# Used for active/active rabbitmq >= grizzly
- ctxt['rabbitmq_hosts'] = []
- for unit in related_units(rid):
- ctxt['rabbitmq_hosts'].append(relation_get('private-address',
- rid=rid, unit=unit))
+ if ('clustered' not in ctxt or relation_get('ha-vip-only') == 'True') and \
+ len(related_units(rid)) > 1:
+ if relation_get('ha_queues'):
+ ctxt['rabbitmq_ha_queues'] = relation_get('ha_queues')
+ else:
+ ctxt['rabbitmq_ha_queues'] = False
+ rabbitmq_hosts = []
+ for unit in related_units(rid):
+ rabbitmq_hosts.append(relation_get('private-address',
+ rid=rid, unit=unit))
+ ctxt['rabbitmq_hosts'] = ','.join(rabbitmq_hosts)
if not context_complete(ctxt):
return {}
else:
@@ -209,11 +250,13 @@ class CephContext(OSContextGenerator):
unit=unit))
auth = relation_get('auth', rid=rid, unit=unit)
key = relation_get('key', rid=rid, unit=unit)
+ use_syslog = str(config('use-syslog')).lower()
ctxt = {
'mon_hosts': ' '.join(mon_hosts),
'auth': auth,
'key': key,
+ 'use_syslog': use_syslog
}
if not os.path.isdir('/etc/ceph'):
@@ -286,6 +329,7 @@ class ImageServiceContext(OSContextGenerator):
class ApacheSSLContext(OSContextGenerator):
+
"""
Generates a context for an apache vhost configuration that configures
HTTPS reverse proxying for one or many endpoints. Generated context
@@ -341,11 +385,9 @@ class ApacheSSLContext(OSContextGenerator):
'private_address': unit_get('private-address'),
'endpoints': []
}
- for ext_port in self.external_ports:
- if peer_units() or is_clustered():
- int_port = determine_haproxy_port(ext_port)
- else:
- int_port = determine_api_port(ext_port)
+ for api_port in self.external_ports:
+ ext_port = determine_apache_port(api_port)
+ int_port = determine_api_port(api_port)
portmap = (int(ext_port), int(int_port))
ctxt['endpoints'].append(portmap)
return ctxt
@@ -428,33 +470,37 @@ class NeutronContext(object):
elif self.plugin == 'nvp':
ctxt.update(self.nvp_ctxt())
+ alchemy_flags = config('neutron-alchemy-flags')
+ if alchemy_flags:
+ flags = config_flags_parser(alchemy_flags)
+ ctxt['neutron_alchemy_flags'] = flags
+
self._save_flag_file()
return ctxt
class OSConfigFlagContext(OSContextGenerator):
- '''
- Responsible adding user-defined config-flags in charm config to a
- to a template context.
- '''
+
+ """
+ Responsible for adding user-defined config-flags in charm config to a
+ template context.
+
+ NOTE: the value of config-flags may be a comma-separated list of
+ key=value pairs and some Openstack config files support
+ comma-separated lists as values.
+ """
+
def __call__(self):
config_flags = config('config-flags')
- if not config_flags or config_flags in ['None', '']:
+ if not config_flags:
return {}
- config_flags = config_flags.split(',')
- flags = {}
- for flag in config_flags:
- if '=' not in flag:
- log('Improperly formatted config-flag, expected k=v '
- 'got %s' % flag, level=WARNING)
- continue
- k, v = flag.split('=')
- flags[k.strip()] = v
- ctxt = {'user_config_flags': flags}
- return ctxt
+
+ flags = config_flags_parser(config_flags)
+ return {'user_config_flags': flags}
class SubordinateConfigContext(OSContextGenerator):
+
"""
Responsible for inspecting relations to subordinates that
may be exporting required config via a json blob.
@@ -495,6 +541,7 @@ class SubordinateConfigContext(OSContextGenerator):
}
"""
+
def __init__(self, service, config_file, interface):
"""
:param service : Service name key to query in any subordinate
@@ -539,3 +586,12 @@ class SubordinateConfigContext(OSContextGenerator):
ctxt['sections'] = {}
return ctxt
+
+
+class SyslogContext(OSContextGenerator):
+
+ def __call__(self):
+ ctxt = {
+ 'use_syslog': config('use-syslog')
+ }
+ return ctxt
diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py
index 43c7df3e..56d04245 100644
--- a/hooks/charmhelpers/contrib/openstack/utils.py
+++ b/hooks/charmhelpers/contrib/openstack/utils.py
@@ -202,7 +202,7 @@ def os_release(package, base='essex'):
def import_key(keyid):
- cmd = "apt-key adv --keyserver keyserver.ubuntu.com " \
+ cmd = "apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 " \
"--recv-keys %s" % keyid
try:
subprocess.check_call(cmd.split(' '))
@@ -415,7 +415,7 @@ def get_host_ip(hostname):
return ns_query(hostname)
-def get_hostname(address):
+def get_hostname(address, fqdn=True):
"""
Resolves hostname for given IP, or returns the input
if it is already a hostname.
@@ -434,7 +434,11 @@ def get_hostname(address):
if not result:
return None
- # strip trailing .
- if result.endswith('.'):
- return result[:-1]
- return result
+ if fqdn:
+ # strip trailing .
+ if result.endswith('.'):
+ return result[:-1]
+ else:
+ return result
+ else:
+ return result.split('.')[0]
diff --git a/hooks/charmhelpers/contrib/storage/linux/ceph.py b/hooks/charmhelpers/contrib/storage/linux/ceph.py
index 69b879ca..12417410 100644
--- a/hooks/charmhelpers/contrib/storage/linux/ceph.py
+++ b/hooks/charmhelpers/contrib/storage/linux/ceph.py
@@ -49,6 +49,9 @@ CEPH_CONF = """[global]
auth supported = {auth}
keyring = {keyring}
mon host = {mon_hosts}
+ log to syslog = {use_syslog}
+ err to syslog = {use_syslog}
+ clog to syslog = {use_syslog}
"""
@@ -194,7 +197,7 @@ def get_ceph_nodes():
return hosts
-def configure(service, key, auth):
+def configure(service, key, auth, use_syslog):
''' Perform basic configuration of Ceph '''
create_keyring(service, key)
create_key_file(service, key)
@@ -202,7 +205,8 @@ def configure(service, key, auth):
with open('/etc/ceph/ceph.conf', 'w') as ceph_conf:
ceph_conf.write(CEPH_CONF.format(auth=auth,
keyring=_keyring_path(service),
- mon_hosts=",".join(map(str, hosts))))
+ mon_hosts=",".join(map(str, hosts)),
+ use_syslog=use_syslog))
modprobe('rbd')
diff --git a/hooks/charmhelpers/contrib/storage/linux/utils.py b/hooks/charmhelpers/contrib/storage/linux/utils.py
index 5b9b6d47..c40218f0 100644
--- a/hooks/charmhelpers/contrib/storage/linux/utils.py
+++ b/hooks/charmhelpers/contrib/storage/linux/utils.py
@@ -22,4 +22,4 @@ def zap_disk(block_device):
:param block_device: str: Full path of block device to clean.
'''
- check_call(['sgdisk', '--zap-all', block_device])
+ check_call(['sgdisk', '--zap-all', '--mbrtogpt', block_device])
diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py
index bb196dfa..505c202d 100644
--- a/hooks/charmhelpers/core/hookenv.py
+++ b/hooks/charmhelpers/core/hookenv.py
@@ -8,6 +8,7 @@ import os
import json
import yaml
import subprocess
+import sys
import UserDict
from subprocess import CalledProcessError
@@ -149,6 +150,11 @@ def service_name():
return local_unit().split('/')[0]
+def hook_name():
+ """The name of the currently executing hook"""
+ return os.path.basename(sys.argv[0])
+
+
@cached
def config(scope=None):
"""Juju charm configuration"""
diff --git a/hooks/charmhelpers/core/host.py b/hooks/charmhelpers/core/host.py
index c8c81b28..cfd26847 100644
--- a/hooks/charmhelpers/core/host.py
+++ b/hooks/charmhelpers/core/host.py
@@ -194,7 +194,7 @@ def file_hash(path):
return None
-def restart_on_change(restart_map):
+def restart_on_change(restart_map, stopstart=False):
"""Restart services based on configuration files changing
This function is used a decorator, for example
@@ -219,8 +219,14 @@ def restart_on_change(restart_map):
for path in restart_map:
if checksums[path] != file_hash(path):
restarts += restart_map[path]
- for service_name in list(OrderedDict.fromkeys(restarts)):
- service('restart', service_name)
+ services_list = list(OrderedDict.fromkeys(restarts))
+ if not stopstart:
+ for service_name in services_list:
+ service('restart', service_name)
+ else:
+ for action in ['stop', 'start']:
+ for service_name in services_list:
+ service(action, service_name)
return wrapped_f
return wrap
diff --git a/hooks/charmhelpers/fetch/__init__.py b/hooks/charmhelpers/fetch/__init__.py
index 1f4f6315..07bb707d 100644
--- a/hooks/charmhelpers/fetch/__init__.py
+++ b/hooks/charmhelpers/fetch/__init__.py
@@ -136,7 +136,7 @@ def apt_hold(packages, fatal=False):
def add_source(source, key=None):
if (source.startswith('ppa:') or
- source.startswith('http:') or
+ source.startswith('http') or
source.startswith('deb ') or
source.startswith('cloud-archive:')):
subprocess.check_call(['add-apt-repository', '--yes', source])
@@ -156,7 +156,9 @@ def add_source(source, key=None):
with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
apt.write(PROPOSED_POCKET.format(release))
if key:
- subprocess.check_call(['apt-key', 'import', key])
+ subprocess.check_call(['apt-key', 'adv', '--keyserver',
+ 'keyserver.ubuntu.com', '--recv',
+ key])
class SourceConfigError(Exception):
diff --git a/hooks/horizon_hooks.py b/hooks/horizon_hooks.py
index 22a3a1dc..ac4e3b19 100755
--- a/hooks/horizon_hooks.py
+++ b/hooks/horizon_hooks.py
@@ -34,6 +34,7 @@ from horizon_utils import (
from charmhelpers.contrib.hahelpers.apache import install_ca_cert
from charmhelpers.contrib.hahelpers.cluster import get_hacluster_config
from charmhelpers.payload.execd import execd_preinstall
+from base64 import b64decode
hooks = Hooks()
CONFIGS = register_configs()
@@ -95,7 +96,7 @@ def keystone_joined(rel_id=None):
def keystone_changed():
CONFIGS.write(LOCAL_SETTINGS)
if relation_get('ca_cert'):
- install_ca_cert(relation_get('ca_cert'))
+ install_ca_cert(b64decode(relation_get('ca_cert')))
@hooks.hook('cluster-relation-departed',
diff --git a/hooks/horizon_utils.py b/hooks/horizon_utils.py
index 62c1e1b7..806b64fc 100644
--- a/hooks/horizon_utils.py
+++ b/hooks/horizon_utils.py
@@ -1,6 +1,7 @@
# vim: set ts=4:et
import horizon_contexts
import charmhelpers.contrib.openstack.templating as templating
+import charmhelpers.contrib.openstack.context as context
import subprocess
import os
from collections import OrderedDict
@@ -38,15 +39,18 @@ TEMPLATES = 'templates'
CONFIG_FILES = OrderedDict([
(LOCAL_SETTINGS, {
'hook_contexts': [horizon_contexts.HorizonContext(),
- horizon_contexts.IdentityServiceContext()],
+ horizon_contexts.IdentityServiceContext(),
+ context.SyslogContext()],
'services': ['apache2']
}),
(APACHE_CONF, {
- 'hook_contexts': [horizon_contexts.HorizonContext()],
+ 'hook_contexts': [horizon_contexts.HorizonContext(),
+ context.SyslogContext()],
'services': ['apache2'],
}),
(APACHE_24_CONF, {
- 'hook_contexts': [horizon_contexts.HorizonContext()],
+ 'hook_contexts': [horizon_contexts.HorizonContext(),
+ context.SyslogContext()],
'services': ['apache2'],
}),
(APACHE_SSL, {
diff --git a/revision b/revision
index e85087af..f5c89552 100644
--- a/revision
+++ b/revision
@@ -1 +1 @@
-31
+32
diff --git a/templates/grizzly/local_settings.py b/templates/grizzly/local_settings.py
index 87c79228..af4bf198 100644
--- a/templates/grizzly/local_settings.py
+++ b/templates/grizzly/local_settings.py
@@ -1,8 +1,10 @@
import os
from django.utils.translation import ugettext_lazy as _
-
from openstack_dashboard import exceptions
+{% if use_syslog %}
+from logging.handlers import SysLogHandler
+{% endif %}
DEBUG = {{ debug }}
TEMPLATE_DEBUG = DEBUG
@@ -176,6 +178,12 @@ LOGGING = {
'level': 'DEBUG',
'class': 'django.utils.log.NullHandler',
},
+ {% if use_syslog %}
+ 'syslog': {
+ 'level': 'INFO',
+ 'class': 'logging.handlers.SysLogHandler',
+ },
+ {% endif %}
'console': {
# Set the level to "DEBUG" for verbose output logging.
'level': 'INFO',
@@ -194,27 +202,59 @@ LOGGING = {
'propagate': False,
},
'horizon': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'openstack_dashboard': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
+ 'propagate': False,
+ },
+ 'openstack_auth': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
+ 'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'novaclient': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'keystoneclient': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'glanceclient': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'nose.plugins.manager': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
}
}
diff --git a/templates/havana/local_settings.py b/templates/havana/local_settings.py
index a04f153c..0f9e0c95 100644
--- a/templates/havana/local_settings.py
+++ b/templates/havana/local_settings.py
@@ -1,6 +1,9 @@
import os
from django.utils.translation import ugettext_lazy as _
+{% if use_syslog %}
+from logging.handlers import SysLogHandler
+{% endif %}
from openstack_dashboard import exceptions
@@ -248,6 +251,12 @@ LOGGING = {
'level': 'INFO',
'class': 'logging.StreamHandler',
},
+ {% if use_syslog %}
+ 'syslog': {
+ 'level': 'INFO',
+ 'class': 'logging.handlers.SysLogHandler',
+ }
+ {% endif %}
},
'loggers': {
# Logging from django.db.backends is VERY verbose, send to null
@@ -261,35 +270,75 @@ LOGGING = {
'propagate': False,
},
'horizon': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'openstack_dashboard': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
+ 'propagate': False,
+ },
+ 'openstack_auth': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
+ 'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'novaclient': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'cinderclient': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'keystoneclient': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'glanceclient': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'heatclient': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
},
'nose.plugins.manager': {
+ {% if use_syslog %}
+ 'handlers': ['syslog'],
+ {% else %}
'handlers': ['console'],
+ {% endif %}
'propagate': False,
}
}
diff --git a/unit_tests/test_horizon_contexts.py b/unit_tests/test_horizon_contexts.py
index 69487b63..451eb329 100644
--- a/unit_tests/test_horizon_contexts.py
+++ b/unit_tests/test_horizon_contexts.py
@@ -40,6 +40,7 @@ def patch_open():
class TestHorizonContexts(CharmTestCase):
+
def setUp(self):
super(TestHorizonContexts, self).setUp(horizon_contexts, TO_PATCH)
self.config.side_effect = self.test_config.get
@@ -67,7 +68,7 @@ class TestHorizonContexts(CharmTestCase):
call('key')
])
# Security check on key permissions
- _chmod.assert_called_with('/etc/ssl/private/dashboard.key', 0600)
+ _chmod.assert_called_with('/etc/ssl/private/dashboard.key', 0o600)
def test_ApacheSSLContext_disabled(self):
self.get_cert.return_value = (None, None)
@@ -78,42 +79,48 @@ class TestHorizonContexts(CharmTestCase):
self.assertEquals(horizon_contexts.HorizonContext()(),
{'compress_offline': True, 'debug': False,
'default_role': 'Member', 'webroot': '/horizon',
- 'ubuntu_theme': True, 'secret': 'secret'})
+ 'ubuntu_theme': True,
+ 'secret': 'secret'})
def test_HorizonContext_debug(self):
self.test_config.set('debug', 'yes')
self.assertEquals(horizon_contexts.HorizonContext()(),
{'compress_offline': True, 'debug': True,
'default_role': 'Member', 'webroot': '/horizon',
- 'ubuntu_theme': True, 'secret': 'secret'})
+ 'ubuntu_theme': True,
+ 'secret': 'secret'})
def test_HorizonContext_theme(self):
self.test_config.set('ubuntu-theme', False)
self.assertEquals(horizon_contexts.HorizonContext()(),
{'compress_offline': True, 'debug': False,
'default_role': 'Member', 'webroot': '/horizon',
- 'ubuntu_theme': False, 'secret': 'secret'})
+ 'ubuntu_theme': False,
+ 'secret': 'secret'})
def test_HorizonContext_compression(self):
self.test_config.set('offline-compression', 'no')
self.assertEquals(horizon_contexts.HorizonContext()(),
{'compress_offline': False, 'debug': False,
'default_role': 'Member', 'webroot': '/horizon',
- 'ubuntu_theme': True, 'secret': 'secret'})
+ 'ubuntu_theme': True,
+ 'secret': 'secret'})
def test_HorizonContext_role(self):
self.test_config.set('default-role', 'foo')
self.assertEquals(horizon_contexts.HorizonContext()(),
{'compress_offline': True, 'debug': False,
'default_role': 'foo', 'webroot': '/horizon',
- 'ubuntu_theme': True, 'secret': 'secret'})
+ 'ubuntu_theme': True,
+ 'secret': 'secret'})
def test_HorizonContext_webroot(self):
self.test_config.set('webroot', '/')
self.assertEquals(horizon_contexts.HorizonContext()(),
{'compress_offline': True, 'debug': False,
'default_role': 'Member', 'webroot': '/',
- 'ubuntu_theme': True, 'secret': 'secret'})
+ 'ubuntu_theme': True,
+ 'secret': 'secret'})
def test_IdentityServiceContext_not_related(self):
self.relation_ids.return_value = []
diff --git a/unit_tests/test_horizon_hooks.py b/unit_tests/test_horizon_hooks.py
index b830cd23..da52641e 100644
--- a/unit_tests/test_horizon_hooks.py
+++ b/unit_tests/test_horizon_hooks.py
@@ -27,7 +27,12 @@ TO_PATCH = [
'install_ca_cert',
'unit_get',
'log',
- 'execd_preinstall']
+ 'execd_preinstall',
+ 'b64decode']
+
+
+def passthrough(value):
+ return value
class TestHorizonHooks(CharmTestCase):
@@ -35,6 +40,7 @@ class TestHorizonHooks(CharmTestCase):
def setUp(self):
super(TestHorizonHooks, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get
+ self.b64decode.side_effect = passthrough
def _call_hook(self, hookname):
hooks.hooks.execute([
diff --git a/unit_tests/test_horizon_utils.py b/unit_tests/test_horizon_utils.py
index fb0ec540..bad87eae 100644
--- a/unit_tests/test_horizon_utils.py
+++ b/unit_tests/test_horizon_utils.py
@@ -20,6 +20,7 @@ TO_PATCH = [
class TestHorizonUtils(CharmTestCase):
+
def setUp(self):
super(TestHorizonUtils, self).setUp(horizon_utils, TO_PATCH)
diff --git a/unit_tests/test_utils.py b/unit_tests/test_utils.py
index fd7fe233..e04a9dc8 100644
--- a/unit_tests/test_utils.py
+++ b/unit_tests/test_utils.py
@@ -44,6 +44,7 @@ def get_default_config():
class CharmTestCase(unittest.TestCase):
+
def setUp(self, obj, patches):
super(CharmTestCase, self).setUp()
self.patches = patches
@@ -64,6 +65,7 @@ class CharmTestCase(unittest.TestCase):
class TestConfig(object):
+
def __init__(self):
self.config = get_default_config()
@@ -83,6 +85,7 @@ class TestConfig(object):
class TestRelation(object):
+
def __init__(self, relation_data={}):
self.relation_data = relation_data