Merge lp:~blake-rouse/maas/fix-1495775 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: 4263
Proposed branch: lp:~blake-rouse/maas/fix-1495775
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 281 lines (+110/-11)
11 files modified
src/maasserver/fields.py (+10/-0)
src/maasserver/forms_interface_link.py (+2/-1)
src/maasserver/models/interface.py (+37/-3)
src/maasserver/models/signals/dhcp.py (+1/-1)
src/maasserver/models/signals/dns.py (+1/-1)
src/maasserver/models/signals/events.py (+1/-1)
src/maasserver/models/signals/monitors.py (+1/-1)
src/maasserver/models/signals/power.py (+1/-1)
src/maasserver/models/tests/test_interface.py (+54/-0)
src/maasserver/tests/test_forms_interface_link.py (+1/-1)
src/maasserver/utils/tests/test_signals.py (+1/-1)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-1495775
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Review via email: mp+271133@code.launchpad.net

Commit message

Fix the link-subnet to allow mode to be case-insensitive. Fix disabling and enabling an interface where the links are cleaned correctly.

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

lgtm!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/fields.py'
--- src/maasserver/fields.py 2015-07-06 08:52:20 +0000
+++ src/maasserver/fields.py 2015-09-15 14:16:03 +0000
@@ -44,6 +44,7 @@
44from django.db.models.fields.subclassing import Creator44from django.db.models.fields.subclassing import Creator
45from django.forms import (45from django.forms import (
46 CharField,46 CharField,
47 ChoiceField,
47 ModelChoiceField,48 ModelChoiceField,
48)49)
49from django.utils.encoding import force_text50from django.utils.encoding import force_text
@@ -600,3 +601,12 @@
600 "Invalid IP address: %s; provide a list of "601 "Invalid IP address: %s; provide a list of "
601 "space-separated IP addresses" % ip)602 "space-separated IP addresses" % ip)
602 return ' '.join(ips)603 return ' '.join(ips)
604
605
606class CaseInsensitiveChoiceField(ChoiceField):
607 """ChoiceField that allows the input to be case insensitive."""
608
609 def to_python(self, value):
610 if value not in self.empty_values:
611 value = value.lower()
612 return super(CaseInsensitiveChoiceField, self).to_python(value)
603613
=== modified file 'src/maasserver/forms_interface_link.py'
--- src/maasserver/forms_interface_link.py 2015-09-03 16:23:08 +0000
+++ src/maasserver/forms_interface_link.py 2015-09-15 14:16:03 +0000
@@ -22,6 +22,7 @@
22 INTERFACE_LINK_TYPE_CHOICES,22 INTERFACE_LINK_TYPE_CHOICES,
23 IPADDRESS_TYPE,23 IPADDRESS_TYPE,
24)24)
25from maasserver.fields import CaseInsensitiveChoiceField
25from maasserver.models.interface import Interface26from maasserver.models.interface import Interface
26from maasserver.utils.forms import (27from maasserver.utils.forms import (
27 compose_invalid_choice_text,28 compose_invalid_choice_text,
@@ -34,7 +35,7 @@
34class InterfaceLinkForm(forms.Form):35class InterfaceLinkForm(forms.Form):
35 """Interface link form."""36 """Interface link form."""
3637
37 mode = forms.ChoiceField(38 mode = CaseInsensitiveChoiceField(
38 choices=INTERFACE_LINK_TYPE_CHOICES, required=True,39 choices=INTERFACE_LINK_TYPE_CHOICES, required=True,
39 error_messages={40 error_messages={
40 'invalid_choice': compose_invalid_choice_text(41 'invalid_choice': compose_invalid_choice_text(
4142
=== modified file 'src/maasserver/models/interface.py'
--- src/maasserver/models/interface.py 2015-09-14 19:31:45 +0000
+++ src/maasserver/models/interface.py 2015-09-15 14:16:03 +0000
@@ -57,6 +57,7 @@
57 VerboseRegexValidator,57 VerboseRegexValidator,
58 MACAddressField,58 MACAddressField,
59)59)
60from maasserver.utils.signals import connect_to_field_change
60from maasserver.clusterrpc.dhcp import update_host_maps61from maasserver.clusterrpc.dhcp import update_host_maps
61from maasserver.models.staticipaddress import StaticIPAddress62from maasserver.models.staticipaddress import StaticIPAddress
62from maasserver.models.cleansave import CleanSave63from maasserver.models.cleansave import CleanSave
@@ -235,6 +236,9 @@
235 def get_name(self):236 def get_name(self):
236 return self.name237 return self.name
237238
239 def is_enabled(self):
240 return self.enabled
241
238 def get_links(self):242 def get_links(self):
239 """Return the definition of links connected to this interface.243 """Return the definition of links connected to this interface.
240244
@@ -1115,9 +1119,6 @@
1115 def get_type(self):1119 def get_type(self):
1116 return INTERFACE_TYPE.PHYSICAL1120 return INTERFACE_TYPE.PHYSICAL
11171121
1118 def is_enabled(self):
1119 return self.enabled
1120
1121 def clean(self):1122 def clean(self):
1122 super(PhysicalInterface, self).clean()1123 super(PhysicalInterface, self).clean()
1123 # Node and MAC address is always required for a physical interface.1124 # Node and MAC address is always required for a physical interface.
@@ -1151,6 +1152,39 @@
1151 })1152 })
11521153
11531154
1155def interface_enabled_or_disabled(instance, old_values, **kwargs):
1156 """When an interface is enabled be sure at minimum a LINK_UP is created.
1157 When an interface is disabled make sure that all its links are removed,
1158 even for all its children that are now disabled."""
1159 if instance.type != INTERFACE_TYPE.PHYSICAL:
1160 return
1161 if instance.is_enabled():
1162 # Make sure it has a LINK_UP link, and for its children.
1163 instance.ensure_link_up()
1164 for rel in instance.children_relationships.all():
1165 rel.child.ensure_link_up()
1166 else:
1167 # Was disabled. Remove the links.
1168 for ip_address in instance.ip_addresses.exclude(
1169 alloc_type=IPADDRESS_TYPE.DISCOVERED):
1170 instance.unlink_ip_address(ip_address, clearing_config=True)
1171 # If any of the children of this interface are now disabled, all of
1172 # their links need to be removed as well.
1173 for rel in instance.children_relationships.all():
1174 if not rel.child.is_enabled():
1175 for ip_address in rel.child.ip_addresses.all():
1176 rel.child.unlink_ip_address(
1177 ip_address, clearing_config=True)
1178
1179
1180connect_to_field_change(
1181 interface_enabled_or_disabled,
1182 Interface, ['enabled'], delete=False)
1183connect_to_field_change(
1184 interface_enabled_or_disabled,
1185 PhysicalInterface, ['enabled'], delete=False)
1186
1187
1154class BondInterface(Interface):1188class BondInterface(Interface):
11551189
1156 class Meta(Interface.Meta):1190 class Meta(Interface.Meta):
11571191
=== modified file 'src/maasserver/models/signals/dhcp.py'
--- src/maasserver/models/signals/dhcp.py 2015-09-08 18:41:57 +0000
+++ src/maasserver/models/signals/dhcp.py 2015-09-15 14:16:03 +0000
@@ -27,7 +27,7 @@
27 NodeGroupInterface,27 NodeGroupInterface,
28 Subnet,28 Subnet,
29)29)
30from maasserver.models.signals.base import connect_to_field_change30from maasserver.utils.signals import connect_to_field_change
3131
3232
33def dhcp_post_change_NodeGroupInterface(instance, old_values, **kwargs):33def dhcp_post_change_NodeGroupInterface(instance, old_values, **kwargs):
3434
=== modified file 'src/maasserver/models/signals/dns.py'
--- src/maasserver/models/signals/dns.py 2015-09-08 18:41:57 +0000
+++ src/maasserver/models/signals/dns.py 2015-09-15 14:16:03 +0000
@@ -29,7 +29,7 @@
29 NodeGroupInterface,29 NodeGroupInterface,
30 Subnet,30 Subnet,
31)31)
32from maasserver.models.signals.base import connect_to_field_change32from maasserver.utils.signals import connect_to_field_change
3333
3434
35@receiver(post_save, sender=NodeGroup)35@receiver(post_save, sender=NodeGroup)
3636
=== modified file 'src/maasserver/models/signals/events.py'
--- src/maasserver/models/signals/events.py 2015-06-10 11:24:44 +0000
+++ src/maasserver/models/signals/events.py 2015-09-15 14:16:03 +0000
@@ -21,7 +21,7 @@
21 Node,21 Node,
22)22)
23from maasserver.models.node import NODE_STATUS23from maasserver.models.node import NODE_STATUS
24from maasserver.models.signals.base import connect_to_field_change24from maasserver.utils.signals import connect_to_field_change
25from provisioningserver.events import (25from provisioningserver.events import (
26 EVENT_DETAILS,26 EVENT_DETAILS,
27 EVENT_TYPES,27 EVENT_TYPES,
2828
=== modified file 'src/maasserver/models/signals/monitors.py'
--- src/maasserver/models/signals/monitors.py 2015-04-01 11:49:45 +0000
+++ src/maasserver/models/signals/monitors.py 2015-09-15 14:16:03 +0000
@@ -17,8 +17,8 @@
1717
1818
19from maasserver.models import Node19from maasserver.models import Node
20from maasserver.models.signals.base import connect_to_field_change
21from maasserver.node_status import get_failed_status20from maasserver.node_status import get_failed_status
21from maasserver.utils.signals import connect_to_field_change
2222
23# Useful to disconnect this in testing.23# Useful to disconnect this in testing.
24MONITOR_CANCEL_CONNECT = True24MONITOR_CANCEL_CONNECT = True
2525
=== modified file 'src/maasserver/models/signals/power.py'
--- src/maasserver/models/signals/power.py 2015-06-10 11:24:44 +0000
+++ src/maasserver/models/signals/power.py 2015-09-15 14:16:03 +0000
@@ -18,13 +18,13 @@
1818
19from maasserver.enum import POWER_STATE19from maasserver.enum import POWER_STATE
20from maasserver.models import Node20from maasserver.models import Node
21from maasserver.models.signals.base import connect_to_field_change
22from maasserver.node_status import QUERY_TRANSITIONS21from maasserver.node_status import QUERY_TRANSITIONS
23from maasserver.rpc import getClientFor22from maasserver.rpc import getClientFor
24from maasserver.utils.orm import (23from maasserver.utils.orm import (
25 post_commit,24 post_commit,
26 transactional,25 transactional,
27)26)
27from maasserver.utils.signals import connect_to_field_change
28from provisioningserver.logger import get_maas_logger28from provisioningserver.logger import get_maas_logger
29from provisioningserver.power.poweraction import (29from provisioningserver.power.poweraction import (
30 PowerActionFail,30 PowerActionFail,
3131
=== modified file 'src/maasserver/models/tests/test_interface.py'
--- src/maasserver/models/tests/test_interface.py 2015-09-14 19:31:45 +0000
+++ src/maasserver/models/tests/test_interface.py 2015-09-15 14:16:03 +0000
@@ -1730,3 +1730,57 @@
1730 INTERFACE_LINK_TYPE.STATIC, subnet_v4,1730 INTERFACE_LINK_TYPE.STATIC, subnet_v4,
1731 ip_address=requested_ip))1731 ip_address=requested_ip))
1732 self.assertEquals(sentinel.claimed_ip, claimed_ip)1732 self.assertEquals(sentinel.claimed_ip, claimed_ip)
1733
1734
1735class TestEnableAndDisableInterface(MAASServerTestCase):
1736
1737 def test__enable_interface_creates_link_up(self):
1738 interface = factory.make_Interface(
1739 INTERFACE_TYPE.PHYSICAL, enabled=False)
1740 interface.enabled = True
1741 interface.save()
1742 link_ip = interface.ip_addresses.get(
1743 alloc_type=IPADDRESS_TYPE.STICKY, ip=None)
1744 self.assertIsNotNone(link_ip)
1745
1746 def test__enable_interface_creates_link_up_on_children(self):
1747 interface = factory.make_Interface(
1748 INTERFACE_TYPE.PHYSICAL, enabled=False)
1749 vlan_interface = factory.make_Interface(
1750 INTERFACE_TYPE.VLAN, parents=[interface])
1751 interface.enabled = True
1752 interface.save()
1753 link_ip = vlan_interface.ip_addresses.get(
1754 alloc_type=IPADDRESS_TYPE.STICKY, ip=None)
1755 self.assertIsNotNone(link_ip)
1756
1757 def test__disable_interface_removes_links(self):
1758 interface = factory.make_Interface(
1759 INTERFACE_TYPE.PHYSICAL, enabled=True)
1760 interface.ensure_link_up()
1761 interface.enabled = False
1762 interface.save()
1763 self.assertItemsEqual([], interface.ip_addresses.all())
1764
1765 def test__disable_interface_removes_links_on_children(self):
1766 interface = factory.make_Interface(
1767 INTERFACE_TYPE.PHYSICAL, enabled=True)
1768 vlan_interface = factory.make_Interface(
1769 INTERFACE_TYPE.VLAN, parents=[interface])
1770 vlan_interface.ensure_link_up()
1771 interface.enabled = False
1772 interface.save()
1773 self.assertItemsEqual([], vlan_interface.ip_addresses.all())
1774
1775 def test__disable_interface_doesnt_remove_links_on_enabled_children(self):
1776 node = factory.make_Node()
1777 nic0 = factory.make_Interface(
1778 INTERFACE_TYPE.PHYSICAL, node=node, enabled=True)
1779 nic1 = factory.make_Interface(
1780 INTERFACE_TYPE.PHYSICAL, node=node, enabled=True)
1781 bond_interface = factory.make_Interface(
1782 INTERFACE_TYPE.BOND, parents=[nic0, nic1])
1783 bond_interface.ensure_link_up()
1784 nic0.enabled = False
1785 nic0.save()
1786 self.assertEquals(1, bond_interface.ip_addresses.count())
17331787
=== modified file 'src/maasserver/tests/test_forms_interface_link.py'
--- src/maasserver/tests/test_forms_interface_link.py 2015-09-08 21:59:19 +0000
+++ src/maasserver/tests/test_forms_interface_link.py 2015-09-15 14:16:03 +0000
@@ -48,7 +48,7 @@
48 def test__mode_is_case_insensitive(self):48 def test__mode_is_case_insensitive(self):
49 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)49 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
50 form = InterfaceLinkForm(instance=interface, data={50 form = InterfaceLinkForm(instance=interface, data={
51 "mode": INTERFACE_LINK_TYPE.DHCP.lower(),51 "mode": INTERFACE_LINK_TYPE.DHCP.upper(),
52 })52 })
53 self.assertTrue(form.is_valid(), form.errors)53 self.assertTrue(form.is_valid(), form.errors)
5454
5555
=== renamed file 'src/maasserver/models/signals/base.py' => 'src/maasserver/utils/signals.py'
=== renamed file 'src/maasserver/models/signals/tests/test_base.py' => 'src/maasserver/utils/tests/test_signals.py'
--- src/maasserver/models/signals/tests/test_base.py 2015-06-10 11:24:44 +0000
+++ src/maasserver/utils/tests/test_signals.py 2015-09-15 14:16:03 +0000
@@ -14,10 +14,10 @@
14__metaclass__ = type14__metaclass__ = type
15__all__ = []15__all__ = []
1616
17from maasserver.models.signals.base import connect_to_field_change
18from maasserver.testing.factory import factory17from maasserver.testing.factory import factory
19from maasserver.testing.testcase import MAASServerTestCase18from maasserver.testing.testcase import MAASServerTestCase
20from maasserver.tests.models import FieldChangeTestModel19from maasserver.tests.models import FieldChangeTestModel
20from maasserver.utils.signals import connect_to_field_change
21from maastesting.djangotestcase import TestModelMixin21from maastesting.djangotestcase import TestModelMixin
22from maastesting.matchers import (22from maastesting.matchers import (
23 IsCallable,23 IsCallable,