~rvb/maas/transaction-1.7-bug-1409852

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# Copyright 2014 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Tests for IP addresses API."""

from __future__ import (
    absolute_import,
    print_function,
    unicode_literals,
    )

str = None

__metaclass__ = type
__all__ = []

import httplib
import json

from django.core.urlresolvers import reverse
from maasserver.enum import (
    IPADDRESS_TYPE,
    NODEGROUP_STATUS,
    )
from maasserver.models import StaticIPAddress
from maasserver.testing.api import APITestCase
from maasserver.testing.factory import factory
from maasserver.testing.orm import reload_object


class TestNetworksAPI(APITestCase):

    def make_interface(self, status=NODEGROUP_STATUS.ACCEPTED, **kwargs):
        cluster = factory.make_NodeGroup(status=status, **kwargs)
        return factory.make_NodeGroupInterface(cluster)

    def post_reservation_request(self, net):
        params = {
            'op': 'reserve',
            'network': unicode(net),
        }
        return self.client.post(reverse('ipaddresses_handler'), params)

    def post_release_request(self, ip):
        params = {
            'op': 'release',
            'ip': ip,
        }
        return self.client.post(reverse('ipaddresses_handler'), params)

    def assertNoMatchingNetworkError(self, response, net):
        self.assertEqual(
            httplib.BAD_REQUEST, response.status_code, response.content)
        self.assertEqual(
            "No network found matching %s" % unicode(net),
            response.content)

    def test_handler_path(self):
        self.assertEqual(
            '/api/1.0/ipaddresses/', reverse('ipaddresses_handler'))

    def test_POST_reserve_creates_ipaddress(self):
        interface = self.make_interface()
        net = interface.network
        response = self.post_reservation_request(net)
        self.assertEqual(httplib.OK, response.status_code)
        returned_address = json.loads(response.content)
        [staticipaddress] = StaticIPAddress.objects.all()
        # We don't need to test the value of the 'created' datetime
        # field. By removing it, we also test for its presence.
        del returned_address['created']
        expected = dict(
            alloc_type=staticipaddress.alloc_type,
            ip=staticipaddress.ip,
            resource_uri=reverse('ipaddresses_handler'),
            )
        self.assertEqual(expected, returned_address)
        self.assertEqual(
            IPADDRESS_TYPE.USER_RESERVED, staticipaddress.alloc_type)
        self.assertEqual(self.logged_in_user, staticipaddress.user)

    def test_POST_reserve_errors_for_no_matching_interface(self):
        interface = self.make_interface()
        net = factory.getRandomNetwork(but_not=[interface.network])
        response = self.post_reservation_request(net)
        self.assertNoMatchingNetworkError(response, net)

    def test_POST_reserve_errors_for_interface_with_no_IP_range(self):
        interface = self.make_interface()
        net = interface.network
        interface.static_ip_range_low = None
        interface.static_ip_range_high = None
        interface.save()
        response = self.post_reservation_request(net)
        self.assertNoMatchingNetworkError(response, net)

    def test_POST_reserve_errors_for_invalid_network(self):
        net = factory.make_string()
        response = self.post_reservation_request(net)
        self.assertEqual(
            httplib.BAD_REQUEST, response.status_code, response.content)
        self.assertEqual(
            "Invalid network parameter %s" % net,
            response.content)

    def test_GET_returns_ipaddresses(self):
        original_ipaddress = factory.make_StaticIPAddress(
            user=self.logged_in_user)
        response = self.client.get(reverse('ipaddresses_handler'))
        self.assertEqual(httplib.OK, response.status_code, response.content)

        parsed_result = json.loads(response.content)
        self.assertEqual(1, len(parsed_result), response.content)
        [returned_address] = parsed_result
        fields = {'alloc_type', 'ip'}
        self.assertEqual(
            fields.union({'resource_uri', 'created'}),
            set(returned_address.keys()))
        expected_values = {
            field: getattr(original_ipaddress, field)
            for field in fields
            if field not in ('resource_uri', 'created')
        }
        # We don't need to test the value of the 'created' datetime
        # field.
        del returned_address['created']
        expected_values['resource_uri'] = reverse('ipaddresses_handler')
        self.assertEqual(expected_values, returned_address)

    def test_GET_returns_empty_if_no_ipaddresses(self):
        response = self.client.get(reverse('ipaddresses_handler'))
        self.assertEqual(httplib.OK, response.status_code, response.content)
        self.assertEqual([], json.loads(response.content))

    def test_GET_only_returns_request_users_addresses(self):
        ipaddress = factory.make_StaticIPAddress(user=self.logged_in_user)
        factory.make_StaticIPAddress(user=factory.make_User())
        response = self.client.get(reverse('ipaddresses_handler'))
        self.assertEqual(httplib.OK, response.status_code, response.content)
        parsed_result = json.loads(response.content)
        [returned_address] = parsed_result
        self.assertEqual(ipaddress.ip, returned_address['ip'])

    def test_GET_sorts_by_id(self):
        addrs = []
        for _ in range(3):
            addrs.append(
                factory.make_StaticIPAddress(user=self.logged_in_user))
        response = self.client.get(reverse('ipaddresses_handler'))
        self.assertEqual(httplib.OK, response.status_code, response.content)
        parsed_result = json.loads(response.content)
        expected = [
            addr.ip for addr in
            sorted(addrs, key=lambda addr: getattr(addr, "id"))]
        observed = [result['ip'] for result in parsed_result]
        self.assertEqual(expected, observed)

    def test_POST_release_deallocates_address(self):
        ipaddress = factory.make_StaticIPAddress(user=self.logged_in_user)
        response = self.post_release_request(ipaddress.ip)
        self.assertEqual(httplib.OK, response.status_code, response.content)
        self.assertIsNone(reload_object(ipaddress))

    def test_POST_release_does_not_delete_IP_that_I_dont_own(self):
        ipaddress = factory.make_StaticIPAddress(user=factory.make_User())
        response = self.post_release_request(ipaddress.ip)
        self.assertEqual(
            httplib.NOT_FOUND, response.status_code, response.content)

    def test_POST_release_does_not_delete_other_IPs_I_own(self):
        ipaddress = factory.make_StaticIPAddress(user=self.logged_in_user)
        other_address = factory.make_StaticIPAddress(user=self.logged_in_user)
        response = self.post_release_request(ipaddress.ip)
        self.assertEqual(httplib.OK, response.status_code, response.content)
        self.assertIsNotNone(reload_object(other_address))