diff --git a/nsot/api/views.py b/nsot/api/views.py index 8344bf51..a5bef72c 100644 --- a/nsot/api/views.py +++ b/nsot/api/views.py @@ -426,6 +426,15 @@ class NetworkViewSet(ResourceViewSet): lookup_value_regex = '[a-fA-F0-9:.]+(?:\/\d+)?' natural_key = 'cidr' + def allocate_networks(self, networks, site_pk, state='allocated'): + site = models.Site.objects.get(pk=site_pk) + for n in networks: + obj = models.Network(cidr=n, site=site, state=state) + obj.save() + models.Change.objects.create( + obj=obj, user=self.request.user, event='Create' + ) + def get_serializer_class(self): if self.request.method == 'POST': return serializers.NetworkCreateSerializer @@ -497,11 +506,10 @@ def supernets(self, request, pk=None, site_pk=None, *args, **kwargs): return self.list(request, queryset=networks, *args, **kwargs) - @detail_route(methods=['get']) + @detail_route(methods=['get', 'post']) def next_network(self, request, pk=None, site_pk=None, *args, **kwargs): """Return next available networks from this Network.""" network = self.get_resource_object(pk, site_pk) - params = request.query_params prefix_length = params.get('prefix_length') num = params.get('num') @@ -509,19 +517,28 @@ def next_network(self, request, pk=None, site_pk=None, *args, **kwargs): networks = network.get_next_network( prefix_length, num, strict, as_objects=False ) - + if request.method == 'POST': + if qpbool(params.get('reserve', False)): + state = models.Network.RESERVED + else: + state = models.Network.ALLOCATED + self.allocate_networks(networks, site_pk, state) return self.success(networks) - @detail_route(methods=['get']) + @detail_route(methods=['get', 'post']) def next_address(self, request, pk=None, site_pk=None, *args, **kwargs): """Return next available IPs from this Network.""" network = self.get_resource_object(pk, site_pk) - params = request.query_params num = params.get('num') strict = qpbool(params.get('strict_allocation', False)) addresses = network.get_next_address(num, strict, as_objects=False) - + if request.method == 'POST': + if qpbool(params.get('reserve', False)): + state = models.Network.RESERVED + else: + state = models.Network.ALLOCATED + self.allocate_networks(addresses, site_pk, state) return self.success(addresses) @detail_route(methods=['get']) diff --git a/nsot/models.py b/nsot/models.py index 1e453bc0..ec4ba492 100644 --- a/nsot/models.py +++ b/nsot/models.py @@ -719,7 +719,7 @@ class Network(Resource): choices=IP_VERSION_CHOICES ) is_ip = models.BooleanField( - null=False, default=False, db_index=True, + null=False, default=False, db_index=True, editable=False, help_text='Whether the Network is a host address or not.' ) site = models.ForeignKey( @@ -1410,10 +1410,9 @@ def get_ancestors(self): p = self.parent ancestors = [] while p is not None: - ancestors.append(p) + ancestors.append(p.id) p = p.parent - ancestor_ids = [a.id for a in ancestors] - return Interface.objects.filter(id__in=ancestor_ids) + return Interface.objects.filter(id__in=ancestors) def get_children(self): """Return the immediate children of an Interface.""" @@ -1425,11 +1424,10 @@ def get_descendants(self): descendants = [] while len(s) > 0: top = s.pop() - descendants.append(top) + descendants.append(top.id) for c in top.get_children(): s.append(c) - descendant_ids = [c.id for c in descendants] - return Interface.objects.filter(id__in=descendant_ids) + return Interface.objects.filter(id__in=descendants) def get_root(self): """Return the parent of all ancestors of an Interface.""" diff --git a/tests/api_tests/test_networks.py b/tests/api_tests/test_networks.py index 061eef22..5557fa27 100644 --- a/tests/api_tests/test_networks.py +++ b/tests/api_tests/test_networks.py @@ -668,6 +668,46 @@ def test_get_next_detail_routes(site, client): ) +def test_next_network_allocation(site, client): + net_uri = site.list_uri('network') + + client.create(net_uri, cidr='10.1.2.0/24') + + net_24_resp = client.retrieve(net_uri, cidr='10.1.2.0/24') + net_24 = get_result(net_24_resp)[0] + net_24_obj_uri = site.detail_uri('network', id=net_24['id']) + + uri = reverse('network-next-network', args=(site.id, net_24['id'])) + + client.post(uri, params={u'prefix_length': u'32'}) + assert_success(client.retrieve(uri, prefix_length=32), [u'10.1.2.2/32']) + + client.post(uri, params={u'prefix_length': u'32', u'reserve': u'True'}) + + uri = reverse('network-reserved', args=(site.id,)) + assert get_result(client.retrieve(uri))[0]['network_address'] == u'10.1.2.2' + + +def test_next_address_allocation(site, client): + net_uri = site.list_uri('network') + + client.create(net_uri, cidr='10.1.2.0/24') + + net_24_resp = client.retrieve(net_uri, cidr='10.1.2.0/24') + net_24 = get_result(net_24_resp)[0] + net_24_obj_uri = site.detail_uri('network', id=net_24['id']) + + uri = reverse('network-next-address', args=(site.id, net_24['id'])) + + client.post(uri) + assert_success(client.retrieve(uri, prefix_length=32), [u'10.1.2.2/32']) + + client.post(uri, params={u'reserve': u'True'}) + + uri = reverse('network-reserved', args=(site.id,)) + assert get_result(client.retrieve(uri))[0]['network_address'] == u'10.1.2.2' + + def test_reservation_list_route(site, client): """Test the list route for getting reserved networks/addresses.""" net_uri = site.list_uri('network') diff --git a/tests/model_tests/test_networks.py b/tests/model_tests/test_networks.py index 84155ef1..fd669871 100644 --- a/tests/model_tests/test_networks.py +++ b/tests/model_tests/test_networks.py @@ -376,4 +376,4 @@ def test_strict_allocation_3(site): parent = models.Network.objects.create(site = site, cidr = u'2001:db8:abcd:0012::0/96') child = models.Network.objects.create(site = site, cidr = u'2001:db8:abcd:0012::0/97') expected = [ipaddress.ip_network(u'2001:db8:abcd:12::8000:0/128')] - assert parent.get_next_network(128, strict = True) == expected \ No newline at end of file + assert parent.get_next_network(128, strict = True) == expected