Switch to new canonical_url helper
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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):
|
||||
|
||||
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 -%}
|
||||
{% 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
|
||||
|
||||
@@ -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'''
|
||||
|
||||
@@ -73,14 +73,17 @@ from nova_cc_utils import (
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.hahelpers.cluster import (
|
||||
canonical_url,
|
||||
eligible_leader,
|
||||
get_hacluster_config,
|
||||
is_leader,
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
hooks = Hooks()
|
||||
CONFIGS = register_configs()
|
||||
@@ -233,15 +236,9 @@ def image_service_changed():
|
||||
def identity_joined(rid=None):
|
||||
if not eligible_leader(CLUSTER_RES):
|
||||
return
|
||||
public_url = canonical_url(CONFIGS,
|
||||
address=get_address_in_network(config('os-public-network'),
|
||||
unit_get('public-address')))
|
||||
internal_url = canonical_url(CONFIGS,
|
||||
address=get_address_in_network(config('os-internal-network'),
|
||||
unit_get('private-address')))
|
||||
admin_url = canonical_url(CONFIGS,
|
||||
address=get_address_in_network(config('os-admin-network'),
|
||||
unit_get('private-address')))
|
||||
public_url = canonical_url(CONFIGS, PUBLIC)
|
||||
internal_url = canonical_url(CONFIGS, INTERNAL)
|
||||
admin_url = canonical_url(CONFIGS, ADMIN)
|
||||
relation_set(relation_id=rid, **determine_endpoints(public_url,
|
||||
internal_url,
|
||||
admin_url))
|
||||
@@ -332,9 +329,7 @@ def neutron_settings():
|
||||
'quantum_plugin': neutron_plugin(),
|
||||
'region': config('region'),
|
||||
'quantum_security_groups': config('quantum-security-groups'),
|
||||
'quantum_url': "{}:{}".format(canonical_url(CONFIGS,
|
||||
get_address_in_network(config('os-internal-network'),
|
||||
unit_get('private-address'))),
|
||||
'quantum_url': "{}:{}".format(canonical_url(CONFIGS, INTERNAL),
|
||||
str(api_port('neutron-server'))),
|
||||
})
|
||||
neutron_url = urlparse(neutron_settings['quantum_url'])
|
||||
@@ -510,9 +505,7 @@ def nova_vmware_relation_joined(rid=None):
|
||||
rel_settings.update({
|
||||
'quantum_plugin': neutron_plugin(),
|
||||
'quantum_security_groups': config('quantum-security-groups'),
|
||||
'quantum_url': "{}:{}".format(canonical_url(CONFIGS,
|
||||
get_address_in_network(config('os-internal-network'),
|
||||
unit_get('private-address'))),
|
||||
'quantum_url': "{}:{}".format(canonical_url(CONFIGS, INTERNAL),
|
||||
str(api_port('neutron-server')))})
|
||||
|
||||
relation_set(relation_id=rid, **rel_settings)
|
||||
@@ -542,7 +535,7 @@ def neutron_api_relation_joined(rid=None):
|
||||
service_stop('neutron-server')
|
||||
for id_rid in relation_ids('identity-service'):
|
||||
identity_joined(rid=id_rid)
|
||||
nova_url = canonical_url(CONFIGS) + ":8774/v2"
|
||||
nova_url = canonical_url(CONFIGS, INTERNAL) + ":8774/v2"
|
||||
relation_set(relation_id=rid, nova_url=nova_url)
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ from charmhelpers.core.host import (
|
||||
service_start,
|
||||
)
|
||||
|
||||
|
||||
import nova_cc_context
|
||||
|
||||
TEMPLATES = 'templates/'
|
||||
|
||||
@@ -441,6 +441,7 @@ class NovaCCUtilsTests(CharmTestCase):
|
||||
self.relation_ids.return_value = []
|
||||
self.assertEquals(
|
||||
BASE_ENDPOINTS, utils.determine_endpoints('http://foohost.com',
|
||||
'http://foohost.com',
|
||||
'http://foohost.com'))
|
||||
|
||||
def test_determine_endpoints_nova_volume(self):
|
||||
@@ -458,6 +459,7 @@ class NovaCCUtilsTests(CharmTestCase):
|
||||
'nova-volume_service': 'nova-volume'})
|
||||
self.assertEquals(
|
||||
endpoints, utils.determine_endpoints('http://foohost.com',
|
||||
'http://foohost.com',
|
||||
'http://foohost.com'))
|
||||
|
||||
def test_determine_endpoints_quantum_neutron(self):
|
||||
@@ -473,6 +475,7 @@ class NovaCCUtilsTests(CharmTestCase):
|
||||
'quantum_service': 'quantum'})
|
||||
self.assertEquals(
|
||||
endpoints, utils.determine_endpoints('http://foohost.com',
|
||||
'http://foohost.com',
|
||||
'http://foohost.com'))
|
||||
|
||||
def test_determine_endpoints_neutron_api_rel(self):
|
||||
@@ -488,6 +491,7 @@ class NovaCCUtilsTests(CharmTestCase):
|
||||
'quantum_service': None})
|
||||
self.assertEquals(
|
||||
endpoints, utils.determine_endpoints('http://foohost.com',
|
||||
'http://foohost.com',
|
||||
'http://foohost.com'))
|
||||
|
||||
@patch.object(utils, 'known_hosts')
|
||||
|
||||
Reference in New Issue
Block a user