diff --git a/charmhelpers/contrib/openstack/context.py b/charmhelpers/contrib/openstack/context.py index 91ddcec..3078076 100644 --- a/charmhelpers/contrib/openstack/context.py +++ b/charmhelpers/contrib/openstack/context.py @@ -14,6 +14,7 @@ import glob import json +import math import os import re import time @@ -90,6 +91,8 @@ from charmhelpers.contrib.network.ip import ( from charmhelpers.contrib.openstack.utils import ( config_flags_parser, get_host_ip, + git_determine_usr_bin, + git_determine_python_path, enable_memcache, ) from charmhelpers.core.unitdata import kv @@ -1208,6 +1211,43 @@ class WorkerConfigContext(OSContextGenerator): return ctxt +class WSGIWorkerConfigContext(WorkerConfigContext): + + def __init__(self, name=None, script=None, admin_script=None, + public_script=None, process_weight=1.00, + admin_process_weight=0.75, public_process_weight=0.25): + self.service_name = name + self.user = name + self.group = name + self.script = script + self.admin_script = admin_script + self.public_script = public_script + self.process_weight = process_weight + self.admin_process_weight = admin_process_weight + self.public_process_weight = public_process_weight + + def __call__(self): + multiplier = config('worker-multiplier') or 1 + total_processes = self.num_cpus * multiplier + ctxt = { + "service_name": self.service_name, + "user": self.user, + "group": self.group, + "script": self.script, + "admin_script": self.admin_script, + "public_script": self.public_script, + "processes": int(math.ceil(self.process_weight * total_processes)), + "admin_processes": int(math.ceil(self.admin_process_weight * + total_processes)), + "public_processes": int(math.ceil(self.public_process_weight * + total_processes)), + "threads": 1, + "usr_bin": git_determine_usr_bin(), + "python_path": git_determine_python_path(), + } + return ctxt + + class ZeroMQContext(OSContextGenerator): interfaces = ['zeromq-configuration'] diff --git a/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf b/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf new file mode 100644 index 0000000..315b2a3 --- /dev/null +++ b/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf @@ -0,0 +1,100 @@ +# Configuration file maintained by Juju. Local changes may be overwritten. + +{% if port -%} +Listen {{ port }} +{% endif -%} + +{% if admin_port -%} +Listen {{ admin_port }} +{% endif -%} + +{% if public_port -%} +Listen {{ public_port }} +{% endif -%} + +{% if port -%} + + WSGIDaemonProcess {{ service_name }} processes={{ processes }} threads={{ threads }} user={{ service_name }} group={{ service_name }} \ +{% if python_path -%} + python-path={{ python_path }} \ +{% endif -%} + display-name=%{GROUP} + WSGIProcessGroup {{ service_name }} + WSGIScriptAlias / {{ script }} + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + = 2.4> + ErrorLogFormat "%{cu}t %M" + + ErrorLog /var/log/apache2/{{ service_name }}_error.log + CustomLog /var/log/apache2/{{ service_name }}_access.log combined + + + = 2.4> + Require all granted + + + Order allow,deny + Allow from all + + + +{% endif -%} + +{% if admin_port -%} + + WSGIDaemonProcess {{ service_name }}-admin processes={{ admin_processes }} threads={{ threads }} user={{ service_name }} group={{ service_name }} \ +{% if python_path -%} + python-path={{ python_path }} \ +{% endif -%} + display-name=%{GROUP} + WSGIProcessGroup {{ service_name }}-admin + WSGIScriptAlias / {{ admin_script }} + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + = 2.4> + ErrorLogFormat "%{cu}t %M" + + ErrorLog /var/log/apache2/{{ service_name }}_error.log + CustomLog /var/log/apache2/{{ service_name }}_access.log combined + + + = 2.4> + Require all granted + + + Order allow,deny + Allow from all + + + +{% endif -%} + +{% if public_port -%} + + WSGIDaemonProcess {{ service_name }}-public processes={{ public_processes }} threads={{ threads }} user={{ service_name }} group={{ service_name }} \ +{% if python_path -%} + python-path={{ python_path }} \ +{% endif -%} + display-name=%{GROUP} + WSGIProcessGroup {{ service_name }}-public + WSGIScriptAlias / {{ public_script }} + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + = 2.4> + ErrorLogFormat "%{cu}t %M" + + ErrorLog /var/log/apache2/{{ service_name }}_error.log + CustomLog /var/log/apache2/{{ service_name }}_access.log combined + + + = 2.4> + Require all granted + + + Order allow,deny + Allow from all + + + +{% endif -%} diff --git a/charmhelpers/contrib/openstack/utils.py b/charmhelpers/contrib/openstack/utils.py index 59f9f51..3d47d81 100644 --- a/charmhelpers/contrib/openstack/utils.py +++ b/charmhelpers/contrib/openstack/utils.py @@ -1119,6 +1119,35 @@ def git_generate_systemd_init_files(templates_dir): shutil.copyfile(service_source, service_dest) +def git_determine_usr_bin(): + """Return the /usr/bin path for Apache2 config. + + The /usr/bin path will be located in the virtualenv if the charm + is configured to deploy from source. + """ + if git_install_requested(): + projects_yaml = config('openstack-origin-git') + projects_yaml = git_default_repos(projects_yaml) + return os.path.join(git_pip_venv_dir(projects_yaml), 'bin') + else: + return '/usr/bin' + + +def git_determine_python_path(): + """Return the python-path for Apache2 config. + + Returns 'None' unless the charm is configured to deploy from source, + in which case the path of the virtualenv's site-packages is returned. + """ + if git_install_requested(): + projects_yaml = config('openstack-origin-git') + projects_yaml = git_default_repos(projects_yaml) + return os.path.join(git_pip_venv_dir(projects_yaml), + 'lib/python2.7/site-packages') + else: + return None + + def os_workload_status(configs, required_interfaces, charm_func=None): """ Decorator to set workload status based on complete contexts diff --git a/config.yaml b/config.yaml index 45342db..a2f391b 100644 --- a/config.yaml +++ b/config.yaml @@ -47,10 +47,6 @@ options: NOTE: updating this setting to a source that is known to provide a later version of OpenStack will trigger a software upgrade. - - NOTE: when openstack-origin-git is specified, openstack specific - packages will be installed from source rather than from the - openstack-origin repository. region: default: RegionOne type: string diff --git a/hooks/ceilometer_hooks.py b/hooks/ceilometer_hooks.py index 7f18c0a..c325fb4 100755 --- a/hooks/ceilometer_hooks.py +++ b/hooks/ceilometer_hooks.py @@ -52,6 +52,7 @@ from charmhelpers.contrib.openstack.ha.utils import ( update_dns_ha_resource_params, ) from ceilometer_utils import ( + disable_package_apache_site, get_packages, CEILOMETER_DB, CEILOMETER_SERVICE, @@ -59,6 +60,7 @@ from ceilometer_utils import ( CEILOMETER_API_SYSTEMD_CONF, register_configs, restart_map, + run_in_apache, services, get_ceilometer_context, get_shared_secret, @@ -114,6 +116,8 @@ def install(): # NOTE(jamespage): ensure systemd override folder exists prior to # attempting to write override.conf mkdir(os.path.dirname(CEILOMETER_API_SYSTEMD_CONF)) + if run_in_apache(): + disable_package_apache_site() @hooks.hook("amqp-relation-joined") diff --git a/lib/ceilometer_contexts.py b/lib/ceilometer_contexts.py index 9d7fe2c..26c67d2 100644 --- a/lib/ceilometer_contexts.py +++ b/lib/ceilometer_contexts.py @@ -46,7 +46,7 @@ class MongoDBContext(OSContextGenerator): def __call__(self): mongo_servers = [] replset = None - use_replset = os_release('ceilometer-api') >= 'icehouse' + use_replset = os_release('ceilometer-common') >= 'icehouse' for relid in relation_ids('shared-db'): rel_units = related_units(relid) diff --git a/lib/ceilometer_utils.py b/lib/ceilometer_utils.py index cf04d33..3203c4d 100644 --- a/lib/ceilometer_utils.py +++ b/lib/ceilometer_utils.py @@ -34,6 +34,7 @@ from charmhelpers.contrib.openstack.utils import ( get_os_codename_package, get_os_codename_install_source, configure_installation_source, + os_release, pause_unit, resume_unit, make_assess_status_func, @@ -104,6 +105,8 @@ REQUIRED_INTERFACES = { CEILOMETER_ROLE = "ResellerAdmin" SVC = 'ceilometer' +WSGI_CEILOMETER_API_CONF = '/etc/apache2/sites-enabled/wsgi-openstack-api.conf' +PACKAGE_CEILOMETER_API_CONF = '/etc/apache2/sites-enabled/ceilometer-api.conf' CONFIG_FILES = OrderedDict([ (CEILOMETER_CONF, { @@ -178,6 +181,14 @@ def register_configs(): CONFIG_FILES[HTTPS_APACHE_CONF]['hook_contexts']) if enable_memcache(release=release): configs.register(MEMCACHED_CONF, [context.MemcacheContext()]) + + if run_in_apache(): + wsgi_script = "/usr/share/ceilometer/app.wsgi" + configs.register(WSGI_CEILOMETER_API_CONF, + [context.WSGIWorkerConfigContext(name="ceilometer", + script=wsgi_script), + CeilometerContext(), + HAProxyContext()]) return configs @@ -203,6 +214,15 @@ def restart_map(): if enable_memcache(source=config('openstack-origin')): _map[MEMCACHED_CONF] = ['memcached'] + if run_in_apache(): + for cfile in _map: + svcs = _map[cfile] + if 'ceilometer-api' in svcs: + svcs.remove('ceilometer-api') + if 'apache2' not in svcs: + svcs.append('apache2') + _map['WSGI_CEILOMETER_API_CONF'] = ['apache2'] + return _map @@ -260,6 +280,9 @@ def do_openstack_upgrade(configs): # set CONFIGS to load templates from new release configs.set_release(openstack_release=new_os_rel) + if run_in_apache(): + disable_package_apache_site() + def ceilometer_release_services(): codename = get_os_codename_install_source(config('openstack-origin')) @@ -389,3 +412,18 @@ def reload_systemd(): """ if init_is_systemd(): subprocess.check_call(['systemctl', 'daemon-reload']) + + +def run_in_apache(): + """Return true if ceilometer API is run under apache2 with mod_wsgi in + this release. + """ + return os_release('ceilometer-common') >= 'ocata' + + +def disable_package_apache_site(): + """Ensure that the package-provided apache configuration is disabled to + prevent it from conflicting with the charm-provided version. + """ + if os.path.exists(PACKAGE_CEILOMETER_API_CONF): + subprocess.check_call(['a2dissite', 'ceilometer-api']) diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 6b340f7..ec9fbb9 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -173,9 +173,12 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment): ceilometer_svcs = [ 'ceilometer-agent-central', 'ceilometer-collector', - 'ceilometer-api', 'ceilometer-agent-notification', ] + if self._get_openstack_release() >= self.xenial_ocata: + ceilometer_svcs.append('apache2') + else: + ceilometer_svcs.append('ceilometer-api') if self._get_openstack_release() < self.trusty_mitaka: ceilometer_svcs.append('ceilometer-alarm-evaluator') @@ -641,11 +644,14 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment): if self._get_openstack_release() >= self.xenial_newton: services = { 'ceilometer-collector - CollectorService(0)': conf_file, - 'ceilometer-api': conf_file, 'ceilometer-polling - AgentManager(0)': conf_file, 'ceilometer-agent-notification - NotificationService(0)': conf_file, } + if self._get_openstack_release() >= self.xenial_ocata: + services['apache2'] = conf_file + else: + services['ceilometer-api'] = conf_file else: services = { 'ceilometer-collector': conf_file, diff --git a/unit_tests/test_ceilometer_hooks.py b/unit_tests/test_ceilometer_hooks.py index 878496a..0e63aa6 100644 --- a/unit_tests/test_ceilometer_hooks.py +++ b/unit_tests/test_ceilometer_hooks.py @@ -64,6 +64,7 @@ TO_PATCH = [ 'status_set', 'update_dns_ha_resource_params', 'reload_systemd', + 'run_in_apache', 'mkdir', 'init_is_systemd', 'os_release', diff --git a/unit_tests/test_ceilometer_utils.py b/unit_tests/test_ceilometer_utils.py index 0cb68cf..07ead94 100644 --- a/unit_tests/test_ceilometer_utils.py +++ b/unit_tests/test_ceilometer_utils.py @@ -36,6 +36,7 @@ TO_PATCH = [ 'os', 'enable_memcache', 'token_cache_pkgs', + 'os_release', ]