Merge ~ltrager/maas:interface_param_dont_allow_unconfigured into maas:master

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: 94754d4c446223f1a680cc0eb2d45627da7347ee
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:interface_param_dont_allow_unconfigured
Merge into: maas:master
Diff against target: 240 lines (+84/-26)
2 files modified
src/maasserver/forms/parameters.py (+30/-17)
src/maasserver/forms/tests/test_parameters.py (+54/-9)
Reviewer Review Type Date Requested Status
Newell Jensen (community) Approve
Alberto Donato (community) Needs Information
Review via email: mp+370395@code.launchpad.net

Commit message

Don't allow unconfigured or disabled interfaces from being set as a script parameter.

To post a comment you must log in.
Revision history for this message
Alberto Donato (ack) wrote :

A few questions/comments inline

review: Needs Information
5a59707... by Lee Trager

ack fixes

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

Thanks for the review, responses inline :)

Revision history for this message
Alberto Donato (ack) :
94754d4... by Lee Trager

ack fixes

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

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/forms/parameters.py b/src/maasserver/forms/parameters.py
2index 034f353..ceb9284 100644
3--- a/src/maasserver/forms/parameters.py
4+++ b/src/maasserver/forms/parameters.py
5@@ -20,6 +20,7 @@ from django.forms import (
6 Field,
7 Form,
8 )
9+from maasserver.enum import IPADDRESS_TYPE
10 from maasserver.utils.dns import validate_url
11 from maasserver.utils.forms import set_form_error
12 from maasserver.utils.mac import is_mac
13@@ -44,6 +45,20 @@ class ParametersForm(Form):
14 self._node = node
15 self._script = script
16
17+ def _get_interfaces(self):
18+ """Return a queryset of the Interfaces that can be used."""
19+ interfaces = self._node.interface_set.filter(
20+ children_relationships=None,
21+ enabled=True)
22+ interfaces = interfaces.exclude(
23+ ip_addresses__alloc_type=IPADDRESS_TYPE.DISCOVERED)
24+ # Exclude all IPAddresses of type DISCOVERED before checking if an
25+ # Interface doesn't have IPAddresses. If done as one excludes
26+ # DISCOVERED IPAddresses won't be filtered out before isnull is
27+ # checked.
28+ return interfaces.exclude(
29+ ip_addresses__isnull=True, ip_addresses__ip=None)
30+
31 def clean_parameters(self):
32 """Validate the parameters set in the embedded YAML within the script.
33 """
34@@ -267,7 +282,7 @@ class ParametersForm(Form):
35 def _validate_and_clean_storage_all(
36 self, param_name, param, result_params, ret):
37 """Validate and clean storage input when set to all."""
38- if not self._node.physicalblockdevice_set.exists():
39+ if len(self._node.physicalblockdevice_set) == 0:
40 # Use 'all' as a place holder until the disks get added during
41 # commissioning.
42 clean_param = copy.deepcopy(result_params)
43@@ -319,7 +334,7 @@ class ParametersForm(Form):
44 qs = self._node.physicalblockdevice_set.filter(
45 Q(name=i) | Q(name=os.path.basename(i)) |
46 Q(model=i) | Q(serial=i) | Q(tags__overlap=[i]))
47- if not qs.exists():
48+ if len(qs) == 0:
49 set_form_error(
50 self, param_name, "Unknown storage device for %s(%s)" % (
51 self._node.fqdn, self._node.system_id))
52@@ -344,15 +359,15 @@ class ParametersForm(Form):
53 def _validate_and_clean_interface_all(
54 self, param_name, param, result_params, ret):
55 """Validate and clean interface input when set to all."""
56- if not self._node.interface_set.exists():
57- # Use 'all' as a place holder until the disks get added during
58+ interfaces = self._get_interfaces()
59+ if len(interfaces) == 0:
60+ # Use 'all' as a place holder until the Interfaces get added during
61 # commissioning.
62 clean_param = copy.deepcopy(result_params)
63 clean_param[param_name] = param
64 ret.append(clean_param)
65 else:
66- for interface in self._node.interface_set.filter(
67- children_relationships=None):
68+ for interface in interfaces:
69 clean_param = copy.deepcopy(result_params)
70 clean_param[param_name] = copy.deepcopy(param)
71 clean_param[param_name]['value'] = self._interface_to_dict(
72@@ -361,10 +376,9 @@ class ParametersForm(Form):
73
74 def _validate_and_clean_interface_id(
75 self, param_name, param, result_params, ret):
76- """Validate and clean storage input when id."""
77+ """Validate and clean interface input when id."""
78 try:
79- interface = self._node.interface_set.get(
80- children_relationships=None, id=int(param['value']))
81+ interface = self._get_interfaces().get(id=int(param['value']))
82 except ObjectDoesNotExist:
83 set_form_error(
84 self, param_name, 'Interface id does not exist')
85@@ -381,12 +395,12 @@ class ParametersForm(Form):
86 value = param['value']
87 for i in value.split(','):
88 if ':' in i:
89- # Allow users to specify a disk using the vendor and product.
90+ # Allow users to specify an Interface using the vendor and
91+ # product.
92 vendor, product = i.split(':')
93 try:
94- interface = self._node.interface_set.get(
95- vendor=vendor, product=product,
96- children_relationships=None)
97+ interface = self._get_interfaces().get(
98+ vendor=vendor, product=product)
99 except ObjectDoesNotExist:
100 pass
101 else:
102@@ -397,14 +411,13 @@ class ParametersForm(Form):
103 ret.append(clean_param)
104 continue
105
106- qs = self._node.interface_set.filter(children_relationships=None)
107 if is_mac(i):
108- qs = qs.filter(mac_address=i)
109+ qs = self._get_interfaces().filter(mac_address=i)
110 else:
111- qs = qs.filter(
112+ qs = self._get_interfaces().filter(
113 Q(name=i) | Q(vendor=i) | Q(product=i) |
114 Q(tags__overlap=[i]))
115- if not qs.exists():
116+ if len(qs) == 0:
117 set_form_error(
118 self, param_name, "Unknown interface for %s(%s)" % (
119 self._node.fqdn, self._node.system_id))
120diff --git a/src/maasserver/forms/tests/test_parameters.py b/src/maasserver/forms/tests/test_parameters.py
121index 7da3034..bf94f65 100644
122--- a/src/maasserver/forms/tests/test_parameters.py
123+++ b/src/maasserver/forms/tests/test_parameters.py
124@@ -7,7 +7,10 @@ __all__ = []
125
126 import random
127
128-from maasserver.enum import INTERFACE_TYPE
129+from maasserver.enum import (
130+ INTERFACE_TYPE,
131+ IPADDRESS_TYPE,
132+)
133 from maasserver.forms.parameters import ParametersForm
134 from maasserver.testing.factory import factory
135 from maasserver.testing.testcase import MAASServerTestCase
136@@ -578,8 +581,18 @@ class TestParametersForm(MAASServerTestCase):
137
138 def test__input_interface_all(self):
139 node = factory.make_Node()
140- for _ in range(3):
141- factory.make_Interface(node=node)
142+ subnet = factory.make_Subnet()
143+ usable_interfaces = [
144+ factory.make_Interface(node=node, subnet=subnet)
145+ for _ in range(3)
146+ ]
147+ # Unconfigured or disabled interfaces
148+ factory.make_Interface(node=node, enabled=False)
149+ factory.make_Interface(node=node, link_connected=False)
150+ discovered = factory.make_Interface(node=node, subnet=subnet)
151+ discovered_ip = discovered.ip_addresses.first()
152+ discovered_ip.alloc_type = IPADDRESS_TYPE.DISCOVERED
153+ discovered_ip.save()
154 script = factory.make_Script(parameters={
155 'interface': {'type': 'interface'},
156 })
157@@ -587,8 +600,8 @@ class TestParametersForm(MAASServerTestCase):
158 data={'interface': 'all'}, script=script, node=node)
159 self.assertTrue(form.is_valid(), form.errors)
160 input = form.cleaned_data['input']
161- self.assertEquals(node.interface_set.count(), len(input))
162- for interface in node.interface_set.all():
163+ self.assertEquals(len(usable_interfaces), len(input))
164+ for interface in usable_interfaces:
165 for i in input:
166 if (str(interface.mac_address) ==
167 i['interface']['value']['mac_address']):
168@@ -603,9 +616,11 @@ class TestParametersForm(MAASServerTestCase):
169
170 def test__input_interface_all_only_includes_children(self):
171 node = factory.make_Node(interface=False)
172+ subnet = factory.make_Subnet()
173 bond = factory.make_Interface(
174- node=node, iftype=INTERFACE_TYPE.BOND, parents=[
175- factory.make_Interface(node=node) for _ in range(2)])
176+ node=node, iftype=INTERFACE_TYPE.BOND, subnet=subnet, parents=[
177+ factory.make_Interface(node=node)
178+ for _ in range(2)])
179 script = factory.make_Script(parameters={
180 'interface': {'type': 'interface'},
181 })
182@@ -624,8 +639,9 @@ class TestParametersForm(MAASServerTestCase):
183
184 def test__input_interface_id(self):
185 node = factory.make_Node()
186+ subnet = factory.make_Subnet()
187 for _ in range(3):
188- factory.make_Interface(node=node)
189+ factory.make_Interface(node=node, subnet=subnet)
190 script = factory.make_Script(parameters={
191 'interface': {'type': 'interface'},
192 })
193@@ -681,10 +697,25 @@ class TestParametersForm(MAASServerTestCase):
194 'interface': ['Interface id does not exist'],
195 }, form.errors)
196
197+ def test__input_interface_id_errors_on_unconfigured_or_disabled(self):
198+ node = factory.make_Node()
199+ bad_interface = random.choice([
200+ factory.make_Interface(node=node, enabled=False),
201+ factory.make_Interface(node=node, link_connected=False),
202+ ])
203+ script = factory.make_Script(parameters={
204+ 'interface': {'type': 'interface'},
205+ })
206+ form = ParametersForm(
207+ data={'interface': bad_interface.id},
208+ script=script, node=node)
209+ self.assertFalse(form.is_valid())
210+
211 def test__input_interface_list(self):
212 node = factory.make_Node()
213+ subnet = factory.make_Subnet()
214 for _ in range(10):
215- factory.make_Interface(node=node)
216+ factory.make_Interface(node=node, subnet=subnet)
217 script = factory.make_Script(parameters={
218 'interface': {'type': 'interface'},
219 })
220@@ -754,6 +785,20 @@ class TestParametersForm(MAASServerTestCase):
221 node.fqdn, node.system_id)],
222 }, form.errors)
223
224+ def test__input_interface_name_errors_on_unconfigured_or_disabled(self):
225+ node = factory.make_Node()
226+ bad_interface = random.choice([
227+ factory.make_Interface(node=node, enabled=False),
228+ factory.make_Interface(node=node, link_connected=False),
229+ ])
230+ script = factory.make_Script(parameters={
231+ 'interface': {'type': 'interface'},
232+ })
233+ form = ParametersForm(
234+ data={'interface': bad_interface.name},
235+ script=script, node=node)
236+ self.assertFalse(form.is_valid())
237+
238 def test__input_url_validates_required(self):
239 script = factory.make_Script(parameters={'url': {
240 'type': 'url',

Subscribers

People subscribed via source and target branches