diff --git a/package/cloudshell/cp/openstack/domain/services/neutron/neutron_network_service.py b/package/cloudshell/cp/openstack/domain/services/neutron/neutron_network_service.py index 7204d75..73f035b 100644 --- a/package/cloudshell/cp/openstack/domain/services/neutron/neutron_network_service.py +++ b/package/cloudshell/cp/openstack/domain/services/neutron/neutron_network_service.py @@ -3,7 +3,7 @@ import traceback import time - +import ipaddress class NeutronNetworkService(object): """ @@ -155,57 +155,74 @@ def _get_unused_cidr(self, client, cp_resvd_cidrs, logger): :return str: """ - # Algorithm below is a very simplistic one where we choose one of the three prefixes and then use - # /24 networks starting with that prefix. This algorithm will break if all three 10.X, 192.168.X and 172.X - # networks are used in a given On Prem Network. - + # We basically start with a 10.0. network to find a subnet that does not overlap with + # either the reserved_cidrs or currently allocated CIDRs + # currently supports /24 subnets logger.debug("reserved CIDRs: {0}".format(cp_resvd_cidrs)) - candidate_prefixes = {'10': '10.0', '192.168': '192.168', '172': '172.0'} - cp_resvd_cidrs = cp_resvd_cidrs.split(",") - possible_prefixes = filter(lambda x: any(map(lambda y: not y.strip().startswith(x), cp_resvd_cidrs)), - candidate_prefixes.keys()) - logger.debug("Possible Prefixes that can be used: {0}".format(possible_prefixes)) - if not possible_prefixes: - return None - - prefix = possible_prefixes[0] - subnet_prefix = candidate_prefixes[prefix] - - # Get all subnets that start with 'our prefix' - subnets = client.list_subnets(fields=['cidr', 'id'])['subnets'] - subnet_cidrs = map(lambda x: x.get('cidr'), subnets) - - allocated_subnets = [] - for subnet in subnets: - if subnet['cidr'].startswith(prefix): - allocated_subnets.append(subnet['cidr']) - - allocated_subnets.sort() - logger.debug("Allocated Subnets: {0}".format(",".join(allocated_subnets))) - - if not allocated_subnets: - subnet_num = 0 - else: - last_subnet = allocated_subnets[-1] - subnet_num = int(last_subnet.split("/")[0].split(".")[2]) - subnet_num += 1 - if subnet_num == 255: - subnet_num = 0 - cidr = ".".join([subnet_prefix, str(subnet_num), "0/24"]) - while cidr in subnet_cidrs: - subnet_num += 1 - cidr = ".".join([subnet_prefix, str(subnet_num), "0/24"]) - else: - cidr = ".".join([subnet_prefix,str(subnet_num), "0/24"]) + blacklist_cidrs = map(lambda x: x.strip(), cp_resvd_cidrs.split(",")) + + current_subnets = client.list_subnets(fields=['cidr', 'id'])['subnets'] + current_subnets_cidrs = map(lambda x: unicode(x.get('cidr')), current_subnets) + + # Total CIDRs we don't care about are - reserved + currently allocated + + blacklist_cidrs += current_subnets_cidrs + blacklist_cidrs = map(lambda x: unicode(x), blacklist_cidrs) + blacklist_subnets = map(lambda x: ipaddress.IPv4Network(x), blacklist_cidrs) + + # start with a 10 subnet + found_subnet = None + first_octet = 10 + for i in range(256): + second_octet = i + for j in range(256): + third_octet = j + subnet_str = '{0}.{1}.{2}.0/24'.format(first_octet, second_octet, third_octet) + u_subnet_str = unicode(subnet_str) + u_subnet = ipaddress.IPv4Network(u_subnet_str) + # print u_subnet, blacklist_subnets + if not any(map(lambda x: u_subnet.overlaps(x), blacklist_subnets)): + found_subnet = u_subnet + break + if found_subnet: + break + + if not found_subnet: + first_octet = 172 + for i in range(16, 32): + second_octet = i + for j in range(256): + third_octet = j + subnet_str = '{0}.{1}.{2}.0/24'.format(first_octet, second_octet, third_octet) + u_subnet_str = unicode(subnet_str) + u_subnet = ipaddress.IPv4Network(u_subnet_str) + if not any(map(lambda x: u_subnet.overlaps(x), blacklist_subnets)): + found_subnet = u_subnet + break + if found_subnet: + break - logger.debug("Found {0} CIDR".format(cidr)) + if not found_subnet: + first_octet = 192 + second_octet = 168 + for j in range(256): + third_octet = j + subnet_str = '{0}.{1}.{2}.0/24'.format(first_octet, second_octet, third_octet) + u_subnet_str = unicode(subnet_str) + u_subnet = ipaddress.IPv4Network(u_subnet_str) + if not any(map(lambda x: u_subnet.overlaps(x), blacklist_subnets)): + found_subnet = u_subnet + break - if subnet_num == 255: + if not found_subnet: return None + cidr = str(found_subnet) + return cidr + def create_floating_ip(self, openstack_session, floating_ip_subnet_id, logger): """ diff --git a/package/tests/test_cp/test_openstack/test_domain/test_services/test_neutron/test_neutron_network_service.py b/package/tests/test_cp/test_openstack/test_domain/test_services/test_neutron/test_neutron_network_service.py index 7688f3d..e30add3 100644 --- a/package/tests/test_cp/test_openstack/test_domain/test_services/test_neutron/test_neutron_network_service.py +++ b/package/tests/test_cp/test_openstack/test_domain/test_services/test_neutron/test_neutron_network_service.py @@ -70,6 +70,37 @@ def test_get_network_with_segmentation_id_no_network(self): logger=self.mock_logger) self.assertEqual(result, None) + def test_valid_cidr_returned(self): + + mock_client = Mock() + test_neutron_network_service.neutron_client.Client = Mock(return_value=mock_client) + mock_client.create_subnet = Mock(return_value={'subnet': 'subnet success'}) + + mock_return_subnets = {'subnets': [{'cidr': '10.0.0.0/24', 'id': 'test-id-1'}, + {'cidr': '10.0.1.0/24', 'id': 'test-id-2'}]} + + test_reserved_subnets = '172.0.0.0/8, 192.168.0.0/24' + mock_client.list_subnets = Mock(return_value=mock_return_subnets) + result = self.network_service._get_unused_cidr(client=mock_client, + cp_resvd_cidrs=test_reserved_subnets, + logger=self.mock_logger) + self.assertEqual(result, '10.0.2.0/24') + + def none_cidr_returned(self): + mock_client = Mock() + test_neutron_network_service.neutron_client.Client = Mock(return_value=mock_client) + mock_client.create_subnet = Mock(return_value={'subnet': 'subnet success'}) + + mock_return_subnets = {'subnets': [{'cidr': '10.0.0.0/24', 'id': 'test-id-1'}, + {'cidr': '10.0.1.0/24', 'id': 'test-id-2'}]} + + test_reserved_subnets = '10.0.0.0/8, 172.16.0.0/12 , 192.168.0.0/24' + mock_client.list_subnets = Mock(return_value=mock_return_subnets) + result = self.network_service._get_unused_cidr(client=mock_client, + cp_resvd_cidrs=test_reserved_subnets, + logger=self.mock_logger) + self.assertEqual(result, None) + def test_create_and_attach_subnet_to_net_success(self): test_net_id = 'test-net-id' @@ -77,10 +108,18 @@ def test_create_and_attach_subnet_to_net_success(self): test_neutron_network_service.neutron_client.Client = Mock(return_value=mock_client) mock_client.create_subnet = Mock(return_value={'subnet':'subnet success'}) - self.network_service._get_unused_cidr = Mock(return_value = '10.0.0.0/24') + mock_return_subnets = {'subnets':[{'cidr': '192.168.1.0/24', 'id':'test-id-1'}, + {'cidr': '192.168.1.0/24', 'id': 'test-id-2'}]} + + test_reserved_subnets = '10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/24' + mock_client.list_subnets = Mock(return_value=mock_return_subnets) + + cp_resource_model = Mock() + cp_resource_model.reserved_networks = test_reserved_subnets + # self.network_service._get_unused_cidr = Mock(return_value = '10.0.0.0/24') result = self.network_service.create_and_attach_subnet_to_net(openstack_session=self.openstack_session, - cp_resource_model=Mock(), + cp_resource_model=cp_resource_model, net_id=test_net_id, logger=self.mock_logger) self.assertEqual(result, 'subnet success')