WIP HA config (Do not merge) #4

Open
wants to merge 7 commits into
from
@@ -1,6 +1,12 @@
"""Adapter classes and utilities for use with Reactive interfaces"""
-from charmhelpers.core import hookenv
+import charms.reactive.bus as reactive_bus
+import charmhelpers.contrib.hahelpers.cluster as ch_cluster
+import charmhelpers.contrib.network.ip as ch_ip
+import charmhelpers.contrib.openstack.utils as ch_utils
+import charmhelpers.core.hookenv as hookenv
+
+ADDRESS_TYPES = ['admin', 'internal', 'public']
class OpenStackRelationAdapter(object):
@@ -82,6 +88,49 @@ def hosts(self):
return None
+class PeerHARelationAdapter(OpenStackRelationAdapter):
+ """
+ Adapter for cluster relation of nodes of the same service
+ """
+
+ interface_type = "cluster"
+
+ def __init__(self, relation):
+ super(PeerHARelationAdapter, self).__init__(relation)
+ self.config = hookenv.config()
+ self.local_address = APIConfigurationAdapter().local_address
+ self.local_unit_name = APIConfigurationAdapter().local_unit_name
+ self.cluster_hosts = {}
+ self.add_network_split_addresses()
+ self.add_default_addresses()
+
+ def add_network_split_addresses(self):
+ """Populate cluster_hosts with addresses of peers on a given network if
+ this node is also on that network"""
+ for addr_type in ADDRESS_TYPES:
+ cfg_opt = 'os-{}-network'.format(addr_type)
+ laddr = ch_ip.get_address_in_network(self.config.get(cfg_opt))
+ if laddr:
+ netmask = ch_ip.get_netmask_for_address(laddr)
+ self.cluster_hosts[laddr] = {
+ 'network': "{}/{}".format(laddr, netmask),
+ 'backends': {self.local_unit_name: laddr}}
+ key = '{}-address'.format(addr_type)
+ for _unit, _laddr in self.relation.ip_map(address_key=key):
+ self.cluster_hosts[laddr]['backends'][_unit] = _laddr
+
+ def add_default_addresses(self):
+ """Populate cluster_hosts with addresses supplied by private-address
+ """
+ self.cluster_hosts[self.local_address] = {}
+ netmask = ch_ip.get_netmask_for_address(self.local_address)
+ self.cluster_hosts[self.local_address] = {
+ 'network': "{}/{}".format(self.local_address, netmask),
+ 'backends': {self.local_unit_name: self.local_address}}
+ for _unit, _laddr in self.relation.ip_map():
+ self.cluster_hosts[self.local_address]['backends'][_unit] = _laddr
+
+
class DatabaseRelationAdapter(OpenStackRelationAdapter):
"""
Adapter for the Database relation interface.
@@ -148,6 +197,153 @@ def __init__(self):
setattr(self, k, v)
+class APIConfigurationAdapter(ConfigurationAdapter):
+ """This configuration adapter extends the base class and adds properties
+ common accross most OpenstackAPI services"""
+
+ def __init__(self, port_map=None):
+ super(APIConfigurationAdapter, self).__init__()
+ self.port_map = port_map
+ self.config = hookenv.config()
+
+ @property
+ def ipv6_mode(self):
+ """Return if charm should enable IPv6
+
+ @return True if user has requested ipv6 support otherwise False
+ """
+ return self.config.get('prefer-ipv6', False)
+
+ @property
+ def local_address(self):
+ """Return remotely accessible address of charm (not localhost)
+
+ @return True if user has requested ipv6 support otherwise False
+ """
+ if self.ipv6_mode:
+ addr = ch_ip.get_ipv6_addr(exc_list=[self.config('vip')])[0]
+ else:
+ addr = ch_utils.get_host_ip(hookenv.unit_get('private-address'))
+ return addr
+
+ @property
+ def local_unit_name(self):
+ """
+ @return local unit name
+ """
+ return hookenv.local_unit().replace('/', '-')
+
+ @property
+ def local_host(self):
+ """Return localhost address depending on whether IPv6 is enabled
+
+ @return localhost ip address
+ """
+ return 'ip6-localhost' if self.ipv6_mode else '127.0.0.1'
+
+ @property
+ def haproxy_host(self):
+ """Return haproxy bind address depending on whether IPv6 is enabled
+
+ @return address
+ """
+ return '::' if self.ipv6_mode else '0.0.0.0'
+
+ @property
+ def haproxy_stat_port(self):
+ """Port to listen on to access haproxy statistics
+
+ @return port
+ """
+ return '8888'
+
+ @property
+ def haproxy_stat_password(self):
+ """Password for accessing haproxy statistics
+
+ @return password
+ """
+ return reactive_bus.get_state('haproxy.stat.password')
+
+ @property
+ def service_ports(self):
+ """Dict of service names and the ports they listen on
+
+ @return {'svc1': 'portA', 'svc2': 'portB', ...}
+ """
+ service_ports = {}
+ if self.port_map:
+ for service in self.port_map.keys():
+ service_ports[service] = [
+ self.port_map[service]['admin'],
+ ch_cluster.determine_apache_port(
+ self.port_map[service]['admin'],
+ singlenode_mode=True)]
+ return service_ports
+
+ @property
+ def service_listen_info(self):
+ """Dict of service names and attributes for backend to listen on
+
+ @return {
+ 'svc1': {
+ 'proto': 'http',
+ 'ip': '10.0.0.10',
+ 'port': '8080',
+ 'url': 'http://10.0.0.10:8080},
+ 'svc2': {
+ 'proto': 'https',
+ 'ip': '10.0.0.20',
+ 'port': '8443',
+ 'url': 'https://10.0.0.20:8443},
+ ...
+
+ """
+ info = {}
+ if self.port_map:
+ for service in self.port_map.keys():
+ key = service.replace('-', '_')
+ info[key] = {
+ 'proto': 'http',
+ 'ip': self.local_address,
+ 'port': ch_cluster.determine_apache_port(
+ self.port_map[service]['admin'],
+ singlenode_mode=True)}
+ info[key]['url'] = '{proto}://{ip}:{port}'.format(**info[key])
+ return info
+
+ @property
+ def external_endpoints(self):
+ """Dict of service names and attributes that clients use to connect
+
+ @return {
+ 'svc1': {
+ 'proto': 'http',
+ 'ip': '10.0.0.10',
+ 'port': '8080',
+ 'url': 'http://10.0.0.10:8080},
+ 'svc2': {
+ 'proto': 'https',
+ 'ip': '10.0.0.20',
+ 'port': '8443',
+ 'url': 'https://10.0.0.20:8443},
+ ...
+
+ """
+ info = {}
+ info = {}
+ ip = self.config.get('vip', self.local_address)
+ if self.port_map:
+ for service in self.port_map.keys():
+ key = service.replace('-', '_')
+ info[key] = {
+ 'proto': 'http',
+ 'ip': ip,
+ 'port': self.port_map[service]['admin']}
+ info[key]['url'] = '{proto}://{ip}:{port}'.format(**info[key])
+ return info
+
+
class OpenStackRelationAdapters(object):
"""
Base adapters class for OpenStack Charms, used to aggregate
@@ -171,13 +367,17 @@ class OpenStackRelationAdapters(object):
_adapters = {
'amqp': RabbitMQRelationAdapter,
'shared_db': DatabaseRelationAdapter,
+ 'cluster': PeerHARelationAdapter,
}
"""
Default adapter mappings; may be overridden by relation adapters
in subclasses.
+
+ Additional kwargs can be passed to the configuration adapterwhich has been
+ specified via the options parameter
"""
- def __init__(self, relations, options=ConfigurationAdapter):
+ def __init__(self, relations, options=ConfigurationAdapter, **kwargs):
self._adapters.update(self.relation_adapters)
self._relations = []
for relation in relations:
@@ -188,7 +388,7 @@ def __init__(self, relations, options=ConfigurationAdapter):
relation_value = OpenStackRelationAdapter(relation)
setattr(self, relation_name, relation_value)
self._relations.append(relation_name)
- self.options = options()
+ self.options = options(**kwargs)
self._relations.append('options')
def __iter__(self):
Oops, something went wrong.