charm-layer-openstack/lib/charm/openstack/charm.py

148 lines
4.3 KiB
Python

"""Classes to support writing re-usable charms in the reactive framework"""
import os
from charmhelpers.contrib.openstack.utils import (
configure_installation_source,
)
from charmhelpers.core.host import path_hash, service_restart
from charmhelpers.core.hookenv import config, status_set
from charmhelpers.fetch import (
apt_install,
apt_update,
filter_installed_packages,
)
from charm.openstack.ip import PUBLIC, INTERNAL, ADMIN, canonical_url
from contextlib import contextmanager
from collections import OrderedDict
from charmhelpers.contrib.openstack.templating import get_loader
from charmhelpers.core.templating import render
class OpenStackCharm(object):
"""
Base class for all OpenStack Charm classes;
encapulates general OpenStack charm payload operations
"""
packages = []
"""Packages to install"""
api_ports = {}
"""
Dictionary mapping services to ports for public, admin and
internal endpoints
"""
service_type = None
"""Keystone endpoint type"""
default_service = None
"""Default service for the charm"""
restart_map = {}
def __init__(self):
self.config = config()
# XXX It's not always liberty!
self.release = 'liberty'
def install(self):
"""
Install packages related to this charm based on
contents of packages attribute.
"""
packages = filter_installed_packages(self.packages)
if packages:
status_set('maintenance', 'Installing packages')
apt_install(packages, fatal=True)
def api_port(self, service, endpoint_type=PUBLIC):
"""
Determine the API port for a particular endpoint type
"""
return self.api_ports[service][endpoint_type]
def configure_source(self):
"""Configure installation source"""
configure_installation_source(self.config['openstack-origin'])
apt_update(fatal=True)
@property
def region(self):
"""OpenStack Region"""
return self.config['region']
@property
def public_url(self):
"""Public Endpoint URL"""
return "{}:{}".format(canonical_url(PUBLIC),
self.api_port(self.default_service,
PUBLIC))
@property
def admin_url(self):
"""Admin Endpoint URL"""
return "{}:{}".format(canonical_url(ADMIN),
self.api_port(self.default_service,
ADMIN))
@property
def internal_url(self):
"""Internal Endpoint URL"""
return "{}:{}".format(canonical_url(INTERNAL),
self.api_port(self.default_service,
INTERNAL))
@contextmanager
def restart_on_change(self):
checksums = {path: path_hash(path) for path in self.restart_map}
yield
restarts = []
for path in self.restart_map:
if path_hash(path) != checksums[path]:
restarts += self.restart_map[path]
services_list = list(OrderedDict.fromkeys(restarts))
for service_name in services_list:
service_restart(service_name)
def render_all_configs(self, adapters):
self.render_configs(adapters, self.restart_map.keys())
def render_configs(self, adapters, configs):
with self.restart_on_change():
for conf in self.restart_map.keys():
render(source=os.path.basename(conf),
template_loader=get_loader('templates/', self.release),
target=conf,
context=adapters)
def restart_all(self):
for svc in self.restart_map.keys():
service_restart(svc)
class OpenStackCharmFactory(object):
releases = {}
"""
Dictionary mapping OpenStack releases to their associated
Charm class for this charm
"""
first_release = "icehouse"
"""
First OpenStack release which this factory supports Charms for
"""
@classmethod
def charm(cls, release=None):
"""
Get an instance of the right charm for the
configured OpenStack series
"""
if release and release in cls.releases:
return cls.releases[release]()
else:
return cls.releases[cls.first_release]()