Merge lp:~blake-rouse/maas/fix-1441841 into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 3838
Proposed branch: lp:~blake-rouse/maas/fix-1441841
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 913 lines (+436/-103)
13 files modified
src/maasserver/api/ip_addresses.py (+5/-3)
src/maasserver/api/tests/test_ipaddresses.py (+22/-5)
src/maasserver/api/tests/test_node.py (+5/-3)
src/maasserver/models/macaddress.py (+3/-0)
src/maasserver/models/staticipaddress.py (+35/-16)
src/maasserver/models/tests/test_staticipaddress.py (+109/-45)
src/maasserver/static/js/angular/controllers/add_device.js (+9/-5)
src/maasserver/static/js/angular/controllers/tests/test_add_device.js (+13/-0)
src/maasserver/static/js/angular/services/tests/test_validation.js (+164/-15)
src/maasserver/static/js/angular/services/validation.js (+48/-1)
src/maasserver/tests/test_forms_nodegroupinterface.py (+3/-1)
src/maasserver/tests/test_forms_validate_new_static_ip_range.py (+18/-8)
src/maasserver/tests/test_node_action.py (+2/-1)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-1441841
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Andres Rodriguez (community) Needs Information
Review via email: mp+256988@code.launchpad.net

Commit message

Fix the validation in AddDeviceController, to check that the ip address in the static range is not within the dynamic range. Modify the StaticIPAddress.allocate_new to validate that the requested_address is inside the network, instead of the static range.

This allows users to add a static ip address anywhere in the selected network except for the dynamic range.

To post a comment you must log in.
Revision history for this message
Andres Rodriguez (andreserl) wrote :

Except for the static range? or except for the dynamic range?

review: Needs Information
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Sorry meant dynamic range. Fixed commit message.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

This required a fix in the backend not just the frontend. This now allows a requested_address to be anywhere in the network, not just within the static range.

Revision history for this message
Raphaël Badin (rvb) wrote :

Looks good. Can you please add an API test where the requested IP is *not* in the static range (nor in the dynamic range)?

review: Approve
Revision history for this message
Blake Rouse (blake-rouse) wrote :

I added that test. While doing so I noticed that I was not actually validating that the requested_address was not inside the dynamic range. I added that logic and tests as well.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/ip_addresses.py'
--- src/maasserver/api/ip_addresses.py 2015-04-17 08:32:15 +0000
+++ src/maasserver/api/ip_addresses.py 2015-04-23 19:17:12 +0000
@@ -81,12 +81,14 @@
81 """81 """
82 if mac is None:82 if mac is None:
83 sip = StaticIPAddress.objects.allocate_new(83 sip = StaticIPAddress.objects.allocate_new(
84 range_low=interface.static_ip_range_low,84 network=interface.network,
85 range_high=interface.static_ip_range_high,85 static_range_low=interface.static_ip_range_low,
86 static_range_high=interface.static_ip_range_high,
87 dynamic_range_low=interface.ip_range_low,
88 dynamic_range_high=interface.ip_range_high,
86 alloc_type=IPADDRESS_TYPE.USER_RESERVED,89 alloc_type=IPADDRESS_TYPE.USER_RESERVED,
87 requested_address=requested_address,90 requested_address=requested_address,
88 user=user, hostname=hostname)91 user=user, hostname=hostname)
89 commit_within_atomic_block()
90 maaslog.info("User %s was allocated IP %s", user.username, sip.ip)92 maaslog.info("User %s was allocated IP %s", user.username, sip.ip)
91 else:93 else:
92 # The user has requested a static IP linked to a MAC94 # The user has requested a static IP linked to a MAC
9395
=== modified file 'src/maasserver/api/tests/test_ipaddresses.py'
--- src/maasserver/api/tests/test_ipaddresses.py 2015-04-17 07:26:25 +0000
+++ src/maasserver/api/tests/test_ipaddresses.py 2015-04-23 19:17:12 +0000
@@ -213,6 +213,25 @@
213 self.expectThat(returned_address["ip"], Equals(ip_in_network))213 self.expectThat(returned_address["ip"], Equals(ip_in_network))
214 self.expectThat(staticipaddress.ip, Equals(ip_in_network))214 self.expectThat(staticipaddress.ip, Equals(ip_in_network))
215215
216 def test_POST_reserve_creates_address_outside_of_static_range(self):
217 interface = self.make_interface()
218 interface.ip_range_high = unicode(
219 IPAddress(interface.ip_range_high) - 1)
220 interface.save()
221 net = interface.network
222 ip_in_network = unicode(
223 IPAddress(interface.ip_range_high) + 1)
224 response = self.post_reservation_request(
225 net=net, requested_address=ip_in_network)
226 self.assertEqual(httplib.OK, response.status_code, response.content)
227 returned_address = json.loads(response.content)
228 [staticipaddress] = StaticIPAddress.objects.all()
229 self.expectThat(
230 returned_address["alloc_type"],
231 Equals(IPADDRESS_TYPE.USER_RESERVED))
232 self.expectThat(returned_address["ip"], Equals(ip_in_network))
233 self.expectThat(staticipaddress.ip, Equals(ip_in_network))
234
216 def test_POST_reserve_requested_address_detects_in_use_address(self):235 def test_POST_reserve_requested_address_detects_in_use_address(self):
217 interface = self.make_interface()236 interface = self.make_interface()
218 net = interface.network237 net = interface.network
@@ -227,13 +246,11 @@
227 response.content, Equals(246 response.content, Equals(
228 "The IP address %s is already in use." % ip_in_network))247 "The IP address %s is already in use." % ip_in_network))
229248
230 def test_POST_reserve_requested_address_detects_out_of_range_addr(self):249 def test_POST_reserve_requested_address_rejects_ip_in_dynamic_range(self):
231 interface = self.make_interface()250 interface = self.make_interface()
232 net = interface.network251 net = interface.network
233 ip_not_in_network = unicode(252 ip_in_network = interface.ip_range_low
234 IPAddress(interface.static_ip_range_low) - 1)253 response = self.post_reservation_request(net, ip_in_network)
235 response = self.post_reservation_request(
236 net=net, requested_address=ip_not_in_network)
237 self.assertEqual(254 self.assertEqual(
238 httplib.FORBIDDEN, response.status_code, response.content)255 httplib.FORBIDDEN, response.status_code, response.content)
239256
240257
=== modified file 'src/maasserver/api/tests/test_node.py'
--- src/maasserver/api/tests/test_node.py 2015-04-17 08:36:07 +0000
+++ src/maasserver/api/tests/test_node.py 2015-04-23 19:17:12 +0000
@@ -1208,11 +1208,12 @@
1208 [observed_static_ip] = StaticIPAddress.objects.all()1208 [observed_static_ip] = StaticIPAddress.objects.all()
1209 self.assertEqual(observed_static_ip.ip, requested_address)1209 self.assertEqual(observed_static_ip.ip, requested_address)
12101210
1211 def test_claim_sticky_ip_address_detects_out_of_range_requested_ip(self):1211 def test_claim_sticky_ip_address_detects_out_of_network_requested_ip(self):
1212 self.become_admin()1212 self.become_admin()
1213 node = factory.make_Node_with_MACAddress_and_NodeGroupInterface()1213 node = factory.make_Node_with_MACAddress_and_NodeGroupInterface()
1214 ngi = node.get_primary_mac().cluster_interface1214 ngi = node.get_primary_mac().cluster_interface
1215 requested_address = IPAddress(ngi.static_ip_range_low) - 11215 other_network = factory.make_ipv4_network(but_not=ngi.network)
1216 requested_address = factory.pick_ip_in_network(other_network)
12161217
1217 response = self.client.post(1218 response = self.client.post(
1218 self.get_node_uri(node),1219 self.get_node_uri(node),
@@ -1265,7 +1266,8 @@
1265 ngi.save()1266 ngi.save()
1266 with transaction.atomic():1267 with transaction.atomic():
1267 StaticIPAddress.objects.allocate_new(1268 StaticIPAddress.objects.allocate_new(
1268 ngi.static_ip_range_high, ngi.static_ip_range_low)1269 ngi.network, ngi.static_ip_range_low, ngi.static_ip_range_high,
1270 ngi.ip_range_low, ngi.ip_range_high)
12691271
1270 response = self.client.post(1272 response = self.client.post(
1271 TestNodeAPI.get_node_uri(node), {'op': 'start'})1273 TestNodeAPI.get_node_uri(node), {'op': 'start'})
12721274
=== modified file 'src/maasserver/models/macaddress.py'
--- src/maasserver/models/macaddress.py 2015-04-08 20:14:03 +0000
+++ src/maasserver/models/macaddress.py 2015-04-23 19:17:12 +0000
@@ -238,8 +238,11 @@
238 )238 )
239239
240 new_sip = StaticIPAddress.objects.allocate_new(240 new_sip = StaticIPAddress.objects.allocate_new(
241 cluster_interface.network,
241 cluster_interface.static_ip_range_low,242 cluster_interface.static_ip_range_low,
242 cluster_interface.static_ip_range_high,243 cluster_interface.static_ip_range_high,
244 cluster_interface.ip_range_low,
245 cluster_interface.ip_range_high,
243 alloc_type, requested_address=requested_address,246 alloc_type, requested_address=requested_address,
244 user=user)247 user=user)
245 MACStaticIPAddressLink(mac_address=self, ip_address=new_sip).save()248 MACStaticIPAddressLink(mac_address=self, ip_address=new_sip).save()
246249
=== modified file 'src/maasserver/models/staticipaddress.py'
--- src/maasserver/models/staticipaddress.py 2015-04-16 12:15:52 +0000
+++ src/maasserver/models/staticipaddress.py 2015-04-23 19:17:12 +0000
@@ -131,13 +131,24 @@
131 ipaddress.save()131 ipaddress.save()
132 return ipaddress132 return ipaddress
133133
134 def allocate_new(self, range_low, range_high,134 def allocate_new(
135 alloc_type=IPADDRESS_TYPE.AUTO, user=None,135 self, network, static_range_low, static_range_high,
136 requested_address=None, hostname=None):136 dynamic_range_low, dynamic_range_high,
137 alloc_type=IPADDRESS_TYPE.AUTO, user=None,
138 requested_address=None, hostname=None):
137 """Return a new StaticIPAddress.139 """Return a new StaticIPAddress.
138140
139 :param range_low: The lowest address to allocate in a range141 :param network: The network the address should be allocated in.
140 :param range_high: The highest address to allocate in a range142 :param static_range_low: The lowest static address to allocate in a
143 range. Used if `requested_address` is not passed.
144 :param static_range_high: The highest static address to allocate in a
145 range. Used if `requested_address` is not passed.
146 :param dynamic_range_low: The lowest dynamic address. Used if
147 `requested_address` is passed, check that its not inside the
148 dynamic range.
149 :param dynamic_range_high: The highest dynamic address. Used if
150 `requested_address` is passed, check that its not inside the
151 dynamic range.
141 :param alloc_type: What sort of IP address to allocate in the152 :param alloc_type: What sort of IP address to allocate in the
142 range of choice in IPADDRESS_TYPE.153 range of choice in IPADDRESS_TYPE.
143 :param user: If providing a user, the alloc_type must be154 :param user: If providing a user, the alloc_type must be
@@ -157,15 +168,15 @@
157 # taken, and so we must first eliminate all other possible causes.168 # taken, and so we must first eliminate all other possible causes.
158 self._verify_alloc_type(alloc_type, user)169 self._verify_alloc_type(alloc_type, user)
159170
160 range_low = IPAddress(range_low)
161 range_high = IPAddress(range_high)
162 static_range = IPRange(range_low, range_high)
163
164 if requested_address is None:171 if requested_address is None:
172 static_range_low = IPAddress(static_range_low)
173 static_range_high = IPAddress(static_range_high)
174 static_range = IPRange(static_range_low, static_range_high)
175
165 with locks.staticip_acquire:176 with locks.staticip_acquire:
166 requested_address = self._async_find_free_ip(177 requested_address = self._async_find_free_ip(
167 range_low, range_high, static_range, alloc_type, user,178 static_range_low, static_range_high, static_range,
168 hostname=hostname).wait(30)179 alloc_type, user, hostname=hostname).wait(30)
169 try:180 try:
170 return self._attempt_allocation(181 return self._attempt_allocation(
171 requested_address, alloc_type, user,182 requested_address, alloc_type, user,
@@ -176,12 +187,20 @@
176 # let the retry mechanism do its thing.187 # let the retry mechanism do its thing.
177 raise make_serialization_failure()188 raise make_serialization_failure()
178 else:189 else:
190 dynamic_range_low = IPAddress(dynamic_range_low)
191 dynamic_range_high = IPAddress(dynamic_range_high)
192 dynamic_range = IPRange(dynamic_range_low, dynamic_range_high)
193
179 requested_address = IPAddress(requested_address)194 requested_address = IPAddress(requested_address)
180 if requested_address not in static_range:195 if requested_address not in network:
181 raise StaticIPAddressOutOfRange(196 raise StaticIPAddressOutOfRange(
182 "%s is not inside the range %s to %s" % (197 "%s is not inside the network %s" % (
183 requested_address.format(), range_low.format(),198 requested_address.format(), network))
184 range_high.format()))199 if requested_address in dynamic_range:
200 raise StaticIPAddressOutOfRange(
201 "%s is inside the dynamic range %s to %s" % (
202 requested_address.format(), dynamic_range_low.format(),
203 dynamic_range_high.format()))
185 return self._attempt_allocation(204 return self._attempt_allocation(
186 requested_address, alloc_type, user=user, hostname=hostname)205 requested_address, alloc_type, user=user, hostname=hostname)
187206
188207
=== modified file 'src/maasserver/models/tests/test_staticipaddress.py'
--- src/maasserver/models/tests/test_staticipaddress.py 2015-04-16 12:30:02 +0000
+++ src/maasserver/models/tests/test_staticipaddress.py 2015-04-23 19:17:12 +0000
@@ -48,108 +48,164 @@
4848
49class TestStaticIPAddressManager(MAASServerTestCase):49class TestStaticIPAddressManager(MAASServerTestCase):
5050
51 def make_ip_ranges(self, network=None):
52 interface = factory.make_NodeGroupInterface(
53 factory.make_NodeGroup(), network=network)
54 return (
55 interface.network,
56 interface.static_ip_range_low,
57 interface.static_ip_range_high,
58 interface.ip_range_low,
59 interface.ip_range_low,
60 )
61
51 def test_allocate_new_returns_ip_in_correct_range(self):62 def test_allocate_new_returns_ip_in_correct_range(self):
52 low, high = factory.make_ip_range()63 network, static_low, static_high, dynamic_low, dynamic_high = (
53 ipaddress = StaticIPAddress.objects.allocate_new(low, high)64 self.make_ip_ranges())
65 ipaddress = StaticIPAddress.objects.allocate_new(
66 network, static_low, static_high, dynamic_low, dynamic_high)
54 self.assertIsInstance(ipaddress, StaticIPAddress)67 self.assertIsInstance(ipaddress, StaticIPAddress)
55 iprange = IPRange(low, high)68 iprange = IPRange(static_low, static_high)
56 self.assertIn(IPAddress(ipaddress.ip), iprange)69 self.assertIn(IPAddress(ipaddress.ip), iprange)
5770
58 def test_allocate_new_allocates_IPv6_address(self):71 def test_allocate_new_allocates_IPv6_address(self):
59 low, high = factory.make_ipv6_range()72 network = factory.make_ipv6_network()
60 ipaddress = StaticIPAddress.objects.allocate_new(low, high)73 network, static_low, static_high, dynamic_low, dynamic_high = (
74 self.make_ip_ranges(network))
75 ipaddress = StaticIPAddress.objects.allocate_new(
76 network, static_low, static_high, dynamic_low, dynamic_high)
61 self.assertIsInstance(ipaddress, StaticIPAddress)77 self.assertIsInstance(ipaddress, StaticIPAddress)
62 self.assertIn(IPAddress(ipaddress.ip), IPRange(low, high))78 self.assertIn(
79 IPAddress(ipaddress.ip), IPRange(static_low, static_high))
6380
64 def test_allocate_new_sets_user(self):81 def test_allocate_new_sets_user(self):
65 low, high = factory.make_ip_range()82 network, static_low, static_high, dynamic_low, dynamic_high = (
83 self.make_ip_ranges())
66 user = factory.make_User()84 user = factory.make_User()
67 ipaddress = StaticIPAddress.objects.allocate_new(85 ipaddress = StaticIPAddress.objects.allocate_new(
68 low, high, alloc_type=IPADDRESS_TYPE.USER_RESERVED, user=user)86 network, static_low, static_high, dynamic_low, dynamic_high,
87 alloc_type=IPADDRESS_TYPE.USER_RESERVED, user=user)
69 self.assertEqual(user, ipaddress.user)88 self.assertEqual(user, ipaddress.user)
7089
71 def test_allocate_new_with_user_disallows_wrong_alloc_types(self):90 def test_allocate_new_with_user_disallows_wrong_alloc_types(self):
72 low, high = factory.make_ip_range()91 network, static_low, static_high, dynamic_low, dynamic_high = (
92 self.make_ip_ranges())
73 user = factory.make_User()93 user = factory.make_User()
74 alloc_type = factory.pick_enum(94 alloc_type = factory.pick_enum(
75 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED])95 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED])
76 self.assertRaises(96 self.assertRaises(
77 AssertionError, StaticIPAddress.objects.allocate_new, low, high,97 AssertionError, StaticIPAddress.objects.allocate_new, network,
98 static_low, static_high, dynamic_low, dynamic_high,
78 user=user, alloc_type=alloc_type)99 user=user, alloc_type=alloc_type)
79100
80 def test_allocate_new_with_reserved_type_requires_a_user(self):101 def test_allocate_new_with_reserved_type_requires_a_user(self):
81 low, high = factory.make_ip_range()102 network, static_low, static_high, dynamic_low, dynamic_high = (
103 self.make_ip_ranges())
82 self.assertRaises(104 self.assertRaises(
83 AssertionError, StaticIPAddress.objects.allocate_new, low, high,105 AssertionError, StaticIPAddress.objects.allocate_new, network,
106 static_low, static_high, dynamic_low, dynamic_high,
84 alloc_type=IPADDRESS_TYPE.USER_RESERVED)107 alloc_type=IPADDRESS_TYPE.USER_RESERVED)
85108
86 def test_allocate_new_compares_by_IP_not_alphabetically(self):109 def test_allocate_new_compares_by_IP_not_alphabetically(self):
87 # Django has a bug that casts IP addresses with HOST(), which110 # Django has a bug that casts IP addresses with HOST(), which
88 # results in alphabetical comparisons of strings instead of IP111 # results in alphabetical comparisons of strings instead of IP
89 # addresses. See https://bugs.launchpad.net/maas/+bug/1338452112 # addresses. See https://bugs.launchpad.net/maas/+bug/1338452
90 low = "10.0.0.98"113 network = "10.0.0.0/8"
91 high = "10.0.0.100"114 static_low = "10.0.0.98"
115 static_high = "10.0.0.100"
116 dynamic_low = "10.0.0.101"
117 dynamic_high = "10.0.0.105"
92 factory.make_StaticIPAddress("10.0.0.99")118 factory.make_StaticIPAddress("10.0.0.99")
93 ipaddress = StaticIPAddress.objects.allocate_new(low, high)119 ipaddress = StaticIPAddress.objects.allocate_new(
120 network, static_low, static_high, dynamic_low, dynamic_high)
94 self.assertEqual(ipaddress.ip, "10.0.0.98")121 self.assertEqual(ipaddress.ip, "10.0.0.98")
95122
96 def test_allocate_new_returns_requested_IP_if_available(self):123 def test_allocate_new_returns_requested_IP_if_available(self):
97 low, high = factory.make_ip_range()124 network, static_low, static_high, dynamic_low, dynamic_high = (
98 requested_address = low + 1125 self.make_ip_ranges())
126 requested_address = unicode(IPAddress(static_low) + 1)
99 ipaddress = StaticIPAddress.objects.allocate_new(127 ipaddress = StaticIPAddress.objects.allocate_new(
100 low, high, factory.pick_enum(128 network, static_low, static_high, dynamic_low, dynamic_high,
129 factory.pick_enum(
101 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED]),130 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED]),
102 requested_address=requested_address)131 requested_address=requested_address)
103 self.assertEqual(requested_address.format(), ipaddress.ip)132 self.assertEqual(requested_address.format(), ipaddress.ip)
104133
105 def test_allocate_new_raises_when_requested_IP_unavailable(self):134 def test_allocate_new_raises_when_requested_IP_unavailable(self):
106 low, high = factory.make_ip_range()135 network, static_low, static_high, dynamic_low, dynamic_high = (
136 self.make_ip_ranges())
107 requested_address = StaticIPAddress.objects.allocate_new(137 requested_address = StaticIPAddress.objects.allocate_new(
108 low, high, factory.pick_enum(138 network, static_low, static_high, dynamic_low, dynamic_high,
139 factory.pick_enum(
109 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED])).ip140 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED])).ip
110 self.assertRaises(141 self.assertRaises(
111 StaticIPAddressUnavailable, StaticIPAddress.objects.allocate_new,142 StaticIPAddressUnavailable, StaticIPAddress.objects.allocate_new,
112 low, high, requested_address=requested_address)143 network, static_low, static_high, dynamic_low, dynamic_high,
144 requested_address=requested_address)
113145
114 def test_allocate_new_raises_serialization_error_if_ip_taken(self):146 def test_allocate_new_raises_serialization_error_if_ip_taken(self):
115 low, high = factory.make_ip_range()147 network, static_low, static_high, dynamic_low, dynamic_high = (
148 self.make_ip_ranges())
116 # Simulate a "IP already taken" error.149 # Simulate a "IP already taken" error.
117 mock_attempt_allocation = self.patch(150 mock_attempt_allocation = self.patch(
118 StaticIPAddress.objects, '_attempt_allocation')151 StaticIPAddress.objects, '_attempt_allocation')
119 mock_attempt_allocation.side_effect = StaticIPAddressUnavailable()152 mock_attempt_allocation.side_effect = StaticIPAddressUnavailable()
120153
121 error = self.assertRaises(154 error = self.assertRaises(
122 Exception, StaticIPAddress.objects.allocate_new, low, high)155 Exception, StaticIPAddress.objects.allocate_new,
156 network, static_low, static_high, dynamic_low, dynamic_high)
123 self.assertTrue(is_serialization_failure(error))157 self.assertTrue(is_serialization_failure(error))
124158
125 def test_allocate_new_does_not_use_lock_for_requested_ip(self):159 def test_allocate_new_does_not_use_lock_for_requested_ip(self):
126 # When requesting a specific IP address, there's no need to160 # When requesting a specific IP address, there's no need to
127 # acquire the lock.161 # acquire the lock.
128 lock = self.patch(locks, 'staticip_acquire')162 lock = self.patch(locks, 'staticip_acquire')
129 low, high = factory.make_ip_range()163 network, static_low, static_high, dynamic_low, dynamic_high = (
130 requested_address = low + 1164 self.make_ip_ranges())
165 requested_address = unicode(IPAddress(static_low) + 1)
131 ipaddress = StaticIPAddress.objects.allocate_new(166 ipaddress = StaticIPAddress.objects.allocate_new(
132 low, high, requested_address=requested_address)167 network, static_low, static_high, dynamic_low, dynamic_high,
168 requested_address=requested_address)
133 self.assertIsInstance(ipaddress, StaticIPAddress)169 self.assertIsInstance(ipaddress, StaticIPAddress)
134 self.assertThat(lock.__enter__, MockNotCalled())170 self.assertThat(lock.__enter__, MockNotCalled())
135171
136 def test_allocate_new_raises_when_requested_IP_out_of_range(self):172 def test_allocate_new_raises_when_requested_IP_out_of_network(self):
137 low, high = factory.make_ip_range()173 network, static_low, static_high, dynamic_low, dynamic_high = (
138 requested_address = low - 1174 self.make_ip_ranges())
139 e = self.assertRaises(175 other_network = factory.make_ipv4_network(but_not=network)
140 StaticIPAddressOutOfRange, StaticIPAddress.objects.allocate_new,176 requested_address = factory.pick_ip_in_network(other_network)
141 low, high, factory.pick_enum(177 e = self.assertRaises(
142 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED]),178 StaticIPAddressOutOfRange, StaticIPAddress.objects.allocate_new,
143 requested_address=requested_address)179 network, static_low, static_high, dynamic_low, dynamic_high,
144 self.assertEqual(180 factory.pick_enum(
145 "%s is not inside the range %s to %s" % (181 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED]),
146 requested_address, low, high),182 requested_address=requested_address)
183 self.assertEqual(
184 "%s is not inside the network %s" % (
185 requested_address, network),
186 e.message)
187
188 def test_allocate_new_raises_when_requested_IP_in_dynamic_range(self):
189 network, static_low, static_high, dynamic_low, dynamic_high = (
190 self.make_ip_ranges())
191 requested_address = dynamic_low
192 e = self.assertRaises(
193 StaticIPAddressOutOfRange, StaticIPAddress.objects.allocate_new,
194 network, static_low, static_high, dynamic_low, dynamic_high,
195 factory.pick_enum(
196 IPADDRESS_TYPE, but_not=[IPADDRESS_TYPE.USER_RESERVED]),
197 requested_address=requested_address)
198 self.assertEqual(
199 "%s is inside the dynamic range %s to %s" % (
200 requested_address, dynamic_low, dynamic_high),
147 e.message)201 e.message)
148202
149 def test_allocate_new_raises_when_alloc_type_is_None(self):203 def test_allocate_new_raises_when_alloc_type_is_None(self):
150 error = self.assertRaises(204 error = self.assertRaises(
151 ValueError, StaticIPAddress.objects.allocate_new,205 ValueError, StaticIPAddress.objects.allocate_new,
152 sentinel.range_low, sentinel.range_high, alloc_type=None)206 sentinel.network, sentinel.static_range_low,
207 sentinel.static_range_low, sentinel.dynamic_range_low,
208 sentinel.dynamic_range_high, alloc_type=None)
153 self.assertEqual(209 self.assertEqual(
154 "IP address type None is not a member of IPADDRESS_TYPE.",210 "IP address type None is not a member of IPADDRESS_TYPE.",
155 unicode(error))211 unicode(error))
@@ -157,15 +213,19 @@
157 def test_allocate_new_raises_when_alloc_type_is_invalid(self):213 def test_allocate_new_raises_when_alloc_type_is_invalid(self):
158 error = self.assertRaises(214 error = self.assertRaises(
159 ValueError, StaticIPAddress.objects.allocate_new,215 ValueError, StaticIPAddress.objects.allocate_new,
160 sentinel.range_low, sentinel.range_high, alloc_type=12345)216 sentinel.network, sentinel.static_range_low,
217 sentinel.static_range_low, sentinel.dynamic_range_low,
218 sentinel.dynamic_range_high, alloc_type=12345)
161 self.assertEqual(219 self.assertEqual(
162 "IP address type 12345 is not a member of IPADDRESS_TYPE.",220 "IP address type 12345 is not a member of IPADDRESS_TYPE.",
163 unicode(error))221 unicode(error))
164222
165 def test_allocate_new_uses_staticip_acquire_lock(self):223 def test_allocate_new_uses_staticip_acquire_lock(self):
166 lock = self.patch(locks, 'staticip_acquire')224 lock = self.patch(locks, 'staticip_acquire')
167 low, high = factory.make_ip_range()225 network, static_low, static_high, dynamic_low, dynamic_high = (
168 ipaddress = StaticIPAddress.objects.allocate_new(low, high)226 self.make_ip_ranges())
227 ipaddress = StaticIPAddress.objects.allocate_new(
228 network, static_low, static_high, dynamic_low, dynamic_high)
169 self.assertIsInstance(ipaddress, StaticIPAddress)229 self.assertIsInstance(ipaddress, StaticIPAddress)
170 self.assertThat(lock.__enter__, MockCalledOnceWith())230 self.assertThat(lock.__enter__, MockCalledOnceWith())
171 self.assertThat(231 self.assertThat(
@@ -273,15 +333,19 @@
273 '''333 '''
274334
275 def test_allocate_new_raises_when_addresses_exhausted(self):335 def test_allocate_new_raises_when_addresses_exhausted(self):
276 low = high = "192.168.230.1"336 network = "192.168.230.0/24"
337 static_low = static_high = "192.168.230.1"
338 dynamic_low = dynamic_high = "192.168.230.2"
277 with transaction.atomic():339 with transaction.atomic():
278 StaticIPAddress.objects.allocate_new(low, high)340 StaticIPAddress.objects.allocate_new(
341 network, static_low, static_high, dynamic_low, dynamic_high)
279 with transaction.atomic():342 with transaction.atomic():
280 e = self.assertRaises(343 e = self.assertRaises(
281 StaticIPAddressExhaustion,344 StaticIPAddressExhaustion,
282 StaticIPAddress.objects.allocate_new, low, high)345 StaticIPAddress.objects.allocate_new,
346 network, static_low, static_high, dynamic_low, dynamic_high)
283 self.assertEqual(347 self.assertEqual(
284 "No more IPs available in range %s-%s" % (low, high),348 "No more IPs available in range %s-%s" % (static_low, static_high),
285 unicode(e))349 unicode(e))
286350
287351
288352
=== modified file 'src/maasserver/static/js/angular/controllers/add_device.js'
--- src/maasserver/static/js/angular/controllers/add_device.js 2015-04-03 18:35:20 +0000
+++ src/maasserver/static/js/angular/controllers/add_device.js 2015-04-23 19:17:12 +0000
@@ -139,16 +139,16 @@
139 if(!ValidationService.validateIP($scope.device.ipAddress)) {139 if(!ValidationService.validateIP($scope.device.ipAddress)) {
140 return true;140 return true;
141 }141 }
142 var i, inRange, managedInterfaces = $scope.getManagedInterfaces();142 var i, inNetwork, managedInterfaces = $scope.getManagedInterfaces();
143 if(angular.isObject($scope.device.ipAssignment)){143 if(angular.isObject($scope.device.ipAssignment)){
144 if($scope.device.ipAssignment.name === "external") {144 if($scope.device.ipAssignment.name === "external") {
145 // External IP address cannot be within a managed interface145 // External IP address cannot be within a managed interface
146 // on one of the clusters.146 // on one of the clusters.
147 for(i = 0; i < managedInterfaces.length; i++) {147 for(i = 0; i < managedInterfaces.length; i++) {
148 inRange = ValidationService.validateIPInRange(148 inNetwork = ValidationService.validateIPInNetwork(
149 $scope.device.ipAddress,149 $scope.device.ipAddress,
150 managedInterfaces[i].network);150 managedInterfaces[i].network);
151 if(inRange) {151 if(inNetwork) {
152 return true;152 return true;
153 }153 }
154 }154 }
@@ -158,9 +158,13 @@
158 // of the selected clusterInterface.158 // of the selected clusterInterface.
159 var clusterInterface = getInterfaceById(159 var clusterInterface = getInterfaceById(
160 $scope.device.clusterInterfaceId);160 $scope.device.clusterInterfaceId);
161 inRange = ValidationService.validateIPInRange(161 inNetwork = ValidationService.validateIPInNetwork(
162 $scope.device.ipAddress, clusterInterface.network);162 $scope.device.ipAddress, clusterInterface.network);
163 if(!inRange) {163 var inDynamicRange = ValidationService.validateIPInRange(
164 $scope.device.ipAddress, clusterInterface.network,
165 clusterInterface.dynamic_range.low,
166 clusterInterface.dynamic_range.high);
167 if(!inNetwork || inDynamicRange) {
164 return true;168 return true;
165 }169 }
166 }170 }
167171
=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_add_device.js'
--- src/maasserver/static/js/angular/controllers/tests/test_add_device.js 2015-04-03 18:35:20 +0000
+++ src/maasserver/static/js/angular/controllers/tests/test_add_device.js 2015-04-23 19:17:12 +0000
@@ -403,6 +403,19 @@
403 };403 };
404 expect($scope.ipHasError()).toBe(true);404 expect($scope.ipHasError()).toBe(true);
405 });405 });
406
407 it("returns true if static ip in dynamic range of network", function() {
408 var controller = makeController();
409 var nic = makeManagedClusterInterface();
410 var cluster = makeCluster([nic]);
411 $scope.clusters = [cluster];
412 $scope.device.ipAddress = nic.dynamic_range.low;
413 $scope.device.clusterInterfaceId = nic.id;
414 $scope.device.ipAssignment = {
415 name: "static"
416 };
417 expect($scope.ipHasError()).toBe(true);
418 });
406 });419 });
407420
408 describe("deviceHasError", function() {421 describe("deviceHasError", function() {
409422
=== modified file 'src/maasserver/static/js/angular/services/tests/test_validation.js'
--- src/maasserver/static/js/angular/services/tests/test_validation.js 2015-04-03 18:26:06 +0000
+++ src/maasserver/static/js/angular/services/tests/test_validation.js 2015-04-23 19:17:12 +0000
@@ -267,51 +267,200 @@
267 });267 });
268 });268 });
269269
270 describe("validateIPInRange", function() {270 describe("validateIPInNetwork", function() {
271271
272 var scenarios = [272 var scenarios = [
273 {273 {
274 ip: "192.168.2.1",274 ip: "192.168.2.1",
275 range: "192.168.1.0/24",275 network: "192.168.1.0/24",
276 valid: false276 valid: false
277 },277 },
278 {278 {
279 ip: "192.168.1.1",279 ip: "192.168.1.1",
280 range: "192.168.1.0/24",280 network: "192.168.1.0/24",
281 valid: true281 valid: true
282 },282 },
283 {283 {
284 ip: "192.168.1.1",284 ip: "192.168.1.1",
285 range: "172.16.0.0/16",285 network: "172.16.0.0/16",
286 valid: false286 valid: false
287 },287 },
288 {288 {
289 ip: "172.17.1.1",289 ip: "172.17.1.1",
290 range: "172.16.0.0/16",290 network: "172.16.0.0/16",
291 valid: false291 valid: false
292 },292 },
293 {293 {
294 ip: "172.16.1.1",294 ip: "172.16.1.1",
295 range: "172.16.0.0/16",295 network: "172.16.0.0/16",
296 valid: true296 valid: true
297 },297 },
298 {298 {
299 ip: "11.1.1.1",299 ip: "11.1.1.1",
300 range: "10.0.0.0/8",300 network: "10.0.0.0/8",
301 valid: false301 valid: false
302 },302 },
303 {303 {
304 ip: "10.1.1.1",304 ip: "10.1.1.1",
305 range: "10.0.0.0/8",305 network: "10.0.0.0/8",
306 valid: true306 valid: true
307 }307 },
308 ];308 {
309309 ip: "2001:67C:1562::16",
310 angular.forEach(scenarios, function(scenario) {310 network: "2001:67C:1562::0/32",
311 it("validates: " + scenario.ip + " in range: " + scenario.range,311 valid: true
312 },
313 {
314 ip: "2002:67C:1562::16",
315 network: "2001:67C:1562::0/32",
316 valid: false
317 },
318 {
319 ip: "2001:67C:1561::16",
320 network: "2001:67C:1562::0/64",
321 valid: false
322 }
323 ];
324
325 angular.forEach(scenarios, function(scenario) {
326 it("validates: " + scenario.ip + " in network: " + scenario.network,
327 function() {
328 var result = ValidationService.validateIPInNetwork(
329 scenario.ip, scenario.network);
330 expect(result).toBe(scenario.valid);
331 });
332 });
333 });
334
335 describe("validateIPInRange", function() {
336
337 var scenarios = [
338 {
339 ip: "192.168.1.1",
340 network: "192.168.1.0/24",
341 range: {
342 low: "192.168.1.2",
343 high: "192.168.1.100"
344 },
345 valid: false
346 },
347 {
348 ip: "192.168.1.2",
349 network: "192.168.1.0/24",
350 range: {
351 low: "192.168.1.2",
352 high: "192.168.1.100"
353 },
354 valid: true
355 },
356 {
357 ip: "192.168.1.3",
358 network: "192.168.1.0/24",
359 range: {
360 low: "192.168.1.2",
361 high: "192.168.1.100"
362 },
363 valid: true
364 },
365 {
366 ip: "192.168.1.100",
367 network: "192.168.1.0/24",
368 range: {
369 low: "192.168.1.2",
370 high: "192.168.1.100"
371 },
372 valid: true
373 },
374 {
375 ip: "192.168.1.101",
376 network: "192.168.1.0/24",
377 range: {
378 low: "192.168.1.2",
379 high: "192.168.1.100"
380 },
381 valid: false
382 },
383 {
384 ip: "192.168.1.2",
385 network: "192.168.2.0/24",
386 range: {
387 low: "192.168.2.2",
388 high: "192.168.2.100"
389 },
390 valid: false
391 },
392 {
393 ip: "2001:67C:1562::1",
394 network: "2001:67C:1562::0/32",
395 range: {
396 low: "2001:67C:1562::2",
397 high: "2001:67C:1562::FFFF:FFFF"
398 },
399 valid: false
400 },
401 {
402 ip: "2001:67C:1562::2",
403 network: "2001:67C:1562::0/32",
404 range: {
405 low: "2001:67C:1562::2",
406 high: "2001:67C:1562::FFFF:FFFF"
407 },
408 valid: true
409 },
410 {
411 ip: "2001:67C:1562::3",
412 network: "2001:67C:1562::0/32",
413 range: {
414 low: "2001:67C:1562::2",
415 high: "2001:67C:1562::FFFF:FFFF"
416 },
417 valid: true
418 },
419 {
420 ip: "2001:67C:1562::FFFF:FFFE",
421 network: "2001:67C:1562::0/32",
422 range: {
423 low: "2001:67C:1562::2",
424 high: "2001:67C:1562::FFFF:FFFF"
425 },
426 valid: true
427 },
428 {
429 ip: "2001:67C:1562::FFFF:FFFF",
430 network: "2001:67C:1562::0/32",
431 range: {
432 low: "2001:67C:1562::2",
433 high: "2001:67C:1562::FFFF:FFFF"
434 },
435 valid: true
436 },
437 {
438 ip: "2001:67C:1562::1:0:0",
439 network: "2001:67C:1562::0/32",
440 range: {
441 low: "2001:67C:1562::2",
442 high: "2001:67C:1562::FFFF:FFFF"
443 },
444 valid: false
445 },
446 {
447 ip: "2001:67C:1562::2",
448 network: "2001:67C:1563::0/64",
449 range: {
450 low: "2001:67C:1563::2",
451 high: "2001:67C:1563::FFFF:FFFF"
452 },
453 valid: false
454 }
455 ];
456
457 angular.forEach(scenarios, function(scenario) {
458 it("validates: " + scenario.ip + " in range: " +
459 scenario.range.low + " - " + scenario.range.high,
312 function() {460 function() {
313 var result = ValidationService.validateIPInRange(461 var result = ValidationService.validateIPInRange(
314 scenario.ip, scenario.range);462 scenario.ip, scenario.network,
463 scenario.range.low, scenario.range.high);
315 expect(result).toBe(scenario.valid);464 expect(result).toBe(scenario.valid);
316 });465 });
317 });466 });
318467
=== modified file 'src/maasserver/static/js/angular/services/validation.js'
--- src/maasserver/static/js/angular/services/validation.js 2015-04-03 18:26:06 +0000
+++ src/maasserver/static/js/angular/services/validation.js 2015-04-23 19:17:12 +0000
@@ -155,7 +155,7 @@
155 };155 };
156156
157 // Return true if the ipAddress is in the network.157 // Return true if the ipAddress is in the network.
158 this.validateIPInRange = function(ipAddress, network) {158 this.validateIPInNetwork = function(ipAddress, network) {
159 var networkSplit = network.split('/');159 var networkSplit = network.split('/');
160 var networkAddress = networkSplit[0];160 var networkAddress = networkSplit[0];
161 var cidrBits = parseInt(networkSplit[1], 10);161 var cidrBits = parseInt(networkSplit[1], 10);
@@ -175,4 +175,51 @@
175 }175 }
176 return false;176 return false;
177 };177 };
178
179 // Return true if the ipAddress is in the network and between the
180 // lowAddress and highAddress inclusive.
181 this.validateIPInRange = function(
182 ipAddress, network, lowAddress, highAddress) {
183 // If the ip address is not even in the network then its
184 // not in the range.
185 if(!this.validateIPInNetwork(ipAddress, network)) {
186 return false;
187 }
188
189 var i, ipOctets, lowOctets, highOctets;
190 if(this.validateIPv4(ipAddress) &&
191 this.validateIPv4(lowAddress) &&
192 this.validateIPv4(highAddress)) {
193
194 // Check that each octet is of the ip address is more or equal
195 // to the low address and less or equal to the high address.
196 ipOctets = ipv4ToOctets(ipAddress);
197 lowOctets = ipv4ToOctets(lowAddress);
198 highOctets = ipv4ToOctets(highAddress);
199 for(i = 0; i < 4; i++) {
200 if(ipOctets[i] > highOctets[i] ||
201 ipOctets[i] < lowOctets[i]) {
202 return false;
203 }
204 }
205 return true;
206 } else if(this.validateIPv6(ipAddress) &&
207 this.validateIPv6(lowAddress) &&
208 this.validateIPv6(highAddress)) {
209
210 // Check that each octet is of the ip address is more or equal
211 // to the low address and less or equal to the high address.
212 ipOctets = ipv6ToOctets(ipAddress);
213 lowOctets = ipv6ToOctets(lowAddress);
214 highOctets = ipv6ToOctets(highAddress);
215 for(i = 0; i < 8; i++) {
216 if(ipOctets[i] > highOctets[i] ||
217 ipOctets[i] < lowOctets[i]) {
218 return false;
219 }
220 }
221 return true;
222 }
223 return false;
224 };
178 });225 });
179226
=== modified file 'src/maasserver/tests/test_forms_nodegroupinterface.py'
--- src/maasserver/tests/test_forms_nodegroupinterface.py 2015-03-25 15:33:23 +0000
+++ src/maasserver/tests/test_forms_nodegroupinterface.py 2015-04-23 19:17:12 +0000
@@ -212,7 +212,9 @@
212 network=network)212 network=network)
213 [interface] = nodegroup.get_managed_interfaces()213 [interface] = nodegroup.get_managed_interfaces()
214 StaticIPAddress.objects.allocate_new(214 StaticIPAddress.objects.allocate_new(
215 interface.static_ip_range_low, interface.static_ip_range_high)215 interface.network, interface.static_ip_range_low,
216 interface.static_ip_range_high, interface.ip_range_low,
217 interface.ip_range_high)
216 form = NodeGroupInterfaceForm(218 form = NodeGroupInterfaceForm(
217 data={'static_ip_range_low': '', 'static_ip_range_high': ''},219 data={'static_ip_range_low': '', 'static_ip_range_high': ''},
218 instance=interface)220 instance=interface)
219221
=== modified file 'src/maasserver/tests/test_forms_validate_new_static_ip_range.py'
--- src/maasserver/tests/test_forms_validate_new_static_ip_range.py 2015-03-25 15:33:23 +0000
+++ src/maasserver/tests/test_forms_validate_new_static_ip_range.py 2015-04-23 19:17:12 +0000
@@ -49,7 +49,8 @@
4949
50 def test_raises_error_when_allocated_ips_fall_outside_new_range(self):50 def test_raises_error_when_allocated_ips_fall_outside_new_range(self):
51 interface = self.make_interface()51 interface = self.make_interface()
52 StaticIPAddress.objects.allocate_new('10.1.0.56', '10.1.0.60')52 StaticIPAddress.objects.allocate_new(
53 '10.1.0.0/16', '10.1.0.56', '10.1.0.60', '10.1.0.1', '10.1.0.10')
53 error = self.assertRaises(54 error = self.assertRaises(
54 ValidationError,55 ValidationError,
55 validate_new_static_ip_ranges,56 validate_new_static_ip_ranges,
@@ -62,7 +63,8 @@
6263
63 def test_removing_static_range_raises_error_if_ips_allocated(self):64 def test_removing_static_range_raises_error_if_ips_allocated(self):
64 interface = self.make_interface()65 interface = self.make_interface()
65 StaticIPAddress.objects.allocate_new('10.1.0.56', '10.1.0.60')66 StaticIPAddress.objects.allocate_new(
67 '10.1.0.0/16', '10.1.0.56', '10.1.0.60', '10.1.0.1', '10.1.0.10')
66 error = self.assertRaises(68 error = self.assertRaises(
67 ValidationError,69 ValidationError,
68 validate_new_static_ip_ranges,70 validate_new_static_ip_ranges,
@@ -75,7 +77,8 @@
7577
76 def test_allows_range_expansion(self):78 def test_allows_range_expansion(self):
77 interface = self.make_interface()79 interface = self.make_interface()
78 StaticIPAddress.objects.allocate_new('10.1.0.56', '10.1.0.60')80 StaticIPAddress.objects.allocate_new(
81 '10.1.0.0/16', '10.1.0.56', '10.1.0.60', '10.1.0.1', '10.1.0.10')
79 is_valid = validate_new_static_ip_ranges(82 is_valid = validate_new_static_ip_ranges(
80 interface, static_ip_range_low='10.1.0.40',83 interface, static_ip_range_low='10.1.0.40',
81 static_ip_range_high='10.1.0.100')84 static_ip_range_high='10.1.0.100')
@@ -83,7 +86,8 @@
8386
84 def test_allows_allocated_ip_as_upper_bound(self):87 def test_allows_allocated_ip_as_upper_bound(self):
85 interface = self.make_interface()88 interface = self.make_interface()
86 StaticIPAddress.objects.allocate_new('10.1.0.55', '10.1.0.55')89 StaticIPAddress.objects.allocate_new(
90 '10.1.0.0/16', '10.1.0.55', '10.1.0.55', '10.1.0.1', '10.1.0.10')
87 is_valid = validate_new_static_ip_ranges(91 is_valid = validate_new_static_ip_ranges(
88 interface,92 interface,
89 static_ip_range_low=interface.static_ip_range_low,93 static_ip_range_low=interface.static_ip_range_low,
@@ -92,7 +96,8 @@
9296
93 def test_allows_allocated_ip_as_lower_bound(self):97 def test_allows_allocated_ip_as_lower_bound(self):
94 interface = self.make_interface()98 interface = self.make_interface()
95 StaticIPAddress.objects.allocate_new('10.1.0.55', '10.1.0.55')99 StaticIPAddress.objects.allocate_new(
100 '10.1.0.0/16', '10.1.0.55', '10.1.0.55', '10.1.0.1', '10.1.0.10')
96 is_valid = validate_new_static_ip_ranges(101 is_valid = validate_new_static_ip_ranges(
97 interface, static_ip_range_low='10.1.0.55',102 interface, static_ip_range_low='10.1.0.55',
98 static_ip_range_high=interface.static_ip_range_high)103 static_ip_range_high=interface.static_ip_range_high)
@@ -103,7 +108,9 @@
103 interface.management = NODEGROUPINTERFACE_MANAGEMENT.UNMANAGED108 interface.management = NODEGROUPINTERFACE_MANAGEMENT.UNMANAGED
104 interface.save()109 interface.save()
105 StaticIPAddress.objects.allocate_new(110 StaticIPAddress.objects.allocate_new(
106 interface.static_ip_range_low, interface.static_ip_range_high)111 interface.network, interface.static_ip_range_low,
112 interface.static_ip_range_high, interface.ip_range_low,
113 interface.ip_range_high)
107 is_valid = validate_new_static_ip_ranges(114 is_valid = validate_new_static_ip_ranges(
108 interface, static_ip_range_low='10.1.0.57',115 interface, static_ip_range_low='10.1.0.57',
109 static_ip_range_high='10.1.0.58')116 static_ip_range_high='10.1.0.58')
@@ -114,7 +121,8 @@
114 interface.static_ip_range_low = None121 interface.static_ip_range_low = None
115 interface.static_ip_range_high = None122 interface.static_ip_range_high = None
116 interface.save()123 interface.save()
117 StaticIPAddress.objects.allocate_new('10.1.0.56', '10.1.0.60')124 StaticIPAddress.objects.allocate_new(
125 '10.1.0.0/16', '10.1.0.56', '10.1.0.60', '10.1.0.1', '10.1.0.10')
118 is_valid = validate_new_static_ip_ranges(126 is_valid = validate_new_static_ip_ranges(
119 interface, static_ip_range_low='10.1.0.57',127 interface, static_ip_range_low='10.1.0.57',
120 static_ip_range_high='10.1.0.58')128 static_ip_range_high='10.1.0.58')
@@ -123,7 +131,9 @@
123 def test_ignores_unchanged_static_range(self):131 def test_ignores_unchanged_static_range(self):
124 interface = self.make_interface()132 interface = self.make_interface()
125 StaticIPAddress.objects.allocate_new(133 StaticIPAddress.objects.allocate_new(
126 interface.static_ip_range_low, interface.static_ip_range_high)134 interface.network, interface.static_ip_range_low,
135 interface.static_ip_range_high, interface.ip_range_low,
136 interface.ip_range_high)
127 is_valid = validate_new_static_ip_ranges(137 is_valid = validate_new_static_ip_ranges(
128 interface,138 interface,
129 static_ip_range_low=interface.static_ip_range_low,139 static_ip_range_low=interface.static_ip_range_low,
130140
=== modified file 'src/maasserver/tests/test_node_action.py'
--- src/maasserver/tests/test_node_action.py 2015-04-16 13:49:51 +0000
+++ src/maasserver/tests/test_node_action.py 2015-04-23 19:17:12 +0000
@@ -485,7 +485,8 @@
485 ngi.save()485 ngi.save()
486 with transaction.atomic():486 with transaction.atomic():
487 StaticIPAddress.objects.allocate_new(487 StaticIPAddress.objects.allocate_new(
488 ngi.static_ip_range_high, ngi.static_ip_range_low)488 ngi.network, ngi.static_ip_range_low, ngi.static_ip_range_high,
489 ngi.ip_range_low, ngi.ip_range_high)
489490
490 e = self.assertRaises(NodeActionError, Deploy(node, user).execute)491 e = self.assertRaises(NodeActionError, Deploy(node, user).execute)
491 self.expectThat(492 self.expectThat(