Merge ~newell-jensen/maas:no-arch-and-mac-address-validation-with-ipmi-power-parameters into maas:master

Proposed by Newell Jensen
Status: Rejected
Rejected by: Newell Jensen
Proposed branch: ~newell-jensen/maas:no-arch-and-mac-address-validation-with-ipmi-power-parameters
Merge into: maas:master
Diff against target: 490 lines (+100/-135)
11 files modified
src/maasserver/api/machines.py (+6/-0)
src/maasserver/api/tests/test_machines.py (+27/-0)
src/maasserver/forms/__init__.py (+28/-13)
src/maasserver/forms/tests/test_machine.py (+1/-1)
src/maasserver/forms/tests/test_machinewithmacaddresses.py (+19/-1)
src/maasserver/models/node.py (+1/-13)
src/maasserver/models/tests/test_node.py (+0/-23)
src/maasserver/static/js/angular/controllers/add_hardware.js (+11/-28)
src/maasserver/static/js/angular/controllers/tests/test_add_hardware.js (+6/-24)
src/maasserver/static/partials/nodes-list.html (+1/-1)
src/maasserver/websockets/handlers/tests/test_machine.py (+0/-31)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Lee Trager (community) Needs Fixing
Review via email: mp+347649@code.launchpad.net

Commit message

LP: #1707216 - No validation for architecture or mac addresses is required for IPMI power types.

To post a comment you must log in.
7b0fdba... by Newell Jensen

Clean some tests.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b no-arch-and-mac-address-validation-with-ipmi-power-parameters lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 7b0fdba3debce6fb4818b6cc6b3300c454d5de48

review: Approve
Revision history for this message
Lee Trager (ltrager) wrote :

I'm not sure clearing the errors after they occur is the best path. If I add form.save() to your unit test an exception is raised about the architecture missing. I think MAAS should be smart enough to know when to avoid the errors.

On Django forms whether or not a field is required is controlled by the optional field argument required. On the mac_addresses field this is not set so Django is assuming it always is, you can turn it off by modifying WithMACAddressesMixin.set_up_mac_addresses_field with

self.fields['mac_addresses'] = MultipleMACAddressField(len(macs), required=(self.data.get('power_type') != 'ipmi'))

If you look at MachineForm.set_up_architecture_field you'll see required is already False. The error is coming from Node.clean_architecture. That is why clearing the error won't help, the model save will still raise the error. The form should be validating the architecture already so I think its safe to remove Node.clean_architecture and Device.clean_architecture and rely on the form by making required=True unless the power type is ipmi.

review: Needs Fixing
a12d6d7... by Newell Jensen

Move validation for architecture to forms. Review fixes and updated unit tests.

18315b3... by Newell Jensen

Merge with master.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b no-arch-and-mac-address-validation-with-ipmi-power-parameters lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: a12d6d70413c1cd5fe1695c6000f522af1799ca1

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b no-arch-and-mac-address-validation-with-ipmi-power-parameters lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 18315b3cef069a5695ab5ee5f21ddbf1a5473b14

review: Approve
97fb812... by Newell Jensen

Set mac_addresses to empty list when no architecture is given for IPMI power types. This will force the machine to enlist if no architecture is supplied.

f2e44fa... by Newell Jensen

Fix failing unit tests by adding an explicit check for ipmi power type when setting mac addresses to an empty list when no architecture is set.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b no-arch-and-mac-address-validation-with-ipmi-power-parameters lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/3164/console
COMMIT: 97fb812e49d1a49dbfd6211157540dfe5fe7ad5f

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b no-arch-and-mac-address-validation-with-ipmi-power-parameters lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: f2e44fad675b05b437679f0993817f3dcf15c991

review: Approve
Revision history for this message
Lee Trager (ltrager) wrote :

If MAC addresses are added and an architecture isn't specified in the UI MAC addresses shouldn't be added for the machine. You stopped adding them only over the API, this should be done in the form. A few other comments below but I think this is close.

review: Needs Fixing
94cf8d4... by Newell Jensen

Review fixes.

Revision history for this message
Newell Jensen (newell-jensen) wrote :

I updated the branch with the review fixes. Thanks for the review Lee.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b no-arch-and-mac-address-validation-with-ipmi-power-parameters lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 94cf8d46212d7476f9720cc44c3334c803630c20

review: Approve
Revision history for this message
Newell Jensen (newell-jensen) wrote :

Rejecting this branch as my changes were used in https://code.launchpad.net/~ltrager/maas/+git/maas/+merge/348256

Unmerged commits

94cf8d4... by Newell Jensen

Review fixes.

f2e44fa... by Newell Jensen

Fix failing unit tests by adding an explicit check for ipmi power type when setting mac addresses to an empty list when no architecture is set.

97fb812... by Newell Jensen

Set mac_addresses to empty list when no architecture is given for IPMI power types. This will force the machine to enlist if no architecture is supplied.

18315b3... by Newell Jensen

Merge with master.

a12d6d7... by Newell Jensen

Move validation for architecture to forms. Review fixes and updated unit tests.

7b0fdba... by Newell Jensen

Clean some tests.

ea5199b... by Newell Jensen

Fix lint.

cf4c545... by Newell Jensen

Drive by clean up.

fb59a9e... by Newell Jensen

LP: #1707216 - No validation for architecture or mac addresses is required for IPMI power types.

dc3931c... by Newell Jensen

Add unit tests.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/machines.py b/src/maasserver/api/machines.py
2index a02cbe2..61e3ec0 100644
3--- a/src/maasserver/api/machines.py
4+++ b/src/maasserver/api/machines.py
5@@ -1200,6 +1200,7 @@ class AnonMachinesHandler(AnonNodesHandler):
6 # BMC enlistment is currently only done for IPMI.
7 # First, check if there is a pre-existing machine within MAAS that
8 # matches the request.
9+ architecture = request.data.get('architecture', None)
10 power_type = request.data.get('power_type', None)
11 power_parameters = request.data.get('power_parameters', None)
12 machine = None
13@@ -1209,6 +1210,11 @@ class AnonMachinesHandler(AnonNodesHandler):
14 status__in=[NODE_STATUS.NEW, NODE_STATUS.COMMISSIONING],
15 bmc__power_parameters__power_address=(
16 params['power_address'])).first()
17+ # Update architecture if it is not already set.
18+ if machine is not None and architecture is not None:
19+ if machine.architecture != architecture:
20+ machine.architecture = architecture
21+ machine.save()
22 if machine is None:
23 machine = create_machine(request)
24 if machine.status == NODE_STATUS.NEW:
25diff --git a/src/maasserver/api/tests/test_machines.py b/src/maasserver/api/tests/test_machines.py
26index c96eaa0..fc44545 100644
27--- a/src/maasserver/api/tests/test_machines.py
28+++ b/src/maasserver/api/tests/test_machines.py
29@@ -173,6 +173,33 @@ class TestMachinesAPI(APITestCase.ForUser):
30 {nic.mac_address for nic in machine.interface_set.all()},
31 Equals(macs))
32
33+ def test_POST_creates_ipmi_machine_sets_mac_addresses_empty_no_arch(self):
34+ make_usable_architecture(self)
35+ hostname = factory.make_name('host')
36+ macs = {
37+ factory.make_mac_address()
38+ for _ in range(random.randint(1, 2))
39+ }
40+ power_address = factory.make_ip_address()
41+ response = self.client.post(
42+ reverse('machines_handler'),
43+ {
44+ 'hostname': hostname,
45+ 'mac_addresses': macs,
46+ 'power_type': 'ipmi',
47+ 'power_parameters_power_address': power_address,
48+ })
49+ self.assertEqual(http.client.OK, response.status_code)
50+ system_id = json.loads(
51+ response.content.decode(settings.DEFAULT_CHARSET))['system_id']
52+ machine = Machine.objects.get(system_id=system_id)
53+ self.expectThat(machine.hostname, Equals(hostname))
54+ self.expectThat(
55+ {nic.mac_address for nic in machine.interface_set.all()},
56+ Equals(set()))
57+ self.assertEqual(
58+ power_address, machine.power_parameters['power_address'])
59+
60 def test_POST_when_logged_in_creates_machine_in_declared_state(self):
61 # When a user enlists a machine, it goes into the New state.
62 # This will change once we start doing proper commissioning.
63diff --git a/src/maasserver/forms/__init__.py b/src/maasserver/forms/__init__.py
64index 970e6a3..44c392d 100644
65--- a/src/maasserver/forms/__init__.py
66+++ b/src/maasserver/forms/__init__.py
67@@ -673,6 +673,9 @@ class MachineForm(NodeForm):
68 # Even though it doesn't need it and doesn't use it, this form accepts
69 # a parameter named 'request' because it is used interchangingly
70 # with AdminMachineForm which actually uses this parameter.
71+ request = kwargs.get('request')
72+ if request is not None:
73+ self.data = request.data
74 instance = kwargs.get('instance')
75
76 self.set_up_architecture_field()
77@@ -702,9 +705,11 @@ class MachineForm(NodeForm):
78 choices = list_architecture_choices(architectures)
79 invalid_arch_message = compose_invalid_choice_text(
80 'architecture', choices)
81+ power_type = self.data.get('power_type', None)
82 self.fields['architecture'] = forms.ChoiceField(
83- choices=choices, required=False, initial=default_arch,
84- error_messages={'invalid_choice': invalid_arch_message})
85+ choices=choices, required=(power_type != 'ipmi'),
86+ initial=default_arch, error_messages={
87+ 'invalid_choice': invalid_arch_message})
88
89 def set_up_osystem_and_distro_series_fields(self, instance):
90 """Create the `osystem` and `distro_series` fields.
91@@ -1205,7 +1210,8 @@ class WithMACAddressesMixin:
92
93 def set_up_mac_addresses_field(self):
94 macs = [mac for mac in self.data.getlist('mac_addresses') if mac]
95- self.fields['mac_addresses'] = MultipleMACAddressField(len(macs))
96+ self.fields['mac_addresses'] = MultipleMACAddressField(
97+ len(macs), required=(self.data.get('power_type') != 'ipmi'))
98 self.data = self.data.copy()
99 self.data['mac_addresses'] = macs
100
101@@ -1255,16 +1261,25 @@ class WithMACAddressesMixin:
102 This implementation of `save` does not support the `commit` argument.
103 """
104 node = super(WithMACAddressesMixin, self).save()
105- for mac in self.cleaned_data['mac_addresses']:
106- mac_addresses_errors = []
107- try:
108- node.add_physical_interface(mac)
109- except ValidationError as e:
110- mac_addresses_errors.append(e.message)
111- if mac_addresses_errors:
112- raise ValidationError({
113- "mac_addresses": mac_addresses_errors
114- })
115+ architecture = self.cleaned_data.get('architecture')
116+ power_type = self.cleaned_data.get('power_type')
117+ # BMC enlistment - if no achitecture is set for IPMI power type,
118+ # force mac_addressesto be empty.
119+ if not architecture and power_type == 'ipmi':
120+ mac_addresses = []
121+ else:
122+ mac_addresses = self.cleaned_data.get('mac_addresses')
123+ if mac_addresses:
124+ for mac in mac_addresses:
125+ mac_addresses_errors = []
126+ try:
127+ node.add_physical_interface(mac)
128+ except ValidationError as e:
129+ mac_addresses_errors.append(e.message)
130+ if mac_addresses_errors:
131+ raise ValidationError({
132+ "mac_addresses": mac_addresses_errors
133+ })
134 # Generate a hostname for this node if the provided hostname is
135 # IP-based (because this means that this name comes from a DNS
136 # reverse query to the MAAS DNS). If the provided hostname was empty,
137diff --git a/src/maasserver/forms/tests/test_machine.py b/src/maasserver/forms/tests/test_machine.py
138index f284136..4bca075 100644
139--- a/src/maasserver/forms/tests/test_machine.py
140+++ b/src/maasserver/forms/tests/test_machine.py
141@@ -369,8 +369,8 @@ class TestAdminMachineForm(MAASServerTestCase):
142 'cpu_count',
143 'memory',
144 'zone',
145- 'power_type',
146 'power_parameters',
147+ 'power_type',
148 'pool',
149 ],
150 list(form.fields))
151diff --git a/src/maasserver/forms/tests/test_machinewithmacaddresses.py b/src/maasserver/forms/tests/test_machinewithmacaddresses.py
152index 5e6b703..fefaaf9 100644
153--- a/src/maasserver/forms/tests/test_machinewithmacaddresses.py
154+++ b/src/maasserver/forms/tests/test_machinewithmacaddresses.py
155@@ -1,4 +1,4 @@
156-# Copyright 2014-2016 Canonical Ltd. This software is licensed under the
157+# Copyright 2014-2018 Canonical Ltd. This software is licensed under the
158 # GNU Affero General Public License version 3 (see the file LICENSE).
159
160 """Tests for `MachineWithMACAddressesForm`."""
161@@ -138,6 +138,24 @@ class MachineWithMACAddressesFormTest(MAASServerTestCase):
162
163 self.assertTrue(form.is_valid())
164
165+ def test__mac_address_is_required(self):
166+ form = MachineWithMACAddressesForm(
167+ data=self.make_params(mac_addresses=[]))
168+
169+ self.assertFalse(form.is_valid())
170+ self.assertEqual(['mac_addresses'], list(form.errors))
171+ self.assertEqual(
172+ ["This field is required."],
173+ form.errors['mac_addresses'])
174+
175+ def test__no_architecture_or_mac_addresses_is_ok_for_ipmi(self):
176+ # No architecture or MAC addresses is okay for IPMI power types.
177+ params = self.make_params(mac_addresses=[])
178+ params['architecture'] = None
179+ params['power_type'] = 'ipmi'
180+ form = MachineWithMACAddressesForm(data=params)
181+ self.assertTrue(form.is_valid())
182+
183 def test__save(self):
184 macs = ['aa:bb:cc:dd:ee:ff', '9a:bb:c3:33:e5:7f']
185 form = MachineWithMACAddressesForm(
186diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
187index 2599a6e..6b4b39f 100644
188--- a/src/maasserver/models/node.py
189+++ b/src/maasserver/models/node.py
190@@ -1563,12 +1563,6 @@ class Node(CleanSave, TimestampedModel):
191 )
192 raise NodeStateViolation(error_text)
193
194- def clean_architecture(self):
195- if self.architecture == '':
196- raise ValidationError(
197- {'architecture':
198- ["Architecture must be defined for installable nodes."]})
199-
200 def clean_hostname_domain(self):
201 # If you set the hostname to a name with dots, that you mean for that
202 # to be the FQDN of the host. Se we check that a domain exists for
203@@ -1607,8 +1601,6 @@ class Node(CleanSave, TimestampedModel):
204 self.clean_pool()
205 if self._state.has_changed('status'):
206 self.clean_status(self._state.get_old_value('status'))
207- if self._state.has_changed('architecture'):
208- self.clean_architecture()
209 if self._state.has_changed('boot_disk_id'):
210 self.clean_boot_disk()
211 if self._state.has_changed('boot_interface_id'):
212@@ -2984,7 +2976,7 @@ class Node(CleanSave, TimestampedModel):
213
214 def split_arch(self):
215 """Return architecture and subarchitecture, as a tuple."""
216- if self.architecture is None:
217+ if self.architecture is None or self.architecture == '':
218 return ("", "")
219 arch, subarch = self.architecture.split('/')
220 return (arch, subarch)
221@@ -5464,10 +5456,6 @@ class Device(Node):
222 super(Device, self).__init__(
223 node_type=NODE_TYPE.DEVICE, *args, **kwargs)
224
225- def clean_architecture(self):
226- # Devices aren't required to have a defined architecture
227- pass
228-
229
230 class NodeGroupToRackController(CleanSave, Model):
231 """Store some of the old NodeGroup data so we can migrate it when a rack
232diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
233index d15ba32..0b5694c 100644
234--- a/src/maasserver/models/tests/test_node.py
235+++ b/src/maasserver/models/tests/test_node.py
236@@ -1004,17 +1004,6 @@ class TestNode(MAASServerTestCase):
237 node = factory.make_Node()
238 self.assertThat(node.system_id, HasLength(6))
239
240- def test_empty_architecture_rejected_for_type_node(self):
241- self.assertRaises(
242- ValidationError,
243- factory.make_Node, node_type=NODE_TYPE.MACHINE, architecture='')
244-
245- def test_empty_architecture_rejected_for_type_rack_controller(self):
246- self.assertRaises(
247- ValidationError,
248- factory.make_Node, node_type=NODE_TYPE.RACK_CONTROLLER,
249- architecture='')
250-
251 def test__set_zone(self):
252 zone = factory.make_Zone()
253 node = factory.make_Node()
254@@ -3429,18 +3418,6 @@ class TestNode(MAASServerTestCase):
255 node = reload_object(node)
256 self.assertIsNone(node.status_expires)
257
258- def test_save_checks_architecture_for_installable_nodes(self):
259- device = factory.make_Device(architecture='')
260- device.node_type = factory.pick_enum(
261- NODE_TYPE, but_not=[NODE_TYPE.DEVICE])
262- device._state._changed_fields['architecture'] = None
263- exception = self.assertRaises(
264- ValidationError, device.as_node().save)
265- self.assertEqual(
266- exception.message_dict,
267- {'architecture':
268- ['Architecture must be defined for installable nodes.']})
269-
270 def test_netboot_defaults_to_True(self):
271 node = Node()
272 self.assertTrue(node.netboot)
273diff --git a/src/maasserver/static/js/angular/controllers/add_hardware.js b/src/maasserver/static/js/angular/controllers/add_hardware.js
274index 5e1b0d8..7d4d7f0 100644
275--- a/src/maasserver/static/js/angular/controllers/add_hardware.js
276+++ b/src/maasserver/static/js/angular/controllers/add_hardware.js
277@@ -25,6 +25,7 @@ angular.module('MAAS').controller('AddHardwareController', [
278 $scope.pools = ResourcePoolsManager.getItems();
279 $scope.domains = DomainsManager.getItems();
280 $scope.architectures = GeneralManager.getData("architectures");
281+ $scope.architectures.push("Choose an architecture");
282 $scope.hwe_kernels = GeneralManager.getData("hwe_kernels");
283 $scope.default_min_hwe_kernel = GeneralManager.getData(
284 "default_min_hwe_kernel");
285@@ -274,22 +275,6 @@ angular.module('MAAS').controller('AddHardwareController', [
286 }
287 }
288
289- // Get the default architecture from the loaded architectures.
290- function defaultArchitecture() {
291- if($scope.architectures.length === 0) {
292- return '';
293- } else {
294- // Return amd64/generic first if available.
295- var i;
296- for(i = 0; i < $scope.architectures.length; i++) {
297- if($scope.architectures[i] === "amd64/generic") {
298- return $scope.architectures[i];
299- }
300- }
301- return $scope.architectures[0];
302- }
303- }
304-
305 // Return a new MAC address object.
306 function newMAC() {
307 return {
308@@ -326,7 +311,7 @@ angular.module('MAAS').controller('AddHardwareController', [
309 macs: [newMAC()],
310 zone: defaultZone(),
311 pool: defaultResourcePool(),
312- architecture: defaultArchitecture(),
313+ architecture: '',
314 min_hwe_kernel: $scope.default_min_hwe_kernel.text,
315 power: {
316 type: null,
317@@ -419,15 +404,6 @@ angular.module('MAAS').controller('AddHardwareController', [
318 $scope.machine = newMachine($scope.machine);
319 $scope.chassis = newChassis($scope.chassis);
320 $scope.error = null;
321-
322- // If the machine doesn't have an architecture
323- // set then it was created before all of the
324- // architectures were loaded. Set the default
325- // architecture for that machine.
326- if(angular.isObject($scope.machine) &&
327- $scope.machine.architecture === '') {
328- $scope.machine.architecture = defaultArchitecture();
329- }
330 $scope.viewable = true;
331 });
332
333@@ -518,7 +494,8 @@ angular.module('MAAS').controller('AddHardwareController', [
334 $scope.machine === null ||
335 $scope.machine.zone === null ||
336 $scope.machine.pool === null ||
337- $scope.machine.architecture === '' ||
338+ ($scope.machine.architecture === 'Choose an architecture' &&
339+ $scope.machine.power.type.name !== 'ipmi') ||
340 $scope.machine.power.type === null ||
341 $scope.invalidName($scope.machine));
342 if(in_error) {
343@@ -527,7 +504,8 @@ angular.module('MAAS').controller('AddHardwareController', [
344
345 // Make sure none of the mac addresses are in error. The first one
346 // cannot be blank the remaining are allowed to be empty.
347- if($scope.machine.macs[0].mac === '' ||
348+ if(($scope.machine.macs[0].mac === '' &&
349+ $scope.machine.power.type.name !== 'ipmi') ||
350 $scope.machine.macs[0].error) {
351 return true;
352 }
353@@ -575,6 +553,11 @@ angular.module('MAAS').controller('AddHardwareController', [
354 // the device.
355 $scope.error = null;
356
357+ // Set the architecture to empty string if none was chosen.
358+ if($scope.machine.architecture === "Choose an architecture") {
359+ $scope.machine.architecture = '';
360+ }
361+
362 // Add the machine.
363 MachinesManager.create(
364 convertMachineToProtocol($scope.machine)).then(
365diff --git a/src/maasserver/static/js/angular/controllers/tests/test_add_hardware.js b/src/maasserver/static/js/angular/controllers/tests/test_add_hardware.js
366index 4f097c4..48f8783 100644
367--- a/src/maasserver/static/js/angular/controllers/tests/test_add_hardware.js
368+++ b/src/maasserver/static/js/angular/controllers/tests/test_add_hardware.js
369@@ -105,7 +105,7 @@ describe("AddHardwareController", function() {
370 expect($scope.zones).toBe(ZonesManager.getItems());
371 expect($scope.pools).toBe(ResourcePoolsManager.getItems());
372 expect($scope.domains).toBe(DomainsManager.getItems());
373- expect($scope.architectures).toEqual([]);
374+ expect($scope.architectures).toEqual(['Choose an architecture']);
375 expect($scope.hwe_kernels).toEqual([]);
376 expect($scope.power_types).toEqual([]);
377 expect($scope.error).toBeNull();
378@@ -121,32 +121,14 @@ describe("AddHardwareController", function() {
379 expect(ManagerHelperService.loadManagers).not.toHaveBeenCalled();
380 });
381
382- it("initializes machine architecture with first arch", function() {
383- var loadManagers_defer = $q.defer();
384- var loadItems_defer = $q.defer();
385- var controller = makeController(loadManagers_defer, loadItems_defer);
386- var arch = makeName("arch");
387- $scope.architectures = [arch];
388- $scope.machine = {
389- architecture: '',
390- power: {type: makeName("power_type")}
391- };
392- $scope.show();
393-
394- loadManagers_defer.resolve();
395- loadItems_defer.resolve();
396- $scope.$digest();
397- expect($scope.machine.architecture).toEqual(arch);
398- });
399-
400- it("initializes machine architecture with amd64 arch", function() {
401+ it("initializes machine arch with 'Choose an architecture'", function() {
402 var loadManagers_defer = $q.defer();
403 var loadItems_defer = $q.defer();
404 var controller = makeController(loadManagers_defer, loadItems_defer);
405 var arch = makeName("arch");
406 $scope.architectures = [arch, "amd64/generic"];
407 $scope.machine = {
408- architecture: '',
409+ architecture: 'Choose an architecture',
410 power: {type: makeName("power_type")}
411 };
412 $scope.show();
413@@ -154,7 +136,7 @@ describe("AddHardwareController", function() {
414 loadManagers_defer.resolve();
415 loadItems_defer.resolve();
416 $scope.$digest();
417- expect($scope.machine.architecture).toEqual("amd64/generic");
418+ expect($scope.machine.architecture).toEqual("Choose an architecture");
419 });
420
421 it("doesnt initializes machine architecture if set", function() {
422@@ -383,11 +365,11 @@ describe("AddHardwareController", function() {
423 expect($scope.machineHasError()).toBe(true);
424 });
425
426- it("returns true if architecture is empty", function() {
427+ it("returns true if architecture is not chosen", function() {
428 var controller = makeControllerWithMachine();
429 $scope.machine.zone = {};
430 $scope.machine.pool = {};
431- $scope.machine.architecture = '';
432+ $scope.machine.architecture = 'Choose an architecture';
433 $scope.machine.power.type = {};
434 $scope.machine.macs[0].mac = '00:11:22:33:44:55';
435 $scope.machine.macs[0].error = false;
436diff --git a/src/maasserver/static/partials/nodes-list.html b/src/maasserver/static/partials/nodes-list.html
437index 8310b39..d318cab 100644
438--- a/src/maasserver/static/partials/nodes-list.html
439+++ b/src/maasserver/static/partials/nodes-list.html
440@@ -382,7 +382,7 @@
441 <select name="architecture" id="architecture"
442 data-ng-model="machine.architecture"
443 data-ng-options="arch for arch in architectures">
444- <option value="" disabled>Choose an architecture</option>
445+ <option value="">Choose an architecture</option>
446 </select>
447 </div>
448 </div>
449diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py
450index 72fd395..183d74f 100644
451--- a/src/maasserver/websockets/handlers/tests/test_machine.py
452+++ b/src/maasserver/websockets/handlers/tests/test_machine.py
453@@ -1778,37 +1778,6 @@ class TestMachineHandler(MAASServerTestCase):
454 HandlerPermissionError,
455 handler.create, {})
456
457- def test_create_raises_validation_error_for_missing_pxe_mac(self):
458- user = factory.make_admin()
459- handler = MachineHandler(user, {})
460- zone = factory.make_Zone()
461- params = {
462- "architecture": make_usable_architecture(self),
463- "zone": {
464- "name": zone.name,
465- },
466- }
467- error = self.assertRaises(
468- HandlerValidationError, handler.create, params)
469- self.assertThat(error.message_dict, Equals(
470- {'mac_addresses': ['This field is required.']}))
471-
472- def test_create_raises_validation_error_for_missing_architecture(self):
473- user = factory.make_admin()
474- handler = MachineHandler(user, {})
475- zone = factory.make_Zone()
476- params = {
477- "pxe_mac": factory.make_mac_address(),
478- "zone": {
479- "name": zone.name,
480- },
481- }
482- error = self.assertRaises(
483- HandlerValidationError, handler.create, params)
484- self.assertThat(error.message_dict, Equals(
485- {'architecture': [
486- 'Architecture must be defined for installable nodes.']}))
487-
488 def test_create_creates_node(self):
489 user = factory.make_admin()
490 handler = MachineHandler(user, {})

Subscribers

People subscribed via source and target branches