Resync network-split helpers

This commit is contained in:
James Page 2014-07-02 09:32:44 +01:00
parent ba95640201
commit fbfea46476
8 changed files with 144 additions and 27 deletions

View File

@ -1,4 +1,4 @@
branch: lp:charm-helpers
branch: lp:~james-page/charm-helpers/network-splits
destination: hooks/charmhelpers
include:
- core
@ -6,3 +6,4 @@ include:
- contrib.hahelpers.cluster
- contrib.peerstorage
- payload.execd
- contrib.network.ip

View File

@ -35,3 +35,12 @@ options:
type: int
default: 5490
description: Default multicast port number that will be used to communicate between HA Cluster nodes.
# Network configuration options
# by default all access is over 'private-address'
access-network:
type: string
description: |
The IP address and netmask of the 'access' network (e.g., 192.168.0.0/24)
.
This network will be used for access to database services.

View File

@ -163,13 +163,14 @@ def get_hacluster_config():
return conf
def canonical_url(configs, vip_setting='vip'):
def canonical_url(configs, vip_setting='vip', address=None):
'''
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.
'''
@ -179,5 +180,5 @@ def canonical_url(configs, vip_setting='vip'):
if is_clustered():
addr = config_get(vip_setting)
else:
addr = unit_get('private-address')
addr = address or unit_get('private-address')
return '%s://%s' % (scheme, addr)

View File

@ -0,0 +1,95 @@
import sys
from charmhelpers.fetch import apt_install
from charmhelpers.core.hookenv import (
ERROR, log,
)
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 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)
for iface in netifaces.interfaces():
addresses = netifaces.ifaddresses(iface)
if 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 netaddr.IPNetwork(network):
return str(cidr.ip)
if fallback is not None:
return fallback
if fatal:
not_found_error_out()
return None
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

View File

@ -25,7 +25,7 @@ cache = {}
def cached(func):
"""Cache return values for multiple executions of func + args
For example:
For example::
@cached
def unit_get(attribute):
@ -445,18 +445,19 @@ class UnregisteredHookError(Exception):
class Hooks(object):
"""A convenient handler for hook functions.
Example:
Example::
hooks = Hooks()
# register a hook, taking its name from the function name
@hooks.hook()
def install():
...
pass # your code here
# register a hook, providing a custom hook name
@hooks.hook("config-changed")
def config_changed():
...
pass # your code here
if __name__ == "__main__":
# execute a hook based on the name the program is called by

View File

@ -211,13 +211,13 @@ def file_hash(path):
def restart_on_change(restart_map, stopstart=False):
"""Restart services based on configuration files changing
This function is used a decorator, for example
This function is used a decorator, for example::
@restart_on_change({
'/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ]
})
def ceph_client_changed():
...
pass # your code here
In this example, the cinder-api and cinder-volume services
would be restarted if /etc/ceph/ceph.conf is changed by the
@ -313,9 +313,11 @@ def get_nic_hwaddr(nic):
def cmp_pkgrevno(package, revno, pkgcache=None):
'''Compare supplied revno with the revno of the installed package
1 => Installed revno is greater than supplied arg
0 => Installed revno is the same as supplied arg
-1 => Installed revno is less than supplied arg
* 1 => Installed revno is greater than supplied arg
* 0 => Installed revno is the same as supplied arg
* -1 => Installed revno is less than supplied arg
'''
import apt_pkg
if not pkgcache:

View File

@ -235,31 +235,39 @@ def configure_sources(update=False,
sources_var='install_sources',
keys_var='install_keys'):
"""
Configure multiple sources from charm configuration
Configure multiple sources from charm configuration.
The lists are encoded as yaml fragments in the configuration.
The frament needs to be included as a string.
Example config:
install_sources:
install_sources: |
- "ppa:foo"
- "http://example.com/repo precise main"
install_keys:
install_keys: |
- null
- "a1b2c3d4"
Note that 'null' (a.k.a. None) should not be quoted.
"""
sources = safe_load(config(sources_var))
keys = config(keys_var)
if keys is not None:
keys = safe_load(keys)
if isinstance(sources, basestring) and (
keys is None or isinstance(keys, basestring)):
add_source(sources, keys)
sources = safe_load((config(sources_var) or '').strip()) or []
keys = safe_load((config(keys_var) or '').strip()) or None
if isinstance(sources, basestring):
sources = [sources]
if keys is None:
for source in sources:
add_source(source, None)
else:
if not len(sources) == len(keys):
msg = 'Install sources and keys lists are different lengths'
raise SourceConfigError(msg)
for src_num in range(len(sources)):
add_source(sources[src_num], keys[src_num])
if isinstance(keys, basestring):
keys = [keys]
if len(sources) != len(keys):
raise SourceConfigError(
'Install sources and keys lists are different lengths')
for source, key in zip(sources, keys):
add_source(source, key)
if update:
apt_update(fatal=True)