Merge lp:~julian-edwards/maas/mac-network-removal into lp:~maas-committers/maas/trunk

Proposed by Julian Edwards
Status: Merged
Approved by: Julian Edwards
Approved revision: no longer in the source branch.
Merged at revision: 3005
Proposed branch: lp:~julian-edwards/maas/mac-network-removal
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 94 lines (+49/-3)
3 files modified
src/maasserver/api/networks.py (+3/-1)
src/maasserver/forms.py (+20/-2)
src/maasserver/tests/test_forms_network.py (+26/-0)
To merge this branch: bzr merge lp:~julian-edwards/maas/mac-network-removal
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Approve
Review via email: mp+234907@code.launchpad.net

Commit message

Make it possible to remove the last MAC from the networks edit form.

Description of the change

Because the same form is used for the API and the web view, and the API uses its own operations for manipulating the MAC addresses, the web view was unable to assume it could delete the mac_addresses on the Network.

I've fixed it by defaulting to deleting the mac_addresses if not set, unless a flag is passed, which the API handler now does.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Ugh. Just one thing missing: could you document the parameter on the form's __init__, preferably in such generally clear terms that "this is a horrible kludge for a very specific situation" is more or less an afterthought?

review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :

You got it, thanks.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/networks.py'
--- src/maasserver/api/networks.py 2014-09-10 16:20:31 +0000
+++ src/maasserver/api/networks.py 2014-09-17 04:06:58 +0000
@@ -73,7 +73,9 @@
73 of users and administrators.73 of users and administrators.
74 """74 """
75 network = get_object_or_404(Network, name=name)75 network = get_object_or_404(Network, name=name)
76 form = NetworkForm(instance=network, data=request.data)76 form = NetworkForm(
77 instance=network, data=request.data,
78 delete_macs_if_not_present=False)
77 if not form.is_valid():79 if not form.is_valid():
78 raise ValidationError(form.errors)80 raise ValidationError(form.errors)
79 return form.save()81 return form.save()
8082
=== modified file 'src/maasserver/forms.py'
--- src/maasserver/forms.py 2014-09-15 14:28:28 +0000
+++ src/maasserver/forms.py 2014-09-17 04:06:58 +0000
@@ -1933,10 +1933,22 @@
1933 widget=forms.SelectMultiple(attrs={'size': 10}),1933 widget=forms.SelectMultiple(attrs={'size': 10}),
1934 )1934 )
19351935
1936 def __init__(self, data=None, instance=None, **kwargs):1936 def __init__(self, data=None, instance=None,
1937 delete_macs_if_not_present=True, **kwargs):
1938 """
1939 :param data: The web request.data
1940 :param instance: the Network instance
1941 :param delete_macs_if_not_present: If there's no mac_addresses present
1942 in the data, then assume that the caller wants to delete them.
1943 Override with True if you don't want that to happen. Yes, this
1944 is a horrible kludge so the same form works in the API and the
1945 web view.
1946 """
1937 super(NetworkForm, self).__init__(1947 super(NetworkForm, self).__init__(
1938 data=data, instance=instance, **kwargs)1948 data=data, instance=instance, **kwargs)
1949 self.macs_in_request = data.get("mac_addresses") if data else None
1939 self.set_up_initial_macaddresses(instance)1950 self.set_up_initial_macaddresses(instance)
1951 self.delete_macs_if_not_present = delete_macs_if_not_present
19401952
1941 def set_up_initial_macaddresses(self, instance):1953 def set_up_initial_macaddresses(self, instance):
1942 """Set the initial value for the field 'macaddresses'.1954 """Set the initial value for the field 'macaddresses'.
@@ -1954,7 +1966,13 @@
1954 """Persist the network into the database."""1966 """Persist the network into the database."""
1955 network = super(NetworkForm, self).save(*args, **kwargs)1967 network = super(NetworkForm, self).save(*args, **kwargs)
1956 macaddresses = self.cleaned_data.get('mac_addresses')1968 macaddresses = self.cleaned_data.get('mac_addresses')
1957 if macaddresses is not None:1969 # Because the form is used in the web view AND the API we need a
1970 # hack. The API uses separate ops to amend the mac_addresses
1971 # list, however the web UI does not. To preserve the API
1972 # behaviour, its handler passes delete_macs_if_not_present as False.
1973 if self.delete_macs_if_not_present and self.macs_in_request is None:
1974 network.macaddress_set.clear()
1975 elif macaddresses is not None:
1958 network.macaddress_set.clear()1976 network.macaddress_set.clear()
1959 network.macaddress_set.add(*macaddresses)1977 network.macaddress_set.add(*macaddresses)
1960 return network1978 return network
19611979
=== modified file 'src/maasserver/tests/test_forms_network.py'
--- src/maasserver/tests/test_forms_network.py 2014-09-10 16:20:31 +0000
+++ src/maasserver/tests/test_forms_network.py 2014-09-17 04:06:58 +0000
@@ -120,6 +120,32 @@
120 network = reload_object(network)120 network = reload_object(network)
121 self.assertItemsEqual(new_macs, network.macaddress_set.all())121 self.assertItemsEqual(new_macs, network.macaddress_set.all())
122122
123 def test_deletes_macaddresses_by_default_if_not_specified(self):
124 network = factory.make_Network()
125 [factory.make_MACAddress(networks=[network]) for _ in range(3)]
126 form = NetworkForm(
127 data={
128 'name': "foo",
129 },
130 instance=network)
131 form.save()
132 network = reload_object(network)
133 self.assertItemsEqual([], network.macaddress_set.all())
134
135 def test_does_not_delete_unspecified_macaddresses_if_told_not_to(self):
136 network = factory.make_Network()
137 macs = [factory.make_MACAddress(networks=[network]) for _ in range(3)]
138 form = NetworkForm(
139 data={
140 'name': "foo",
141 },
142 instance=network,
143 delete_macs_if_not_present=False,
144 )
145 form.save()
146 network = reload_object(network)
147 self.assertItemsEqual(macs, network.macaddress_set.all())
148
123 def test_reports_clashes(self):149 def test_reports_clashes(self):
124 # The uniqueness test on the Network model raises a ValidationError150 # The uniqueness test on the Network model raises a ValidationError
125 # when it finds a clash, but Django is prone to crashing when the151 # when it finds a clash, but Django is prone to crashing when the