Merged trunk in
This commit is contained in:
commit
939e1e22a8
|
@ -5,5 +5,9 @@ include:
|
||||||
- fetch
|
- fetch
|
||||||
- contrib.storage.linux:
|
- contrib.storage.linux:
|
||||||
- utils
|
- utils
|
||||||
|
- contrib.hahelpers:
|
||||||
|
- apache
|
||||||
|
- cluster
|
||||||
- payload.execd
|
- payload.execd
|
||||||
- contrib.openstack.alternatives
|
- contrib.openstack.alternatives
|
||||||
|
- contrib.network.ip
|
||||||
|
|
20
config.yaml
20
config.yaml
|
@ -57,3 +57,23 @@ options:
|
||||||
100-continue. See the following page for more info:
|
100-continue. See the following page for more info:
|
||||||
|
|
||||||
http://ceph.com/docs/dumpling/radosgw/manual-install/#continue-support
|
http://ceph.com/docs/dumpling/radosgw/manual-install/#continue-support
|
||||||
|
vip:
|
||||||
|
type: string
|
||||||
|
default:
|
||||||
|
description: |
|
||||||
|
Virtual IP(s) to use to front API services in HA configuration.
|
||||||
|
.
|
||||||
|
If multiple networks are being used, a VIP should be provided for each
|
||||||
|
network, separated by spaces.
|
||||||
|
ha-bindiface:
|
||||||
|
type: string
|
||||||
|
default: eth0
|
||||||
|
description: |
|
||||||
|
Default network interface on which HA cluster will bind to communication
|
||||||
|
with the other members of the HA Cluster.
|
||||||
|
ha-mcastport:
|
||||||
|
type: int
|
||||||
|
default: 5414
|
||||||
|
description: |
|
||||||
|
Default multicast port number that will be used to communicate between
|
||||||
|
HA Cluster nodes.
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
#
|
||||||
|
# Copyright 2012 Canonical Ltd.
|
||||||
|
#
|
||||||
|
# This file is sourced from lp:openstack-charm-helpers
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# James Page <james.page@ubuntu.com>
|
||||||
|
# Adam Gandelman <adamg@ubuntu.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
config as config_get,
|
||||||
|
relation_get,
|
||||||
|
relation_ids,
|
||||||
|
related_units as relation_list,
|
||||||
|
log,
|
||||||
|
INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_cert():
|
||||||
|
cert = config_get('ssl_cert')
|
||||||
|
key = config_get('ssl_key')
|
||||||
|
if not (cert and key):
|
||||||
|
log("Inspecting identity-service relations for SSL certificate.",
|
||||||
|
level=INFO)
|
||||||
|
cert = key = None
|
||||||
|
for r_id in relation_ids('identity-service'):
|
||||||
|
for unit in relation_list(r_id):
|
||||||
|
if not cert:
|
||||||
|
cert = relation_get('ssl_cert',
|
||||||
|
rid=r_id, unit=unit)
|
||||||
|
if not key:
|
||||||
|
key = relation_get('ssl_key',
|
||||||
|
rid=r_id, unit=unit)
|
||||||
|
return (cert, key)
|
||||||
|
|
||||||
|
|
||||||
|
def get_ca_cert():
|
||||||
|
ca_cert = config_get('ssl_ca')
|
||||||
|
if ca_cert is None:
|
||||||
|
log("Inspecting identity-service relations for CA SSL certificate.",
|
||||||
|
level=INFO)
|
||||||
|
for r_id in relation_ids('identity-service'):
|
||||||
|
for unit in relation_list(r_id):
|
||||||
|
if ca_cert is None:
|
||||||
|
ca_cert = relation_get('ca_cert',
|
||||||
|
rid=r_id, unit=unit)
|
||||||
|
return ca_cert
|
||||||
|
|
||||||
|
|
||||||
|
def install_ca_cert(ca_cert):
|
||||||
|
if ca_cert:
|
||||||
|
with open('/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt',
|
||||||
|
'w') as crt:
|
||||||
|
crt.write(ca_cert)
|
||||||
|
subprocess.check_call(['update-ca-certificates', '--fresh'])
|
|
@ -0,0 +1,226 @@
|
||||||
|
#
|
||||||
|
# Copyright 2012 Canonical Ltd.
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# James Page <james.page@ubuntu.com>
|
||||||
|
# Adam Gandelman <adamg@ubuntu.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Helpers for clustering and determining "cluster leadership" and other
|
||||||
|
clustering-related helpers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
|
||||||
|
from socket import gethostname as get_unit_hostname
|
||||||
|
|
||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
log,
|
||||||
|
relation_ids,
|
||||||
|
related_units as relation_list,
|
||||||
|
relation_get,
|
||||||
|
config as config_get,
|
||||||
|
INFO,
|
||||||
|
ERROR,
|
||||||
|
WARNING,
|
||||||
|
unit_get,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HAIncompleteConfig(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def is_elected_leader(resource):
|
||||||
|
"""
|
||||||
|
Returns True if the charm executing this is the elected cluster leader.
|
||||||
|
|
||||||
|
It relies on two mechanisms to determine leadership:
|
||||||
|
1. If the charm is part of a corosync cluster, call corosync to
|
||||||
|
determine leadership.
|
||||||
|
2. If the charm is not part of a corosync cluster, the leader is
|
||||||
|
determined as being "the alive unit with the lowest unit numer". In
|
||||||
|
other words, the oldest surviving unit.
|
||||||
|
"""
|
||||||
|
if is_clustered():
|
||||||
|
if not is_crm_leader(resource):
|
||||||
|
log('Deferring action to CRM leader.', level=INFO)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
peers = peer_units()
|
||||||
|
if peers and not oldest_peer(peers):
|
||||||
|
log('Deferring action to oldest service unit.', level=INFO)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_clustered():
|
||||||
|
for r_id in (relation_ids('ha') or []):
|
||||||
|
for unit in (relation_list(r_id) or []):
|
||||||
|
clustered = relation_get('clustered',
|
||||||
|
rid=r_id,
|
||||||
|
unit=unit)
|
||||||
|
if clustered:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_crm_leader(resource):
|
||||||
|
"""
|
||||||
|
Returns True if the charm calling this is the elected corosync leader,
|
||||||
|
as returned by calling the external "crm" command.
|
||||||
|
"""
|
||||||
|
cmd = [
|
||||||
|
"crm", "resource",
|
||||||
|
"show", resource
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
status = subprocess.check_output(cmd)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if get_unit_hostname() in status:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_leader(resource):
|
||||||
|
log("is_leader is deprecated. Please consider using is_crm_leader "
|
||||||
|
"instead.", level=WARNING)
|
||||||
|
return is_crm_leader(resource)
|
||||||
|
|
||||||
|
|
||||||
|
def peer_units(peer_relation="cluster"):
|
||||||
|
peers = []
|
||||||
|
for r_id in (relation_ids(peer_relation) or []):
|
||||||
|
for unit in (relation_list(r_id) or []):
|
||||||
|
peers.append(unit)
|
||||||
|
return peers
|
||||||
|
|
||||||
|
|
||||||
|
def peer_ips(peer_relation='cluster', addr_key='private-address'):
|
||||||
|
'''Return a dict of peers and their private-address'''
|
||||||
|
peers = {}
|
||||||
|
for r_id in relation_ids(peer_relation):
|
||||||
|
for unit in relation_list(r_id):
|
||||||
|
peers[unit] = relation_get(addr_key, rid=r_id, unit=unit)
|
||||||
|
return peers
|
||||||
|
|
||||||
|
|
||||||
|
def oldest_peer(peers):
|
||||||
|
"""Determines who the oldest peer is by comparing unit numbers."""
|
||||||
|
local_unit_no = int(os.getenv('JUJU_UNIT_NAME').split('/')[1])
|
||||||
|
for peer in peers:
|
||||||
|
remote_unit_no = int(peer.split('/')[1])
|
||||||
|
if remote_unit_no < local_unit_no:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def eligible_leader(resource):
|
||||||
|
log("eligible_leader is deprecated. Please consider using "
|
||||||
|
"is_elected_leader instead.", level=WARNING)
|
||||||
|
return is_elected_leader(resource)
|
||||||
|
|
||||||
|
|
||||||
|
def https():
|
||||||
|
'''
|
||||||
|
Determines whether enough data has been provided in configuration
|
||||||
|
or relation data to configure HTTPS
|
||||||
|
.
|
||||||
|
returns: boolean
|
||||||
|
'''
|
||||||
|
if config_get('use-https') == "yes":
|
||||||
|
return True
|
||||||
|
if config_get('ssl_cert') and config_get('ssl_key'):
|
||||||
|
return True
|
||||||
|
for r_id in relation_ids('identity-service'):
|
||||||
|
for unit in relation_list(r_id):
|
||||||
|
rel_state = [
|
||||||
|
relation_get('https_keystone', rid=r_id, unit=unit),
|
||||||
|
relation_get('ssl_cert', rid=r_id, unit=unit),
|
||||||
|
relation_get('ssl_key', rid=r_id, unit=unit),
|
||||||
|
relation_get('ca_cert', rid=r_id, unit=unit),
|
||||||
|
]
|
||||||
|
# NOTE: works around (LP: #1203241)
|
||||||
|
if (None not in rel_state) and ('' not in rel_state):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def determine_api_port(public_port):
|
||||||
|
'''
|
||||||
|
Determine correct API server listening port based on
|
||||||
|
existence of HTTPS reverse proxy and/or haproxy.
|
||||||
|
|
||||||
|
public_port: int: standard public port for given service
|
||||||
|
|
||||||
|
returns: int: the correct listening port for the API service
|
||||||
|
'''
|
||||||
|
i = 0
|
||||||
|
if len(peer_units()) > 0 or is_clustered():
|
||||||
|
i += 1
|
||||||
|
if https():
|
||||||
|
i += 1
|
||||||
|
return public_port - (i * 10)
|
||||||
|
|
||||||
|
|
||||||
|
def determine_apache_port(public_port):
|
||||||
|
'''
|
||||||
|
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 len(peer_units()) > 0 or is_clustered():
|
||||||
|
i += 1
|
||||||
|
return public_port - (i * 10)
|
||||||
|
|
||||||
|
|
||||||
|
def get_hacluster_config():
|
||||||
|
'''
|
||||||
|
Obtains all relevant configuration from charm configuration required
|
||||||
|
for initiating a relation to hacluster:
|
||||||
|
|
||||||
|
ha-bindiface, ha-mcastport, vip
|
||||||
|
|
||||||
|
returns: dict: A dict containing settings keyed by setting name.
|
||||||
|
raises: HAIncompleteConfig if settings are missing.
|
||||||
|
'''
|
||||||
|
settings = ['ha-bindiface', 'ha-mcastport', 'vip']
|
||||||
|
conf = {}
|
||||||
|
for setting in settings:
|
||||||
|
conf[setting] = config_get(setting)
|
||||||
|
missing = []
|
||||||
|
[missing.append(s) for s, v in conf.iteritems() if v is None]
|
||||||
|
if missing:
|
||||||
|
log('Insufficient config data to configure hacluster.', level=ERROR)
|
||||||
|
raise HAIncompleteConfig
|
||||||
|
return conf
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_url(configs, vip_setting='vip'):
|
||||||
|
'''
|
||||||
|
Returns the correct HTTP URL to this host given the state of HTTPS
|
||||||
|
configuration and hacluster.
|
||||||
|
|
||||||
|
:configs : OSTemplateRenderer: A config tempating object to inspect for
|
||||||
|
a complete https context.
|
||||||
|
|
||||||
|
:vip_setting: str: Setting in charm config that specifies
|
||||||
|
VIP address.
|
||||||
|
'''
|
||||||
|
scheme = 'http'
|
||||||
|
if 'https' in configs.complete_contexts():
|
||||||
|
scheme = 'https'
|
||||||
|
if is_clustered():
|
||||||
|
addr = config_get(vip_setting)
|
||||||
|
else:
|
||||||
|
addr = unit_get('private-address')
|
||||||
|
return '%s://%s' % (scheme, addr)
|
|
@ -0,0 +1,187 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from charmhelpers.fetch import apt_install
|
||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
ERROR, log, config,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import netifaces
|
||||||
|
except ImportError:
|
||||||
|
apt_install('python-netifaces')
|
||||||
|
import netifaces
|
||||||
|
|
||||||
|
try:
|
||||||
|
import netaddr
|
||||||
|
except ImportError:
|
||||||
|
apt_install('python-netaddr')
|
||||||
|
import netaddr
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_cidr(network):
|
||||||
|
try:
|
||||||
|
netaddr.IPNetwork(network)
|
||||||
|
except (netaddr.core.AddrFormatError, ValueError):
|
||||||
|
raise ValueError("Network (%s) is not in CIDR presentation format" %
|
||||||
|
network)
|
||||||
|
|
||||||
|
|
||||||
|
def get_address_in_network(network, fallback=None, fatal=False):
|
||||||
|
"""
|
||||||
|
Get an IPv4 or IPv6 address within the network from the host.
|
||||||
|
|
||||||
|
:param network (str): CIDR presentation format. For example,
|
||||||
|
'192.168.1.0/24'.
|
||||||
|
:param fallback (str): If no address is found, return fallback.
|
||||||
|
:param fatal (boolean): If no address is found, fallback is not
|
||||||
|
set and fatal is True then exit(1).
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def not_found_error_out():
|
||||||
|
log("No IP address found in network: %s" % network,
|
||||||
|
level=ERROR)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if network is None:
|
||||||
|
if fallback is not None:
|
||||||
|
return fallback
|
||||||
|
else:
|
||||||
|
if fatal:
|
||||||
|
not_found_error_out()
|
||||||
|
|
||||||
|
_validate_cidr(network)
|
||||||
|
network = netaddr.IPNetwork(network)
|
||||||
|
for iface in netifaces.interfaces():
|
||||||
|
addresses = netifaces.ifaddresses(iface)
|
||||||
|
if network.version == 4 and netifaces.AF_INET in addresses:
|
||||||
|
addr = addresses[netifaces.AF_INET][0]['addr']
|
||||||
|
netmask = addresses[netifaces.AF_INET][0]['netmask']
|
||||||
|
cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
|
||||||
|
if cidr in network:
|
||||||
|
return str(cidr.ip)
|
||||||
|
if network.version == 6 and netifaces.AF_INET6 in addresses:
|
||||||
|
for addr in addresses[netifaces.AF_INET6]:
|
||||||
|
if not addr['addr'].startswith('fe80'):
|
||||||
|
cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
|
||||||
|
addr['netmask']))
|
||||||
|
if cidr in network:
|
||||||
|
return str(cidr.ip)
|
||||||
|
|
||||||
|
if fallback is not None:
|
||||||
|
return fallback
|
||||||
|
|
||||||
|
if fatal:
|
||||||
|
not_found_error_out()
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_ipv6(address):
|
||||||
|
'''Determine whether provided address is IPv6 or not'''
|
||||||
|
try:
|
||||||
|
address = netaddr.IPAddress(address)
|
||||||
|
except netaddr.AddrFormatError:
|
||||||
|
# probably a hostname - so not an address at all!
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return address.version == 6
|
||||||
|
|
||||||
|
|
||||||
|
def is_address_in_network(network, address):
|
||||||
|
"""
|
||||||
|
Determine whether the provided address is within a network range.
|
||||||
|
|
||||||
|
:param network (str): CIDR presentation format. For example,
|
||||||
|
'192.168.1.0/24'.
|
||||||
|
:param address: An individual IPv4 or IPv6 address without a net
|
||||||
|
mask or subnet prefix. For example, '192.168.1.1'.
|
||||||
|
:returns boolean: Flag indicating whether address is in network.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
network = netaddr.IPNetwork(network)
|
||||||
|
except (netaddr.core.AddrFormatError, ValueError):
|
||||||
|
raise ValueError("Network (%s) is not in CIDR presentation format" %
|
||||||
|
network)
|
||||||
|
try:
|
||||||
|
address = netaddr.IPAddress(address)
|
||||||
|
except (netaddr.core.AddrFormatError, ValueError):
|
||||||
|
raise ValueError("Address (%s) is not in correct presentation format" %
|
||||||
|
address)
|
||||||
|
if address in network:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _get_for_address(address, key):
|
||||||
|
"""Retrieve an attribute of or the physical interface that
|
||||||
|
the IP address provided could be bound to.
|
||||||
|
|
||||||
|
:param address (str): An individual IPv4 or IPv6 address without a net
|
||||||
|
mask or subnet prefix. For example, '192.168.1.1'.
|
||||||
|
:param key: 'iface' for the physical interface name or an attribute
|
||||||
|
of the configured interface, for example 'netmask'.
|
||||||
|
:returns str: Requested attribute or None if address is not bindable.
|
||||||
|
"""
|
||||||
|
address = netaddr.IPAddress(address)
|
||||||
|
for iface in netifaces.interfaces():
|
||||||
|
addresses = netifaces.ifaddresses(iface)
|
||||||
|
if address.version == 4 and netifaces.AF_INET in addresses:
|
||||||
|
addr = addresses[netifaces.AF_INET][0]['addr']
|
||||||
|
netmask = addresses[netifaces.AF_INET][0]['netmask']
|
||||||
|
cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
|
||||||
|
if address in cidr:
|
||||||
|
if key == 'iface':
|
||||||
|
return iface
|
||||||
|
else:
|
||||||
|
return addresses[netifaces.AF_INET][0][key]
|
||||||
|
if address.version == 6 and netifaces.AF_INET6 in addresses:
|
||||||
|
for addr in addresses[netifaces.AF_INET6]:
|
||||||
|
if not addr['addr'].startswith('fe80'):
|
||||||
|
cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
|
||||||
|
addr['netmask']))
|
||||||
|
if address in cidr:
|
||||||
|
if key == 'iface':
|
||||||
|
return iface
|
||||||
|
else:
|
||||||
|
return addr[key]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
get_iface_for_address = partial(_get_for_address, key='iface')
|
||||||
|
|
||||||
|
get_netmask_for_address = partial(_get_for_address, key='netmask')
|
||||||
|
|
||||||
|
|
||||||
|
def get_ipv6_addr(iface="eth0"):
|
||||||
|
try:
|
||||||
|
iface_addrs = netifaces.ifaddresses(iface)
|
||||||
|
if netifaces.AF_INET6 not in iface_addrs:
|
||||||
|
raise Exception("Interface '%s' doesn't have an ipv6 address." % iface)
|
||||||
|
|
||||||
|
addresses = netifaces.ifaddresses(iface)[netifaces.AF_INET6]
|
||||||
|
ipv6_addr = [a['addr'] for a in addresses if not a['addr'].startswith('fe80')
|
||||||
|
and config('vip') != a['addr']]
|
||||||
|
if not ipv6_addr:
|
||||||
|
raise Exception("Interface '%s' doesn't have global ipv6 address." % iface)
|
||||||
|
|
||||||
|
return ipv6_addr[0]
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("Invalid interface '%s'" % iface)
|
||||||
|
|
||||||
|
|
||||||
|
def format_ipv6_addr(address):
|
||||||
|
"""
|
||||||
|
IPv6 needs to be wrapped with [] in url link to parse correctly.
|
||||||
|
"""
|
||||||
|
if is_ipv6(address):
|
||||||
|
address = "[%s]" % address
|
||||||
|
else:
|
||||||
|
log("Not an valid ipv6 address: %s" % address,
|
||||||
|
level=ERROR)
|
||||||
|
address = None
|
||||||
|
return address
|
|
@ -0,0 +1 @@
|
||||||
|
hooks.py
|
|
@ -0,0 +1 @@
|
||||||
|
hooks.py
|
|
@ -0,0 +1 @@
|
||||||
|
hooks.py
|
|
@ -0,0 +1 @@
|
||||||
|
hooks.py
|
|
@ -13,7 +13,7 @@ import sys
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import ceph
|
import ceph
|
||||||
|
import charmhelpers.contrib.hahelpers.cluster as cluster
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
relation_get,
|
relation_get,
|
||||||
relation_ids,
|
relation_ids,
|
||||||
|
@ -22,7 +22,7 @@ from charmhelpers.core.hookenv import (
|
||||||
unit_get,
|
unit_get,
|
||||||
open_port,
|
open_port,
|
||||||
relation_set,
|
relation_set,
|
||||||
log,
|
log, ERROR,
|
||||||
Hooks, UnregisteredHookError,
|
Hooks, UnregisteredHookError,
|
||||||
)
|
)
|
||||||
from charmhelpers.fetch import (
|
from charmhelpers.fetch import (
|
||||||
|
@ -35,13 +35,18 @@ from utils import (
|
||||||
render_template,
|
render_template,
|
||||||
get_host_ip,
|
get_host_ip,
|
||||||
enable_pocket,
|
enable_pocket,
|
||||||
is_apache_24
|
is_apache_24,
|
||||||
|
CEPHRG_HA_RES,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.payload.execd import execd_preinstall
|
from charmhelpers.payload.execd import execd_preinstall
|
||||||
from charmhelpers.core.host import cmp_pkgrevno
|
from charmhelpers.core.host import cmp_pkgrevno
|
||||||
from socket import gethostname as get_unit_hostname
|
from socket import gethostname as get_unit_hostname
|
||||||
|
|
||||||
|
from charmhelpers.contrib.network.ip import (
|
||||||
|
get_iface_for_address,
|
||||||
|
get_netmask_for_address
|
||||||
|
)
|
||||||
hooks = Hooks()
|
hooks = Hooks()
|
||||||
|
|
||||||
|
|
||||||
|
@ -237,8 +242,13 @@ def identity_joined(relid=None):
|
||||||
if cmp_pkgrevno('radosgw', '0.55') < 0:
|
if cmp_pkgrevno('radosgw', '0.55') < 0:
|
||||||
log('Integration with keystone requires ceph >= 0.55')
|
log('Integration with keystone requires ceph >= 0.55')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
if not cluster.eligible_leader(CEPHRG_HA_RES):
|
||||||
|
return
|
||||||
|
if cluster.is_clustered():
|
||||||
|
hostname = config('vip')
|
||||||
|
else:
|
||||||
|
hostname = unit_get('private-address')
|
||||||
|
|
||||||
hostname = unit_get('private-address')
|
|
||||||
admin_url = 'http://{}:80/swift'.format(hostname)
|
admin_url = 'http://{}:80/swift'.format(hostname)
|
||||||
internal_url = public_url = '{}/v1'.format(admin_url)
|
internal_url = public_url = '{}/v1'.format(admin_url)
|
||||||
relation_set(service='swift',
|
relation_set(service='swift',
|
||||||
|
@ -255,6 +265,76 @@ def identity_changed():
|
||||||
restart()
|
restart()
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('cluster-relation-changed',
|
||||||
|
'cluster-relation-joined')
|
||||||
|
def cluster_changed():
|
||||||
|
print "Do cluster changed actions here"
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('ha-relation-joined')
|
||||||
|
def ha_relation_joined():
|
||||||
|
# Obtain the config values necessary for the cluster config. These
|
||||||
|
# include multicast port and interface to bind to.
|
||||||
|
corosync_bindiface = config('ha-bindiface')
|
||||||
|
corosync_mcastport = config('ha-mcastport')
|
||||||
|
vip = config('vip')
|
||||||
|
if not vip:
|
||||||
|
log('Unable to configure hacluster as vip not provided',
|
||||||
|
level=ERROR)
|
||||||
|
sys.exit(1)
|
||||||
|
# Obtain resources
|
||||||
|
# SWIFT_HA_RES = 'grp_swift_vips'
|
||||||
|
resources = {
|
||||||
|
'res_cephrg_haproxy': 'lsb:haproxy'
|
||||||
|
}
|
||||||
|
resource_params = {
|
||||||
|
'res_cephrg_haproxy': 'op monitor interval="5s"'
|
||||||
|
}
|
||||||
|
|
||||||
|
vip_group = []
|
||||||
|
for vip in vip.split():
|
||||||
|
iface = get_iface_for_address(vip)
|
||||||
|
if iface is not None:
|
||||||
|
vip_key = 'res_cephrg_{}_vip'.format(iface)
|
||||||
|
resources[vip_key] = 'ocf:heartbeat:IPaddr2'
|
||||||
|
resource_params[vip_key] = (
|
||||||
|
'params ip="{vip}" cidr_netmask="{netmask}"'
|
||||||
|
' nic="{iface}"'.format(vip=vip,
|
||||||
|
iface=iface,
|
||||||
|
netmask=get_netmask_for_address(vip))
|
||||||
|
)
|
||||||
|
vip_group.append(vip_key)
|
||||||
|
|
||||||
|
if len(vip_group) >= 1:
|
||||||
|
relation_set(groups={CEPHRG_HA_RES: ' '.join(vip_group)})
|
||||||
|
|
||||||
|
init_services = {
|
||||||
|
'res_cephrg_haproxy': 'haproxy'
|
||||||
|
}
|
||||||
|
clones = {
|
||||||
|
'cl_cephrg_haproxy': 'res_cephrg_haproxy'
|
||||||
|
}
|
||||||
|
|
||||||
|
relation_set(init_services=init_services,
|
||||||
|
corosync_bindiface=corosync_bindiface,
|
||||||
|
corosync_mcastport=corosync_mcastport,
|
||||||
|
resources=resources,
|
||||||
|
resource_params=resource_params,
|
||||||
|
clones=clones)
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('ha-relation-changed')
|
||||||
|
def ha_relation_changed():
|
||||||
|
clustered = relation_get('clustered')
|
||||||
|
if clustered and cluster.is_leader(CEPHRG_HA_RES):
|
||||||
|
log('Cluster configured, notifying other services and'
|
||||||
|
'updating keystone endpoint configuration')
|
||||||
|
# Tell all related services to start using
|
||||||
|
# the VIP instead
|
||||||
|
for r_id in relation_ids('identity-service'):
|
||||||
|
identity_joined(relid=r_id)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
try:
|
try:
|
||||||
hooks.execute(sys.argv)
|
hooks.execute(sys.argv)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import os
|
||||||
from charmhelpers.core.hookenv import unit_get
|
from charmhelpers.core.hookenv import unit_get
|
||||||
from charmhelpers.fetch import apt_install
|
from charmhelpers.fetch import apt_install
|
||||||
|
|
||||||
|
CEPHRG_HA_RES = 'grp_cephrg_vips'
|
||||||
TEMPLATES_DIR = 'templates'
|
TEMPLATES_DIR = 'templates'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -14,6 +14,12 @@ requires:
|
||||||
interface: ceph-radosgw
|
interface: ceph-radosgw
|
||||||
identity-service:
|
identity-service:
|
||||||
interface: keystone
|
interface: keystone
|
||||||
|
ha:
|
||||||
|
interface: hacluster
|
||||||
|
scope: container
|
||||||
provides:
|
provides:
|
||||||
gateway:
|
gateway:
|
||||||
interface: http
|
interface: http
|
||||||
|
peers:
|
||||||
|
cluster:
|
||||||
|
interface: swift-ha
|
||||||
|
|
Loading…
Reference in New Issue