Use canonical_url helper for endpoint resolution
This commit is contained in:
2
.bzrignore
Normal file
2
.bzrignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.coverage
|
||||||
|
bin
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<EFBFBD>}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
|
|
||||||
KKKKKKKK K"K&K'K(K)K*K+K,K-K1K2K3K4K5K6K7K8K<K=K>K?K@KAKBKCKDKEKFKGKHKIKLKOKTKmKrK{K<>K<EFBFBD>K<EFBFBD>K<EFBFBD>K<EFBFBD>K<EFBFBD>M*M<MOMpM<>M<>M<>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(KKKKK
|
|
||||||
@@ -28,7 +28,7 @@ def _validate_cidr(network):
|
|||||||
|
|
||||||
def get_address_in_network(network, fallback=None, fatal=False):
|
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,
|
:param network (str): CIDR presentation format. For example,
|
||||||
'192.168.1.0/24'.
|
'192.168.1.0/24'.
|
||||||
@@ -51,13 +51,22 @@ def get_address_in_network(network, fallback=None, fatal=False):
|
|||||||
not_found_error_out()
|
not_found_error_out()
|
||||||
|
|
||||||
_validate_cidr(network)
|
_validate_cidr(network)
|
||||||
|
network = netaddr.IPNetwork(network)
|
||||||
for iface in netifaces.interfaces():
|
for iface in netifaces.interfaces():
|
||||||
addresses = netifaces.ifaddresses(iface)
|
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']
|
addr = addresses[netifaces.AF_INET][0]['addr']
|
||||||
netmask = addresses[netifaces.AF_INET][0]['netmask']
|
netmask = addresses[netifaces.AF_INET][0]['netmask']
|
||||||
cidr = netaddr.IPNetwork("%s/%s" % (addr, 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)
|
return str(cidr.ip)
|
||||||
|
|
||||||
if fallback is not None:
|
if fallback is not None:
|
||||||
@@ -69,6 +78,17 @@ def get_address_in_network(network, fallback=None, fatal=False):
|
|||||||
return None
|
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):
|
def is_address_in_network(network, address):
|
||||||
"""
|
"""
|
||||||
Determine whether the provided address is within a network range.
|
Determine whether the provided address is within a network range.
|
||||||
@@ -93,3 +113,60 @@ def is_address_in_network(network, address):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
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')
|
||||||
|
|||||||
@@ -400,7 +400,9 @@ class HAProxyContext(OSContextGenerator):
|
|||||||
|
|
||||||
cluster_hosts = {}
|
cluster_hosts = {}
|
||||||
l_unit = local_unit().replace('/', '-')
|
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 rid in relation_ids('cluster'):
|
||||||
for unit in related_units(rid):
|
for unit in related_units(rid):
|
||||||
|
|||||||
75
hooks/charmhelpers/contrib/openstack/ip.py
Normal file
75
hooks/charmhelpers/contrib/openstack/ip.py
Normal 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
|
||||||
@@ -27,7 +27,12 @@ listen stats :8888
|
|||||||
|
|
||||||
{% if units -%}
|
{% if units -%}
|
||||||
{% for service, ports in service_ports.iteritems() -%}
|
{% 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
|
balance roundrobin
|
||||||
{% for unit, address in units.iteritems() -%}
|
{% for unit, address in units.iteritems() -%}
|
||||||
server {{ unit }} {{ address }}:{{ ports[1] }} check
|
server {{ unit }} {{ address }}:{{ ports[1] }} check
|
||||||
|
|||||||
@@ -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'''
|
|
||||||
|
|
||||||
@@ -47,7 +47,11 @@ from charmhelpers.fetch import (
|
|||||||
apt_update
|
apt_update
|
||||||
)
|
)
|
||||||
from charmhelpers.payload.execd import execd_preinstall
|
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 = [
|
extra_pkgs = [
|
||||||
"haproxy",
|
"haproxy",
|
||||||
@@ -93,25 +97,12 @@ def install():
|
|||||||
def keystone_joined(relid=None):
|
def keystone_joined(relid=None):
|
||||||
if not cluster.eligible_leader(SWIFT_HA_RES):
|
if not cluster.eligible_leader(SWIFT_HA_RES):
|
||||||
return
|
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')
|
port = config('bind-port')
|
||||||
if cluster.https():
|
admin_url = '%s://%s:%s' % (canonical_url(CONFIGS, ADMIN), port)
|
||||||
proto = 'https'
|
internal_url = '%s://%s:%s/v1/AUTH_$(tenant_id)s' % \
|
||||||
else:
|
(canonical_url(CONFIGS, INTERNAL), port)
|
||||||
proto = 'http'
|
public_url = '%s://%s:%s/v1/AUTH_$(tenant_id)s' % \
|
||||||
admin_url = '%s://%s:%s' % (proto, admin_ip, port)
|
(canonical_url(CONFIGS, PUBLIC), 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)
|
|
||||||
relation_set(service='swift',
|
relation_set(service='swift',
|
||||||
region=config('region'),
|
region=config('region'),
|
||||||
public_url=public_url,
|
public_url=public_url,
|
||||||
|
|||||||
Reference in New Issue
Block a user