Merge pull request #121 from asadoughi/allocation_pools
Allocation pools
This commit is contained in:
		| @@ -16,13 +16,9 @@ | |||||||
| #    under the License. | #    under the License. | ||||||
|  |  | ||||||
| from neutron.api import extensions | from neutron.api import extensions | ||||||
| from neutron.api.v2 import attributes |  | ||||||
|  |  | ||||||
| EXTENDED_ATTRIBUTES_2_0 = { | EXTENDED_ATTRIBUTES_2_0 = { | ||||||
|     'subnets': { |     'subnets': { | ||||||
|         "allocation_pools": {'allow_post': True, 'allow_put': True, |  | ||||||
|                              'default': attributes.ATTR_NOT_SPECIFIED, |  | ||||||
|                              'is_visible': False}, |  | ||||||
|         "enable_dhcp": {'allow_post': False, 'allow_put': False, |         "enable_dhcp": {'allow_post': False, 'allow_put': False, | ||||||
|                         'default': False, |                         'default': False, | ||||||
|                         'is_visible': True}, |                         'is_visible': True}, | ||||||
|   | |||||||
| @@ -30,7 +30,8 @@ LOG = logging.getLogger("neutron") | |||||||
|  |  | ||||||
|  |  | ||||||
| class QuarkIpam(object): | class QuarkIpam(object): | ||||||
|     def _get_ip_policy_rule_set(self, subnet): |     @staticmethod | ||||||
|  |     def get_ip_policy_rule_set(subnet): | ||||||
|         ip_policy = subnet["ip_policy"] or \ |         ip_policy = subnet["ip_policy"] or \ | ||||||
|             subnet["network"]["ip_policy"] or \ |             subnet["network"]["ip_policy"] or \ | ||||||
|             dict() |             dict() | ||||||
| @@ -38,7 +39,7 @@ class QuarkIpam(object): | |||||||
|         ip_policy_rules = netaddr.IPSet( |         ip_policy_rules = netaddr.IPSet( | ||||||
|             [netaddr.IPNetwork((int(ippr["address"]), ippr["prefix"])) |             [netaddr.IPNetwork((int(ippr["address"]), ippr["prefix"])) | ||||||
|              for ippr in ip_policy_rules]) |              for ippr in ip_policy_rules]) | ||||||
|         subnet_set = netaddr.IPSet(netaddr.IPNetwork(subnet["cidr"])) |         subnet_set = netaddr.IPSet([netaddr.IPNetwork(subnet["cidr"])]) | ||||||
|         ip_policy_rules = subnet_set & ip_policy_rules |         ip_policy_rules = subnet_set & ip_policy_rules | ||||||
|         return ip_policy_rules |         return ip_policy_rules | ||||||
|  |  | ||||||
| @@ -57,7 +58,7 @@ class QuarkIpam(object): | |||||||
|  |  | ||||||
|             ip_policy_rules = None |             ip_policy_rules = None | ||||||
|             if not ip_address: |             if not ip_address: | ||||||
|                 ip_policy_rules = self._get_ip_policy_rule_set(subnet) |                 ip_policy_rules = self.get_ip_policy_rule_set(subnet) | ||||||
|             policy_size = ip_policy_rules.size if ip_policy_rules else 0 |             policy_size = ip_policy_rules.size if ip_policy_rules else 0 | ||||||
|             if ipnet.size > (ips_in_subnet + policy_size): |             if ipnet.size > (ips_in_subnet + policy_size): | ||||||
|                 return subnet |                 return subnet | ||||||
| @@ -117,7 +118,7 @@ class QuarkIpam(object): | |||||||
|  |  | ||||||
|         subnet = self._choose_available_subnet( |         subnet = self._choose_available_subnet( | ||||||
|             elevated, net_id, ip_address=ip_address, version=version) |             elevated, net_id, ip_address=ip_address, version=version) | ||||||
|         ip_policy_rules = self._get_ip_policy_rule_set(subnet) |         ip_policy_rules = self.get_ip_policy_rule_set(subnet) | ||||||
|  |  | ||||||
|         # Creating this IP for the first time |         # Creating this IP for the first time | ||||||
|         next_ip = None |         next_ip = None | ||||||
|   | |||||||
| @@ -251,6 +251,7 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2, | |||||||
|         gateway_ip = _pop_param(sub_attrs, "gateway_ip", str(cidr[1])) |         gateway_ip = _pop_param(sub_attrs, "gateway_ip", str(cidr[1])) | ||||||
|         dns_ips = _pop_param(sub_attrs, "dns_nameservers", []) |         dns_ips = _pop_param(sub_attrs, "dns_nameservers", []) | ||||||
|         routes = _pop_param(sub_attrs, "host_routes", []) |         routes = _pop_param(sub_attrs, "host_routes", []) | ||||||
|  |         allocation_pools = _pop_param(sub_attrs, "allocation_pools", []) | ||||||
|  |  | ||||||
|         new_subnet = db_api.subnet_create(context, **sub_attrs) |         new_subnet = db_api.subnet_create(context, **sub_attrs) | ||||||
|  |  | ||||||
| @@ -270,6 +271,16 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2, | |||||||
|             new_subnet["dns_nameservers"].append(db_api.dns_create( |             new_subnet["dns_nameservers"].append(db_api.dns_create( | ||||||
|                 context, ip=netaddr.IPAddress(dns_ip))) |                 context, ip=netaddr.IPAddress(dns_ip))) | ||||||
|  |  | ||||||
|  |         if allocation_pools: | ||||||
|  |             exclude = netaddr.IPSet([cidr]) | ||||||
|  |             for p in allocation_pools: | ||||||
|  |                 x = netaddr.IPSet(netaddr.IPRange(p["start"], p["end"])) | ||||||
|  |                 exclude = exclude - x | ||||||
|  |             new_subnet["ip_policy"] = db_api.ip_policy_create(context, | ||||||
|  |                                                               exclude=exclude) | ||||||
|  |         # HACK(amir): force backref for ip_policy | ||||||
|  |         if not new_subnet["network"]: | ||||||
|  |             new_subnet["network"] = net | ||||||
|         subnet_dict = v._make_subnet_dict(new_subnet, |         subnet_dict = v._make_subnet_dict(new_subnet, | ||||||
|                                           default_route=DEFAULT_ROUTE) |                                           default_route=DEFAULT_ROUTE) | ||||||
|         subnet_dict["gateway_ip"] = gateway_ip |         subnet_dict["gateway_ip"] = gateway_ip | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ View Helpers for Quark Plugin | |||||||
|  |  | ||||||
| import netaddr | import netaddr | ||||||
|  |  | ||||||
|  | from quark.ipam import QuarkIpam | ||||||
| from quark import network_strategy | from quark import network_strategy | ||||||
| STRATEGY = network_strategy.STRATEGY | STRATEGY = network_strategy.STRATEGY | ||||||
|  |  | ||||||
| @@ -42,12 +43,38 @@ def _make_subnet_dict(subnet, default_route=None, fields=None): | |||||||
|     dns_nameservers = [str(netaddr.IPAddress(dns["ip"])) |     dns_nameservers = [str(netaddr.IPAddress(dns["ip"])) | ||||||
|                        for dns in subnet.get("dns_nameservers")] |                        for dns in subnet.get("dns_nameservers")] | ||||||
|     net_id = STRATEGY.get_parent_network(subnet["network_id"]) |     net_id = STRATEGY.get_parent_network(subnet["network_id"]) | ||||||
|  |  | ||||||
|  |     def _allocation_pools(subnet): | ||||||
|  |         ip_policy_rules = QuarkIpam.get_ip_policy_rule_set(subnet) | ||||||
|  |         cidr = netaddr.IPSet([netaddr.IPNetwork(subnet["cidr"])]) | ||||||
|  |         allocatable = cidr - ip_policy_rules | ||||||
|  |  | ||||||
|  |         cidrs = allocatable.iter_cidrs() | ||||||
|  |         if len(cidrs) == 0: | ||||||
|  |             return [] | ||||||
|  |         if len(cidrs) == 1: | ||||||
|  |             return [dict(start=str(cidrs[0][0]), | ||||||
|  |                          end=str(cidrs[0][-1]))] | ||||||
|  |  | ||||||
|  |         pool_start = cidrs[0][0] | ||||||
|  |         prev_cidr_end = cidrs[0][-1] | ||||||
|  |         pools = [] | ||||||
|  |         for cidr in cidrs[1:]: | ||||||
|  |             cidr_start = cidr[0] | ||||||
|  |             if prev_cidr_end + 1 != cidr_start: | ||||||
|  |                 pools.append(dict(start=str(pool_start), | ||||||
|  |                                   end=str(prev_cidr_end))) | ||||||
|  |                 pool_start = cidr_start | ||||||
|  |             prev_cidr_end = cidr[-1] | ||||||
|  |         pools.append(dict(start=str(pool_start), end=str(prev_cidr_end))) | ||||||
|  |         return pools | ||||||
|  |  | ||||||
|     res = {"id": subnet.get('id'), |     res = {"id": subnet.get('id'), | ||||||
|            "name": subnet.get('name'), |            "name": subnet.get('name'), | ||||||
|            "tenant_id": subnet.get('tenant_id'), |            "tenant_id": subnet.get('tenant_id'), | ||||||
|            "network_id": net_id, |            "network_id": net_id, | ||||||
|            "ip_version": subnet.get('ip_version'), |            "ip_version": subnet.get('ip_version'), | ||||||
|            "allocation_pools": [], |            "allocation_pools": _allocation_pools(subnet), | ||||||
|            "dns_nameservers": dns_nameservers or [], |            "dns_nameservers": dns_nameservers or [], | ||||||
|            "cidr": subnet.get('cidr'), |            "cidr": subnet.get('cidr'), | ||||||
|            "enable_dhcp": None} |            "enable_dhcp": None} | ||||||
|   | |||||||
| @@ -89,11 +89,11 @@ class TestQuarkGetSubnets(TestQuarkPlugin): | |||||||
|             for subnet in subnets: |             for subnet in subnets: | ||||||
|                 s_dict = subnet.copy() |                 s_dict = subnet.copy() | ||||||
|                 s_dict["routes"] = route_models |                 s_dict["routes"] = route_models | ||||||
|                 s = models.Subnet() |                 s = models.Subnet(network=models.Network()) | ||||||
|                 s.update(s_dict) |                 s.update(s_dict) | ||||||
|                 subnet_models.append(s) |                 subnet_models.append(s) | ||||||
|         elif subnets: |         elif subnets: | ||||||
|             mod = models.Subnet() |             mod = models.Subnet(network=models.Network()) | ||||||
|             mod.update(subnets) |             mod.update(subnets) | ||||||
|             mod["routes"] = route_models |             mod["routes"] = route_models | ||||||
|             subnet_models = mod |             subnet_models = mod | ||||||
| @@ -111,7 +111,7 @@ class TestQuarkGetSubnets(TestQuarkPlugin): | |||||||
|         subnet = dict(id=subnet_id, network_id=1, name=subnet_id, |         subnet = dict(id=subnet_id, network_id=1, name=subnet_id, | ||||||
|                       tenant_id=self.context.tenant_id, ip_version=4, |                       tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|                       cidr="192.168.0.0/24", gateway_ip="192.168.0.1", |                       cidr="192.168.0.0/24", gateway_ip="192.168.0.1", | ||||||
|                       allocation_pools=[], dns_nameservers=[], |                       dns_nameservers=[], | ||||||
|                       enable_dhcp=None) |                       enable_dhcp=None) | ||||||
|         expected_route = dict(destination=route["cidr"], |         expected_route = dict(destination=route["cidr"], | ||||||
|                               nexthop=route["gateway"]) |                               nexthop=route["gateway"]) | ||||||
| @@ -141,7 +141,7 @@ class TestQuarkGetSubnets(TestQuarkPlugin): | |||||||
|         subnet = dict(id=subnet_id, network_id=1, name=subnet_id, |         subnet = dict(id=subnet_id, network_id=1, name=subnet_id, | ||||||
|                       tenant_id=self.context.tenant_id, ip_version=4, |                       tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|                       cidr="192.168.0.0/24", gateway_ip="192.168.0.1", |                       cidr="192.168.0.0/24", gateway_ip="192.168.0.1", | ||||||
|                       allocation_pools=[], dns_nameservers=[], |                       dns_nameservers=[], | ||||||
|                       enable_dhcp=None) |                       enable_dhcp=None) | ||||||
|  |  | ||||||
|         with self._stubs(subnets=subnet, routes=[route]): |         with self._stubs(subnets=subnet, routes=[route]): | ||||||
| @@ -174,6 +174,9 @@ class TestQuarkCreateSubnetOverlapping(TestQuarkPlugin): | |||||||
|         ) as (net_find, subnet_find, subnet_create): |         ) as (net_find, subnet_find, subnet_create): | ||||||
|             net_find.return_value = network |             net_find.return_value = network | ||||||
|             subnet_find.return_value = subnet_models |             subnet_find.return_value = subnet_models | ||||||
|  |             subnet_create.return_value = models.Subnet( | ||||||
|  |                 network=models.Network(), | ||||||
|  |                 cidr="192.168.1.1/24") | ||||||
|             yield subnet_create |             yield subnet_create | ||||||
|  |  | ||||||
|     def test_create_subnet_overlapping_true(self): |     def test_create_subnet_overlapping_true(self): | ||||||
| @@ -207,13 +210,63 @@ class TestQuarkCreateSubnetOverlapping(TestQuarkPlugin): | |||||||
|                 self.plugin.create_subnet(self.context, s) |                 self.plugin.create_subnet(self.context, s) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestQuarkCreateSubnetAllocationPools(TestQuarkPlugin): | ||||||
|  |     @contextlib.contextmanager | ||||||
|  |     def _stubs(self, subnet): | ||||||
|  |         s = models.Subnet(network=models.Network(id=1, subnets=[])) | ||||||
|  |         s.update(subnet) | ||||||
|  |  | ||||||
|  |         with contextlib.nested( | ||||||
|  |             mock.patch("quark.db.api.network_find"), | ||||||
|  |             mock.patch("quark.db.api.subnet_find"), | ||||||
|  |             mock.patch("quark.db.api.subnet_create") | ||||||
|  |         ) as (net_find, subnet_find, subnet_create): | ||||||
|  |             net_find.return_value = s["network"] | ||||||
|  |             subnet_find.return_value = [] | ||||||
|  |             subnet_create.return_value = s | ||||||
|  |             yield subnet_create | ||||||
|  |  | ||||||
|  |     def test_create_subnet_allocation_pools_zero(self): | ||||||
|  |         s = dict(subnet=dict( | ||||||
|  |             cidr="192.168.1.1/24", | ||||||
|  |             network_id=1)) | ||||||
|  |         with self._stubs(s["subnet"]) as subnet_create: | ||||||
|  |             resp = self.plugin.create_subnet(self.context, s) | ||||||
|  |             self.assertEqual(subnet_create.call_count, 1) | ||||||
|  |             self.assertEqual(resp["allocation_pools"], | ||||||
|  |                              [dict(start="192.168.1.0", end="192.168.1.255")]) | ||||||
|  |  | ||||||
|  |     def test_create_subnet_allocation_pools_one(self): | ||||||
|  |         pools = [dict(start="192.168.1.10", end="192.168.1.20")] | ||||||
|  |         s = dict(subnet=dict( | ||||||
|  |             allocation_pools=pools, | ||||||
|  |             cidr="192.168.1.1/24", | ||||||
|  |             network_id=1)) | ||||||
|  |         with self._stubs(s["subnet"]) as subnet_create: | ||||||
|  |             resp = self.plugin.create_subnet(self.context, s) | ||||||
|  |             self.assertEqual(subnet_create.call_count, 1) | ||||||
|  |             self.assertEqual(resp["allocation_pools"], pools) | ||||||
|  |  | ||||||
|  |     def test_create_subnet_allocation_pools_two(self): | ||||||
|  |         pools = [dict(start="192.168.1.10", end="192.168.1.20"), | ||||||
|  |                  dict(start="192.168.1.40", end="192.168.1.50")] | ||||||
|  |         s = dict(subnet=dict( | ||||||
|  |             allocation_pools=pools, | ||||||
|  |             cidr="192.168.1.1/24", | ||||||
|  |             network_id=1)) | ||||||
|  |         with self._stubs(s["subnet"]) as subnet_create: | ||||||
|  |             resp = self.plugin.create_subnet(self.context, s) | ||||||
|  |             self.assertEqual(subnet_create.call_count, 1) | ||||||
|  |             self.assertEqual(resp["allocation_pools"], pools) | ||||||
|  |  | ||||||
|  |  | ||||||
| # TODO(amir): Refactor the tests to test individual subnet attributes. | # TODO(amir): Refactor the tests to test individual subnet attributes. | ||||||
| # * copy.deepcopy was necessary to maintain tests on keys, which is a bit ugly. | # * copy.deepcopy was necessary to maintain tests on keys, which is a bit ugly. | ||||||
| # * workaround is also in place for lame ATTR_NOT_SPECIFIED object() | # * workaround is also in place for lame ATTR_NOT_SPECIFIED object() | ||||||
| class TestQuarkCreateSubnet(TestQuarkPlugin): | class TestQuarkCreateSubnet(TestQuarkPlugin): | ||||||
|     @contextlib.contextmanager |     @contextlib.contextmanager | ||||||
|     def _stubs(self, subnet=None, network=None, routes=None, dns=None): |     def _stubs(self, subnet=None, network=None, routes=None, dns=None): | ||||||
|         subnet_mod = models.Subnet() |         subnet_mod = models.Subnet(network=models.Network()) | ||||||
|         dns_ips = subnet.pop("dns_nameservers", []) |         dns_ips = subnet.pop("dns_nameservers", []) | ||||||
|         host_routes = subnet.pop("host_routes", []) |         host_routes = subnet.pop("host_routes", []) | ||||||
|         subnet_mod.update(subnet) |         subnet_mod.update(subnet) | ||||||
| @@ -242,7 +295,6 @@ class TestQuarkCreateSubnet(TestQuarkPlugin): | |||||||
|             subnet=dict(network_id=1, |             subnet=dict(network_id=1, | ||||||
|                         tenant_id=self.context.tenant_id, ip_version=4, |                         tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|                         cidr="172.16.0.0/24", gateway_ip="0.0.0.0", |                         cidr="172.16.0.0/24", gateway_ip="0.0.0.0", | ||||||
|                         allocation_pools=[], |  | ||||||
|                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, |                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, | ||||||
|                         host_routes=neutron_attrs.ATTR_NOT_SPECIFIED, |                         host_routes=neutron_attrs.ATTR_NOT_SPECIFIED, | ||||||
|                         enable_dhcp=None)) |                         enable_dhcp=None)) | ||||||
| @@ -281,7 +333,6 @@ class TestQuarkCreateSubnet(TestQuarkPlugin): | |||||||
|             subnet=dict(network_id=1, |             subnet=dict(network_id=1, | ||||||
|                         tenant_id=self.context.tenant_id, ip_version=4, |                         tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|                         cidr="172.16.0.0/24", |                         cidr="172.16.0.0/24", | ||||||
|                         allocation_pools=[], |  | ||||||
|                         gateway_ip=neutron_attrs.ATTR_NOT_SPECIFIED, |                         gateway_ip=neutron_attrs.ATTR_NOT_SPECIFIED, | ||||||
|                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, |                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, | ||||||
|                         enable_dhcp=None)) |                         enable_dhcp=None)) | ||||||
| @@ -316,7 +367,6 @@ class TestQuarkCreateSubnet(TestQuarkPlugin): | |||||||
|             subnet=dict(network_id=1, |             subnet=dict(network_id=1, | ||||||
|                         tenant_id=self.context.tenant_id, ip_version=4, |                         tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|                         cidr="172.16.0.0/24", gateway_ip="0.0.0.0", |                         cidr="172.16.0.0/24", gateway_ip="0.0.0.0", | ||||||
|                         allocation_pools=[], |  | ||||||
|                         dns_nameservers=["4.2.2.1", "4.2.2.2"], |                         dns_nameservers=["4.2.2.1", "4.2.2.2"], | ||||||
|                         enable_dhcp=None)) |                         enable_dhcp=None)) | ||||||
|         network = dict(network_id=1) |         network = dict(network_id=1) | ||||||
| @@ -345,7 +395,6 @@ class TestQuarkCreateSubnet(TestQuarkPlugin): | |||||||
|             subnet=dict(network_id=1, |             subnet=dict(network_id=1, | ||||||
|                         tenant_id=self.context.tenant_id, ip_version=4, |                         tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|                         cidr="172.16.0.0/24", gateway_ip="0.0.0.0", |                         cidr="172.16.0.0/24", gateway_ip="0.0.0.0", | ||||||
|                         allocation_pools=[], |  | ||||||
|                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, |                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, | ||||||
|                         host_routes=[{"destination": "1.1.1.1/8", |                         host_routes=[{"destination": "1.1.1.1/8", | ||||||
|                                       "nexthop": "172.16.0.4"}], |                                       "nexthop": "172.16.0.4"}], | ||||||
| @@ -379,7 +428,6 @@ class TestQuarkCreateSubnet(TestQuarkPlugin): | |||||||
|             subnet=dict(network_id=1, |             subnet=dict(network_id=1, | ||||||
|                         tenant_id=self.context.tenant_id, ip_version=4, |                         tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|                         cidr="172.16.0.0/24", |                         cidr="172.16.0.0/24", | ||||||
|                         allocation_pools=[], |  | ||||||
|                         gateway_ip=neutron_attrs.ATTR_NOT_SPECIFIED, |                         gateway_ip=neutron_attrs.ATTR_NOT_SPECIFIED, | ||||||
|                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, |                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, | ||||||
|                         host_routes=[{"destination": "0.0.0.0/0", |                         host_routes=[{"destination": "0.0.0.0/0", | ||||||
| @@ -420,7 +468,6 @@ class TestQuarkCreateSubnet(TestQuarkPlugin): | |||||||
|                         tenant_id=self.context.tenant_id, ip_version=4, |                         tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|                         cidr="172.16.0.0/24", |                         cidr="172.16.0.0/24", | ||||||
|                         gateway_ip="172.16.0.3", |                         gateway_ip="172.16.0.3", | ||||||
|                         allocation_pools=[], |  | ||||||
|                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, |                         dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED, | ||||||
|                         host_routes=[{"destination": "0.0.0.0/0", |                         host_routes=[{"destination": "0.0.0.0/0", | ||||||
|                                       "nexthop": "172.16.0.4"}], |                                       "nexthop": "172.16.0.4"}], | ||||||
| @@ -473,7 +520,6 @@ class TestQuarkUpdateSubnet(TestQuarkPlugin): | |||||||
|             network_id=1, |             network_id=1, | ||||||
|             tenant_id=self.context.tenant_id, ip_version=4, |             tenant_id=self.context.tenant_id, ip_version=4, | ||||||
|             cidr="172.16.0.0/24", |             cidr="172.16.0.0/24", | ||||||
|             allocation_pools=[], |  | ||||||
|             host_routes=host_routes, |             host_routes=host_routes, | ||||||
|             dns_nameservers=["4.2.2.1", "4.2.2.2"], |             dns_nameservers=["4.2.2.1", "4.2.2.2"], | ||||||
|             enable_dhcp=None) |             enable_dhcp=None) | ||||||
| @@ -503,7 +549,8 @@ class TestQuarkUpdateSubnet(TestQuarkPlugin): | |||||||
|             subnet_find.return_value = subnet_mod |             subnet_find.return_value = subnet_mod | ||||||
|             route_find.return_value = subnet_mod["routes"][0] \ |             route_find.return_value = subnet_mod["routes"][0] \ | ||||||
|                 if subnet_mod["routes"] and find_routes else None |                 if subnet_mod["routes"] and find_routes else None | ||||||
|             new_subnet_mod = copy.deepcopy(subnet_mod) |             new_subnet_mod = models.Subnet(network=models.Network()) | ||||||
|  |             new_subnet_mod.update(subnet_mod) | ||||||
|             if new_routes: |             if new_routes: | ||||||
|                 new_subnet_mod["routes"] = new_routes |                 new_subnet_mod["routes"] = new_routes | ||||||
|             if new_dns_servers: |             if new_dns_servers: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Matt Dietz
					Matt Dietz