import os import subprocess import ConfigParser from base64 import b64encode from collections import OrderedDict from copy import deepcopy from charmhelpers.contrib.openstack import templating, context from charmhelpers.contrib.openstack.utils import ( get_os_codename_package, save_script_rc as _save_script_rc, ) from charmhelpers.core.hookenv import ( config, relation_ids, ) import nova_cc_context TEMPLATES = 'templates/' CLUSTER_RES = 'res_nova_vip' # removed from original: python-mysqldb python-keystone charm-helper-sh BASE_PACKAGES = [ 'apache2', 'haproxy', 'uuid', ] BASE_SERVICES = [ 'nova-api-ec2', 'nova-api-os-compute', 'nova-objectstore', 'nova-cert', 'nova-scheduler', ] API_PORTS = { 'nova-api-ec2': 8773, 'nova-api-os-compute': 8774, 'nova-api-os-volume': 8776, 'nova-objectstore': 3333, 'quantum-server': 9696, } BASE_RESOURCE_MAP = OrderedDict([ ('/etc/nova/nova.conf', { 'services': BASE_SERVICES, 'contexts': [context.AMQPContext(), context.SharedDBContext(), context.ImageServiceContext(), nova_cc_context.VolumeServiceContext()], }), ('/etc/nova/api-paste.ini', { 'services': [s for s in BASE_SERVICES if 'api' in s], 'contexts': [context.IdentityServiceContext()], }), ('/etc/quantum/quantum.conf', { 'services': ['quantum-server'], 'contexts': [], }), ('/etc/quantum/api-paste.ini', { 'services': ['quantum-server'], 'contexts': [], }), ('/etc/haproxy/haproxy.cfg', { 'contexts': [context.HAProxyContext(), nova_cc_context.HAProxyContext()], 'services': ['haproxy'], }), ('/etc/apache2/sites-available/openstack_https_frontend', { 'contexts': [], 'contexts': [nova_cc_context.ApacheSSLContext()], 'services': ['apache2'], }), ]) CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' def resource_map(): ''' Dynamically generate a map of resources that will be managed for a single hook execution. ''' resource_map = deepcopy(BASE_RESOURCE_MAP) if relation_ids('nova-volume-service'): # if we have a relation to a nova-volume service, we're # also managing the nova-volume API endpoint (legacy) resource_map['/etc/nova/nova.conf']['services'].append( 'nova-api-os-volume') if config('network-manager').lower() != 'quantum': # pop out quantum resources if not deploying it. easier to # remove it from the base ordered dict than add it in later # and still preserve ordering for restart_map(). [resource_map.pop(k) for k in list(resource_map.iterkeys()) if 'quantum' in k] return resource_map def register_configs(): release = get_os_codename_package('nova-common', fatal=False) or 'essex' configs = templating.OSConfigRenderer(templates_dir=TEMPLATES, openstack_release=release) for cfg, rscs in resource_map.iteritems(): configs.register(cfg, rscs['contexts']) return configs def restart_map(): return {k: v['services'] for k, v in resource_map().iteritems()} def determine_ports(): '''Assemble a list of API ports for services we are managing''' ports = [] for cfg, services in restart_map().iteritems(): for service in services: try: ports.append(API_PORTS[service]) except KeyError: pass return ports def determine_packages(): # currently all packages match service names packages = [] + BASE_PACKAGES for k, v in resource_map().iteritems(): packages.extend(v['services']) return list(set(packages)) def save_script_rc(): env_vars = { 'OPENSTACK_PORT_MCASTPORT': config('ha-mcastport'), 'OPENSTACK_SERVICE_API_EC2': 'nova-api-ec2', 'OPENSTACK_SERVICE_API_OS_COMPUTE': 'nova-api-os-compute', 'OPENSTACK_SERVICE_CERT': 'nova-cert', 'OPENSTACK_SERVICE_CONDUCTOR': 'nova-conductor', 'OPENSTACK_SERVICE_OBJECTSTORE': 'nova-objectstore', 'OPENSTACK_SERVICE_SCHEDULER': 'nova-scheduler', } if relation_ids('nova-volume-service'): env_vars['OPENSTACK_SERVICE_API_OS_VOL'] = 'nova-api-os-volume' if config('network-manager').lower() == 'quantum': env_vars['OPENSTACK_SERVICE_API_QUANTUM'] = 'quantum-server' _save_script_rc(**env_vars) def do_openstack_upgrade(): # TODO pass def quantum_plugin(): return config('quantum-plugin').lower() def volume_service(): '''Specifies correct volume API for specific OS release''' os_vers = get_os_codename_package('nova-common') if os_vers == 'essex': return 'nova-volume' elif os_vers == 'folsom': # support both drivers in folsom. if not relation_ids('cinder-volume-service'): return 'nova-volume' return 'cinder' def migrate_database(): '''Runs nova-manage to initialize a new database or migrate existing''' cmd = ['nova-manage', 'db', 'sync'] subprocess.check_call(cmd) def auth_token_config(setting): ''' Returns currently configured value for setting in api-paste.ini's authtoken section, or None. ''' config = ConfigParser.RawConfigParser() config.read('/etc/nova/api-paste.ini') try: value = config.get('filter:authtoken', setting) except: return None if value.startswith('%'): return None return value def keystone_ca_cert_b64(): '''Returns the local Keystone-provided CA cert if it exists, or None.''' if not os.path.isfile(CA_CERT_PATH): return None with open(CA_CERT_PATH) as _in: return b64encode(_in.read()) def ssh_compute_add(): pass def determine_endpoints(): pass