Move IP logic into separate module
This patch moves the IP logic into a module separate from the overall code. Ideally, this will serve as a way to abstract IP information handling to be populated by pluggable backends in addition to existing logic. Further work is needed to remove various code smells from tests and provide a more consistent public API that could be adopted as more generic. Change-Id: I09a57c43064eca46f776e941cbedb2296f43fbbc
This commit is contained in:
parent
b7354adacb
commit
35691fef01
@ -15,12 +15,11 @@
|
|||||||
#
|
#
|
||||||
# (c) 2014, Kevin Carter <kevin.carter@rackspace.com>
|
# (c) 2014, Kevin Carter <kevin.carter@rackspace.com>
|
||||||
|
|
||||||
|
import ip
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import netaddr
|
import netaddr
|
||||||
import os
|
import os
|
||||||
import Queue
|
|
||||||
import random
|
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
import yaml
|
import yaml
|
||||||
@ -32,7 +31,6 @@ from filesystem import save_inventory
|
|||||||
|
|
||||||
logger = logging.getLogger('osa-inventory')
|
logger = logging.getLogger('osa-inventory')
|
||||||
|
|
||||||
USED_IPS = set()
|
|
||||||
INVENTORY_SKEL = {
|
INVENTORY_SKEL = {
|
||||||
'_meta': {
|
'_meta': {
|
||||||
'hostvars': {}
|
'hostvars': {}
|
||||||
@ -127,40 +125,6 @@ class LxcHostsDefined(Exception):
|
|||||||
return self.message
|
return self.message
|
||||||
|
|
||||||
|
|
||||||
def get_ip_address(name, ip_q):
|
|
||||||
"""Return an IP address from our IP Address queue."""
|
|
||||||
try:
|
|
||||||
ip_addr = ip_q.get(timeout=1)
|
|
||||||
while ip_addr in USED_IPS:
|
|
||||||
ip_addr = ip_q.get(timeout=1)
|
|
||||||
else:
|
|
||||||
USED_IPS.add(ip_addr)
|
|
||||||
return str(ip_addr)
|
|
||||||
except AttributeError:
|
|
||||||
return None
|
|
||||||
except Queue.Empty:
|
|
||||||
raise SystemExit(
|
|
||||||
'Cannot retrieve requested amount of IP addresses. Increase the {}'
|
|
||||||
' range in your openstack_user_config.yml.'.format(name)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _load_ip_q(cidr, ip_q):
|
|
||||||
"""Load the IP queue with all IP address from a given cidr.
|
|
||||||
|
|
||||||
:param cidr: ``str`` IP address with cidr notation
|
|
||||||
"""
|
|
||||||
_all_ips = [str(i) for i in list(netaddr.IPNetwork(cidr))]
|
|
||||||
base_exclude = [
|
|
||||||
str(netaddr.IPNetwork(cidr).network),
|
|
||||||
str(netaddr.IPNetwork(cidr).broadcast)
|
|
||||||
]
|
|
||||||
USED_IPS.update(base_exclude)
|
|
||||||
for ip in random.sample(_all_ips, len(_all_ips)):
|
|
||||||
if ip not in USED_IPS:
|
|
||||||
ip_q.put(ip)
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_belongs_to(key, belongs_to, inventory):
|
def _parse_belongs_to(key, belongs_to, inventory):
|
||||||
"""Parse all items in a `belongs_to` list.
|
"""Parse all items in a `belongs_to` list.
|
||||||
|
|
||||||
@ -480,7 +444,7 @@ def user_defined_setup(config, inventory):
|
|||||||
for _k, _v in _value['host_vars'].items():
|
for _k, _v in _value['host_vars'].items():
|
||||||
hvs[_key][_k] = _v
|
hvs[_key][_k] = _v
|
||||||
|
|
||||||
USED_IPS.add(_value['ip'])
|
ip.USED_IPS.add(_value['ip'])
|
||||||
appended = append_if(array=inventory[key]['hosts'], item=_key)
|
appended = append_if(array=inventory[key]['hosts'], item=_key)
|
||||||
if appended:
|
if appended:
|
||||||
logger.debug("Added host %s to group %s",
|
logger.debug("Added host %s to group %s",
|
||||||
@ -537,20 +501,6 @@ def skel_load(skeleton, inventory):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _load_optional_q(config, cidr_name):
|
|
||||||
"""Load optional queue with ip addresses.
|
|
||||||
|
|
||||||
:param config: ``dict`` User defined information
|
|
||||||
:param cidr_name: ``str`` Name of the cidr name
|
|
||||||
"""
|
|
||||||
cidr = config.get(cidr_name)
|
|
||||||
ip_q = None
|
|
||||||
if cidr is not None:
|
|
||||||
ip_q = Queue.Queue()
|
|
||||||
_load_ip_q(cidr=cidr, ip_q=ip_q)
|
|
||||||
return ip_q
|
|
||||||
|
|
||||||
|
|
||||||
def network_entry(is_metal, interface,
|
def network_entry(is_metal, interface,
|
||||||
bridge=None, net_type=None, net_mtu=None):
|
bridge=None, net_type=None, net_mtu=None):
|
||||||
"""Return a network entry for a container."""
|
"""Return a network entry for a container."""
|
||||||
@ -663,7 +613,7 @@ def _add_additional_networks(key, inventory, ip_q, q_name, netmask, interface,
|
|||||||
if old_address in container and container[old_address]:
|
if old_address in container and container[old_address]:
|
||||||
network['address'] = container.pop(old_address)
|
network['address'] = container.pop(old_address)
|
||||||
elif not is_metal:
|
elif not is_metal:
|
||||||
address = get_ip_address(name=q_name, ip_q=ip_q)
|
address = ip.get_ip_address(name=q_name, ip_q=ip_q)
|
||||||
if address:
|
if address:
|
||||||
network['address'] = address
|
network['address'] = address
|
||||||
|
|
||||||
@ -737,7 +687,7 @@ def container_skel_load(container_skel, inventory, config):
|
|||||||
cidr_networks = config.get('cidr_networks')
|
cidr_networks = config.get('cidr_networks')
|
||||||
provider_queues = {}
|
provider_queues = {}
|
||||||
for net_name in cidr_networks:
|
for net_name in cidr_networks:
|
||||||
ip_q = _load_optional_q(
|
ip_q = ip.load_optional_q(
|
||||||
cidr_networks, cidr_name=net_name
|
cidr_networks, cidr_name=net_name
|
||||||
)
|
)
|
||||||
provider_queues[net_name] = ip_q
|
provider_queues[net_name] = ip_q
|
||||||
@ -806,38 +756,6 @@ def find_config_path(user_config_path=None):
|
|||||||
raise SystemExit('No config found at: {}'.format(path_check))
|
raise SystemExit('No config found at: {}'.format(path_check))
|
||||||
|
|
||||||
|
|
||||||
def _set_used_ips(user_defined_config, inventory):
|
|
||||||
"""Set all of the used ips into a global list.
|
|
||||||
|
|
||||||
:param user_defined_config: ``dict`` User defined configuration
|
|
||||||
:param inventory: ``dict`` Living inventory of containers and hosts
|
|
||||||
"""
|
|
||||||
used_ips = user_defined_config.get('used_ips')
|
|
||||||
if isinstance(used_ips, list):
|
|
||||||
for ip in used_ips:
|
|
||||||
split_ip = ip.split(',')
|
|
||||||
if len(split_ip) >= 2:
|
|
||||||
ip_range = list(
|
|
||||||
netaddr.iter_iprange(
|
|
||||||
split_ip[0],
|
|
||||||
split_ip[-1]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
USED_IPS.update([str(i) for i in ip_range])
|
|
||||||
else:
|
|
||||||
logger.debug("IP %s set as used", split_ip[0])
|
|
||||||
USED_IPS.add(split_ip[0])
|
|
||||||
|
|
||||||
# Find all used IP addresses and ensure that they are not used again
|
|
||||||
for host_entry in inventory['_meta']['hostvars'].values():
|
|
||||||
networks = host_entry.get('container_networks', dict())
|
|
||||||
for network_entry in networks.values():
|
|
||||||
address = network_entry.get('address')
|
|
||||||
if address:
|
|
||||||
logger.debug("IP %s set as used", address)
|
|
||||||
USED_IPS.add(address)
|
|
||||||
|
|
||||||
|
|
||||||
def _ensure_inventory_uptodate(inventory, container_skel):
|
def _ensure_inventory_uptodate(inventory, container_skel):
|
||||||
"""Update inventory if needed.
|
"""Update inventory if needed.
|
||||||
|
|
||||||
@ -1155,7 +1073,7 @@ def main(config=None, check=False, debug=False, environment=None, **kwargs):
|
|||||||
_parse_global_variables(user_cidr, dynamic_inventory, user_defined_config)
|
_parse_global_variables(user_cidr, dynamic_inventory, user_defined_config)
|
||||||
|
|
||||||
# Load all of the IP addresses that we know are used and set the queue
|
# Load all of the IP addresses that we know are used and set the queue
|
||||||
_set_used_ips(user_defined_config, dynamic_inventory)
|
ip.set_used_ips(user_defined_config, dynamic_inventory)
|
||||||
user_defined_setup(user_defined_config, dynamic_inventory)
|
user_defined_setup(user_defined_config, dynamic_inventory)
|
||||||
skel_setup(environment, dynamic_inventory)
|
skel_setup(environment, dynamic_inventory)
|
||||||
logger.debug("Loading physical skel.")
|
logger.debug("Loading physical skel.")
|
||||||
|
89
lib/ip.py
Normal file
89
lib/ip.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import netaddr
|
||||||
|
import Queue
|
||||||
|
import random
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger('osa-inventory')
|
||||||
|
|
||||||
|
|
||||||
|
USED_IPS = set()
|
||||||
|
|
||||||
|
|
||||||
|
def get_ip_address(name, ip_q):
|
||||||
|
"""Return an IP address from our IP Address queue."""
|
||||||
|
try:
|
||||||
|
ip_addr = ip_q.get(timeout=1)
|
||||||
|
while ip_addr in USED_IPS:
|
||||||
|
ip_addr = ip_q.get(timeout=1)
|
||||||
|
else:
|
||||||
|
USED_IPS.add(ip_addr)
|
||||||
|
return str(ip_addr)
|
||||||
|
except AttributeError:
|
||||||
|
return None
|
||||||
|
except Queue.Empty:
|
||||||
|
raise SystemExit(
|
||||||
|
'Cannot retrieve requested amount of IP addresses. Increase the %s'
|
||||||
|
' range in your openstack_user_config.yml.' % name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_ip_q(cidr, ip_q):
|
||||||
|
"""Load the IP queue with all IP address from a given cidr.
|
||||||
|
|
||||||
|
:param cidr: ``str`` IP address with cidr notation
|
||||||
|
"""
|
||||||
|
_all_ips = [str(i) for i in list(netaddr.IPNetwork(cidr))]
|
||||||
|
base_exclude = [
|
||||||
|
str(netaddr.IPNetwork(cidr).network),
|
||||||
|
str(netaddr.IPNetwork(cidr).broadcast)
|
||||||
|
]
|
||||||
|
USED_IPS.update(base_exclude)
|
||||||
|
for ip in random.sample(_all_ips, len(_all_ips)):
|
||||||
|
if ip not in USED_IPS:
|
||||||
|
ip_q.put(ip)
|
||||||
|
|
||||||
|
|
||||||
|
def load_optional_q(config, cidr_name):
|
||||||
|
"""Load optional queue with ip addresses.
|
||||||
|
|
||||||
|
:param config: ``dict`` User defined information
|
||||||
|
:param cidr_name: ``str`` Name of the cidr name
|
||||||
|
"""
|
||||||
|
cidr = config.get(cidr_name)
|
||||||
|
ip_q = None
|
||||||
|
if cidr is not None:
|
||||||
|
ip_q = Queue.Queue()
|
||||||
|
load_ip_q(cidr=cidr, ip_q=ip_q)
|
||||||
|
return ip_q
|
||||||
|
|
||||||
|
|
||||||
|
def set_used_ips(user_defined_config, inventory):
|
||||||
|
"""Set all of the used ips into a global list.
|
||||||
|
|
||||||
|
:param user_defined_config: ``dict`` User defined configuration
|
||||||
|
:param inventory: ``dict`` Living inventory of containers and hosts
|
||||||
|
"""
|
||||||
|
used_ips = user_defined_config.get('used_ips')
|
||||||
|
if isinstance(used_ips, list):
|
||||||
|
for ip in used_ips:
|
||||||
|
split_ip = ip.split(',')
|
||||||
|
if len(split_ip) >= 2:
|
||||||
|
ip_range = list(
|
||||||
|
netaddr.iter_iprange(
|
||||||
|
split_ip[0],
|
||||||
|
split_ip[-1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
USED_IPS.update([str(i) for i in ip_range])
|
||||||
|
else:
|
||||||
|
logger.debug("IP %s set as used", split_ip[0])
|
||||||
|
USED_IPS.add(split_ip[0])
|
||||||
|
|
||||||
|
# Find all used IP addresses and ensure that they are not used again
|
||||||
|
for host_entry in inventory['_meta']['hostvars'].values():
|
||||||
|
networks = host_entry.get('container_networks', dict())
|
||||||
|
for network_entry in networks.values():
|
||||||
|
address = network_entry.get('address')
|
||||||
|
if address:
|
||||||
|
logger.debug("IP %s set as used", address)
|
||||||
|
USED_IPS.add(address)
|
@ -473,7 +473,7 @@ class TestIps(unittest.TestCase):
|
|||||||
# tearDown is ineffective for this loop, so clean the USED_IPs
|
# tearDown is ineffective for this loop, so clean the USED_IPs
|
||||||
# on each run
|
# on each run
|
||||||
inventory = None
|
inventory = None
|
||||||
di.USED_IPS = set()
|
di.ip.USED_IPS = set()
|
||||||
|
|
||||||
# Mock out the context manager being used to write files.
|
# Mock out the context manager being used to write files.
|
||||||
# We don't need to hit the file system for this test.
|
# We don't need to hit the file system for this test.
|
||||||
@ -496,7 +496,8 @@ class TestIps(unittest.TestCase):
|
|||||||
def test_empty_ip_queue(self):
|
def test_empty_ip_queue(self):
|
||||||
q = Queue.Queue()
|
q = Queue.Queue()
|
||||||
with self.assertRaises(SystemExit) as context:
|
with self.assertRaises(SystemExit) as context:
|
||||||
di.get_ip_address('test', q)
|
# TODO(nrb): import and use ip module directly
|
||||||
|
di.ip.get_ip_address('test', q)
|
||||||
expectedLog = ("Cannot retrieve requested amount of IP addresses. "
|
expectedLog = ("Cannot retrieve requested amount of IP addresses. "
|
||||||
"Increase the test range in your "
|
"Increase the test range in your "
|
||||||
"openstack_user_config.yml.")
|
"openstack_user_config.yml.")
|
||||||
@ -505,7 +506,7 @@ class TestIps(unittest.TestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# Since the get_ip_address function touches USED_IPS,
|
# Since the get_ip_address function touches USED_IPS,
|
||||||
# and USED_IPS is currently a global var, make sure we clean it out
|
# and USED_IPS is currently a global var, make sure we clean it out
|
||||||
di.USED_IPS = set()
|
di.ip.USED_IPS = set()
|
||||||
|
|
||||||
|
|
||||||
class TestConfigCheckBase(unittest.TestCase):
|
class TestConfigCheckBase(unittest.TestCase):
|
||||||
@ -1118,7 +1119,7 @@ class TestOverridingEnvIntegration(OverridingEnvBase):
|
|||||||
class TestSetUsedIPS(unittest.TestCase):
|
class TestSetUsedIPS(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Clean up the used ips in case other tests didn't.
|
# Clean up the used ips in case other tests didn't.
|
||||||
di.USED_IPS = set()
|
di.ip.USED_IPS = set()
|
||||||
|
|
||||||
# Create a fake inventory just for this test.
|
# Create a fake inventory just for this test.
|
||||||
self.inventory = {'_meta': {'hostvars': {
|
self.inventory = {'_meta': {'hostvars': {
|
||||||
@ -1133,14 +1134,16 @@ class TestSetUsedIPS(unittest.TestCase):
|
|||||||
def test_adding_inventory_used_ips(self):
|
def test_adding_inventory_used_ips(self):
|
||||||
config = {'used_ips': None}
|
config = {'used_ips': None}
|
||||||
|
|
||||||
di._set_used_ips(config, self.inventory)
|
# TODO(nrb): This is a smell, needs to set more directly
|
||||||
|
|
||||||
self.assertEqual(len(di.USED_IPS), 2)
|
di.ip.set_used_ips(config, self.inventory)
|
||||||
self.assertIn('172.12.1.1', di.USED_IPS)
|
|
||||||
self.assertIn('172.12.1.2', di.USED_IPS)
|
self.assertEqual(len(di.ip.USED_IPS), 2)
|
||||||
|
self.assertIn('172.12.1.1', di.ip.USED_IPS)
|
||||||
|
self.assertIn('172.12.1.2', di.ip.USED_IPS)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
di.USED_IPS = set()
|
di.ip.USED_IPS = set()
|
||||||
|
|
||||||
|
|
||||||
class TestConfigCheckFunctional(TestConfigCheckBase):
|
class TestConfigCheckFunctional(TestConfigCheckBase):
|
||||||
|
Loading…
Reference in New Issue
Block a user