Use canonical_url helper for endpoint resolution

This commit is contained in:
James Page 2014-07-14 15:03:48 +01:00
parent 11038ef5e7
commit b1290e9351
8 changed files with 177 additions and 55 deletions

2
.bzrignore Normal file
View File

@ -0,0 +1,2 @@
.coverage
bin

View File

@ -1,5 +0,0 @@
}q(U collectorqUcoverage v3.7.1qUlinesq}q(Ui/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/storage/linux/ceph.pyq]Uh/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/openstack/neutron.pyq]Ug/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/storage/__init__.pyq]q KaUf/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/openstack/utils.pyq
]q (KKKKK K
K KKKKKKK K"K&K'K(K)K*K+K,K-K1K2K3K4K5K6K7K8K<K=K>K?K@KAKBKCKDKEKFKGKHKIKLKOKTKmKrK{K…K±KÄKÇK×KàM*M<MOMpM‡M“eUh/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/storage/linux/lvm.pyq ]q (KK KK(K3KEKOeUk/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/openstack/templating.pyq]q(KKKK K KKKKK-K.K0K7K8K9K:K<K=K>K?K@KAKBKCKFKJKKKUKaKkK´KµKÆKÏKÔKÚKóMMMeUW/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/__init__.pyq]qKaUm/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/openstack/alternatives.pyq]UM/home/jamespage/src/charms/reference-network/swift-proxy/hooks/swift_hooks.pyq]Um/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/storage/linux/__init__.pyq]qKaU_/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/fetch/archiveurl.pyq]UY/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/core/fstab.pyq]q(KKK K KKKKK!K)K+K2K7KAKHKPKfKgKnKoeU[/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/core/hookenv.pyq]q(KKKK K
K K K KKKKKKKKK&K/K2K=KFKGKIKNK\K`KdKhKmK{K€K…KŠK<C5A0>K”K™KžKÂKÃKÅKÌKÜKåKîKüKýM M M M/M0M:M;MDMEMPMQM\M]MhMwM‡MˆMM¸eU_/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/__init__.pyq]qKaUs/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/openstack/templates/__init__.pyq]U_/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/payload/__init__.pyq]UO/home/jamespage/src/charms/reference-network/swift-proxy/hooks/swift_context.pyq]U]/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/fetch/__init__.pyq ]q!(KKKKKK K KKKKKKKKKK K!K"K$K%K&K'K(K)K*K,K-K.K/K0K1K2K4K5K6K7K8K9K:K<K=K>K?K@KAKBKCKJKMKNKOKRKSKVKWKZK[K^K`KbKgKlKoKvKKœK«K±K¼KËKêKëKìM M"M(M;eUh/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/openstack/context.pyq"]Ua/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/network/ip.pyq#]Uj/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/storage/linux/utils.pyq$]q%(KKKKK KK(eUg/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/hahelpers/apache.pyq&]Uh/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/hahelpers/cluster.pyq']Ui/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/hahelpers/__init__.pyq(]U\/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/core/__init__.pyq)]q*KaUM/home/jamespage/src/charms/reference-network/swift-proxy/hooks/swift_utils.pyq+]Ui/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/openstack/__init__.pyq,]q-KaU[/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/fetch/bzrurl.pyq.]U\/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/payload/execd.pyq/]Ug/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/network/__init__.pyq0]UX/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/core/host.pyq1]q2(KKK K
K K K KKKKKKKK!K&K/K5KBKXKcKnKzKŠK•KK¡K²KÀKÉKÔKùMMM M&M1M;eUm/home/jamespage/src/charms/reference-network/swift-proxy/hooks/charmhelpers/contrib/storage/linux/loopback.pyq3]q4(KKKKK K-euu.

View File

@ -28,7 +28,7 @@ def _validate_cidr(network):
def get_address_in_network(network, fallback=None, fatal=False):
"""
Get an IPv4 address within the network from the host.
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'.
@ -51,14 +51,23 @@ def get_address_in_network(network, fallback=None, fatal=False):
not_found_error_out()
_validate_cidr(network)
network = netaddr.IPNetwork(network)
for iface in netifaces.interfaces():
addresses = netifaces.ifaddresses(iface)
if netifaces.AF_INET in addresses:
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 netaddr.IPNetwork(network):
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 'fe80' not in addr['addr']:
netmask = addr['netmask']
cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
netmask))
if cidr in network:
return str(cidr.ip)
if fallback is not None:
return fallback
@ -69,6 +78,17 @@ def get_address_in_network(network, fallback=None, fatal=False):
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.
@ -93,3 +113,60 @@ def is_address_in_network(network, address):
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 'fe80' not in addr['addr']:
cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
addr['netmask']))
if address in cidr:
if key == 'iface':
return iface
else:
return addr[key]
return None
def get_iface_for_address(address):
"""Determine the physical interface to which an IP address could be bound
:param address (str): An individual IPv4 or IPv6 address without a net
mask or subnet prefix. For example, '192.168.1.1'.
:returns str: Interface name or None if address is not bindable.
"""
return _get_for_address(address, 'iface')
def get_netmask_for_address(address):
"""Determine the netmask of the physical interface to which and IP address
could be bound
:param address (str): An individual IPv4 or IPv6 address without a net
mask or subnet prefix. For example, '192.168.1.1'.
:returns str: Netmask of configured interface or None if address is
not bindable.
"""
return _get_for_address(address, 'netmask')

View File

@ -148,7 +148,7 @@ class SharedDBContext(OSContextGenerator):
if self.relation_prefix is not None:
hostname_key = "{}_hostname".format(self.relation_prefix)
else:
hostname_key = "hostname"
hostname_key = "hostname"
access_hostname = get_address_in_network(access_network,
unit_get('private-address'))
set_hostname = relation_get(attribute=hostname_key,
@ -400,7 +400,9 @@ class HAProxyContext(OSContextGenerator):
cluster_hosts = {}
l_unit = local_unit().replace('/', '-')
cluster_hosts[l_unit] = unit_get('private-address')
cluster_hosts[l_unit] = \
get_address_in_network(config('os-internal-network'),
unit_get('private-address'))
for rid in relation_ids('cluster'):
for unit in related_units(rid):

View File

@ -0,0 +1,75 @@
from charmhelpers.core.hookenv import (
config,
unit_get,
)
from charmhelpers.contrib.network.ip import (
get_address_in_network,
is_address_in_network,
is_ipv6,
)
from charmhelpers.contrib.hahelpers.cluster import is_clustered
PUBLIC = 'public'
INTERNAL = 'int'
ADMIN = 'admin'
_address_map = {
PUBLIC: {
'config': 'os-public-network',
'fallback': 'public-address'
},
INTERNAL: {
'config': 'os-internal-network',
'fallback': 'private-address'
},
ADMIN: {
'config': 'os-admin-network',
'fallback': 'private-address'
}
}
def canonical_url(configs, endpoint_type=PUBLIC):
'''
Returns the correct HTTP URL to this host given the state of HTTPS
configuration, hacluster and charm configuration.
:configs OSTemplateRenderer: A config tempating object to inspect for
a complete https context.
:endpoint_type str: The endpoint type to resolve.
:returns str: Base URL for services on the current service unit.
'''
scheme = 'http'
if 'https' in configs.complete_contexts():
scheme = 'https'
address = resolve_address(endpoint_type)
if is_ipv6(address):
address = "[{}]".format(address)
return '%s://%s' % (scheme, address)
def resolve_address(endpoint_type=PUBLIC):
resolved_address = None
if is_clustered():
if config(_address_map[endpoint_type]['config']) is None:
# Assume vip is simple and pass back directly
resolved_address = config('vip')
else:
for vip in config('vip').split():
if is_address_in_network(
config(_address_map[endpoint_type]['config']),
vip):
resolved_address = vip
else:
resolved_address = get_address_in_network(
config(_address_map[endpoint_type]['config']),
unit_get(_address_map[endpoint_type]['fallback'])
)
if resolved_address is None:
raise ValueError('Unable to resolve a suitable IP address'
' based on charm state and configuration')
else:
return resolved_address

View File

@ -27,7 +27,12 @@ listen stats :8888
{% if units -%}
{% for service, ports in service_ports.iteritems() -%}
listen {{ service }} 0.0.0.0:{{ ports[0] }}
listen {{ service }}_ipv4 0.0.0.0:{{ ports[0] }}
balance roundrobin
{% for unit, address in units.iteritems() -%}
server {{ unit }} {{ address }}:{{ ports[1] }} check
{% endfor %}
listen {{ service }}_ipv6 :::{{ ports[0] }}
balance roundrobin
{% for unit, address in units.iteritems() -%}
server {{ unit }} {{ address }}:{{ ports[1] }} check

View File

@ -1,25 +0,0 @@
from netaddr import IPAddress, IPNetwork
class VIPConfiguration():
def __init__(self, configuration):
self.vip = []
for vip in configuration.split():
self.vips.append(IPAddress(vip))
def getVIP(self, network):
''' Determine the VIP for the provided network
:network str: CIDR presented network, e.g. 192.168.1.1/24
:returns str: IP address of VIP in provided network or None
'''
network = IPNetwork(network)
for vip in self.vips:
if vip in network:
return str(vip)
return None
def getNIC(self, network):
''' Determine the physical network interface in use
for the specified network'''

View File

@ -47,7 +47,11 @@ from charmhelpers.fetch import (
apt_update
)
from charmhelpers.payload.execd import execd_preinstall
from charmhelpers.contrib.network.ip import get_address_in_network
from charmhelpers.contrib.openstack.ip import (
canonical_url,
PUBLIC, INTERNAL, ADMIN
)
extra_pkgs = [
"haproxy",
@ -93,25 +97,12 @@ def install():
def keystone_joined(relid=None):
if not cluster.eligible_leader(SWIFT_HA_RES):
return
if cluster.is_clustered():
public_ip, internal_ip, admin_ip = config('vip')
else:
public_ip = get_address_in_network(config('os-public-network'),
unit_get('public-address'))
internal_ip = get_address_in_network(config('os-internal-network'),
unit_get('private-address'))
admin_ip = get_address_in_network(config('os-admin-network'),
unit_get('private-address'))
port = config('bind-port')
if cluster.https():
proto = 'https'
else:
proto = 'http'
admin_url = '%s://%s:%s' % (proto, admin_ip, port)
internal_url = '%s://%s:%s/v1/AUTH_$(tenant_id)s' % (proto, internal_ip,
port)
public_url = '%s://%s:%s/v1/AUTH_$(tenant_id)s' % (proto, public_ip,
port)
admin_url = '%s://%s:%s' % (canonical_url(CONFIGS, ADMIN), port)
internal_url = '%s://%s:%s/v1/AUTH_$(tenant_id)s' % \
(canonical_url(CONFIGS, INTERNAL), port)
public_url = '%s://%s:%s/v1/AUTH_$(tenant_id)s' % \
(canonical_url(CONFIGS, PUBLIC), port)
relation_set(service='swift',
region=config('region'),
public_url=public_url,