Merge lp:~ltrager/maas/disable_delete_rack into lp:~maas-committers/maas/trunk
- disable_delete_rack
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Lee Trager |
Approved revision: | no longer in the source branch. |
Merged at revision: | 5011 |
Proposed branch: | lp:~ltrager/maas/disable_delete_rack |
Merge into: | lp:~maas-committers/maas/trunk |
Prerequisite: | lp:~ltrager/maas/delete_rack |
Diff against target: |
215 lines (+106/-14) 6 files modified
src/maasserver/models/node.py (+10/-9) src/maasserver/models/tests/test_node.py (+30/-5) src/provisioningserver/rpc/cluster.py (+12/-0) src/provisioningserver/rpc/clusterservice.py (+24/-0) src/provisioningserver/rpc/exceptions.py (+5/-0) src/provisioningserver/rpc/tests/test_clusterservice.py (+25/-0) |
To merge this branch: | bzr merge lp:~ltrager/maas/disable_delete_rack |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Review via email: mp+293594@code.launchpad.net |
Commit message
Disable and stop the rackd service if running on deletion
Description of the change
Upon deletion of a rack controller if a secondary rack controller is still connected send an RPC call which disables and stops the maas-rackd service. A user can reenable it using systemctl. As maas-dhcpd{
Last Friday when testing this branch the delete failed because there still was a relation between the rack controller and RegionRackRPCCo
For maas-rackd to be able to disable and stop the maas-rackd service I had to add permission to 99-maas-sudoers. Review at https:/
MAAS Lander (maas-lander) wrote : | # |
There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~ltrager/maas/disable_delete_rack into lp:maas failed. Below is the output from the failed tests.
Hit:1 http://
Hit:2 http://
Get:3 http://
Hit:4 http://
Fetched 94.5 kB in 0s (179 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
build-essential is already the newest version (12.1ubuntu2).
bzr is already the newest version (2.7.0-2ubuntu1).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubun
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
libpq-dev is already the newest version (9.5.2-1).
make is already the newest version (4.1-6).
postgresql is already the newest version (9.5+173...
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~ltrager/maas/disable_delete_rack into lp:maas failed. Below is the output from the failed tests.
Hit:1 http://
Hit:2 http://
Get:3 http://
Hit:4 http://
Fetched 94.5 kB in 0s (217 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
build-essential is already the newest version (12.1ubuntu2).
bzr is already the newest version (2.7.0-2ubuntu1).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubun
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
libpq-dev is already the newest version (9.5.2-1).
make is already the newest version (4.1-6).
postgresql is already the newest version (9.5+173...
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~ltrager/maas/disable_delete_rack into lp:maas failed. Below is the output from the failed tests.
Hit:1 http://
Get:2 http://
Hit:3 http://
Hit:4 http://
Get:5 http://
Get:6 http://
Fetched 174 kB in 0s (397 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
build-essential is already the newest version (12.1ubuntu2).
bzr is already the newest version (2.7.0-2ubuntu1).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubun
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
l...
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~ltrager/maas/disable_delete_rack into lp:maas failed. Below is the output from the failed tests.
Hit:1 http://
Get:2 http://
Hit:3 http://
Hit:4 http://
Fetched 94.5 kB in 0s (231 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
build-essential is already the newest version (12.1ubuntu2).
bzr is already the newest version (2.7.0-2ubuntu1).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubun
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
libpq-dev is already the newest version (9.5.2-1).
make is already the newest version (4.1-6).
postgresql is already the newest version (9...
Andres Rodriguez (andreserl) wrote : | # |
ERROR: provisioningser
-------
testtools.
Traceback (most recent call last):
provisioningser
}}}
twisted-log: {{{
2016-05-10 18:30:20+0000 [-] Unhandled error in Deferred:
2016-05-10 18:30:20+0000 [-] Unhandled Error
Traceback (most recent call last):
Failure: provisioningser
}}}
-------
Ran 2369 tests in 44.324s
Preview Diff
1 | === modified file 'src/maasserver/models/node.py' | |||
2 | --- src/maasserver/models/node.py 2016-05-09 21:18:43 +0000 | |||
3 | +++ src/maasserver/models/node.py 2016-05-10 23:45:39 +0000 | |||
4 | @@ -162,6 +162,7 @@ | |||
5 | 162 | from provisioningserver.power import QUERY_POWER_TYPES | 162 | from provisioningserver.power import QUERY_POWER_TYPES |
6 | 163 | from provisioningserver.rpc.cluster import ( | 163 | from provisioningserver.rpc.cluster import ( |
7 | 164 | AddChassis, | 164 | AddChassis, |
8 | 165 | DisableAndShutoffRackd, | ||
9 | 165 | IsImportBootImagesRunning, | 166 | IsImportBootImagesRunning, |
10 | 166 | RefreshRackControllerInfo, | 167 | RefreshRackControllerInfo, |
11 | 167 | ) | 168 | ) |
12 | @@ -3658,15 +3659,6 @@ | |||
13 | 3658 | 3659 | ||
14 | 3659 | def delete(self): | 3660 | def delete(self): |
15 | 3660 | """Delete this rack controller.""" | 3661 | """Delete this rack controller.""" |
16 | 3661 | # Avoid circular dependency. | ||
17 | 3662 | from maasserver.models import RegionRackRPCConnection | ||
18 | 3663 | connections = RegionRackRPCConnection.objects.filter( | ||
19 | 3664 | rack_controller=self) | ||
20 | 3665 | if len(connections) != 0: | ||
21 | 3666 | raise ValidationError( | ||
22 | 3667 | "Unable to delete %s as it's currently connected to one or " | ||
23 | 3668 | "more regions." % self.hostname) | ||
24 | 3669 | |||
25 | 3670 | primary_vlans = VLAN.objects.filter(primary_rack=self) | 3662 | primary_vlans = VLAN.objects.filter(primary_rack=self) |
26 | 3671 | if len(primary_vlans) != 0: | 3663 | if len(primary_vlans) != 0: |
27 | 3672 | raise ValidationError( | 3664 | raise ValidationError( |
28 | @@ -3675,6 +3667,15 @@ | |||
29 | 3675 | (self.hostname, | 3667 | (self.hostname, |
30 | 3676 | ', '.join([str(vlan) for vlan in primary_vlans]))) | 3668 | ', '.join([str(vlan) for vlan in primary_vlans]))) |
31 | 3677 | 3669 | ||
32 | 3670 | try: | ||
33 | 3671 | client = getClientFor(self.system_id, timeout=1) | ||
34 | 3672 | call = client(DisableAndShutoffRackd) | ||
35 | 3673 | call.wait(30) | ||
36 | 3674 | except NoConnectionsAvailable: | ||
37 | 3675 | # NoConnectionsAvailable is always thrown. Either because the rack | ||
38 | 3676 | # is currently disconnected or rackd was killed | ||
39 | 3677 | pass | ||
40 | 3678 | |||
41 | 3678 | for vlan in VLAN.objects.filter(secondary_rack=self): | 3679 | for vlan in VLAN.objects.filter(secondary_rack=self): |
42 | 3679 | vlan.secondary_rack = None | 3680 | vlan.secondary_rack = None |
43 | 3680 | vlan.save() | 3681 | vlan.save() |
44 | 3681 | 3682 | ||
45 | === modified file 'src/maasserver/models/tests/test_node.py' | |||
46 | --- src/maasserver/models/tests/test_node.py 2016-05-09 23:54:19 +0000 | |||
47 | +++ src/maasserver/models/tests/test_node.py 2016-05-10 23:45:39 +0000 | |||
48 | @@ -137,10 +137,12 @@ | |||
49 | 137 | from provisioningserver.power.schema import JSON_POWER_TYPE_PARAMETERS | 137 | from provisioningserver.power.schema import JSON_POWER_TYPE_PARAMETERS |
50 | 138 | from provisioningserver.rpc.cluster import ( | 138 | from provisioningserver.rpc.cluster import ( |
51 | 139 | AddChassis, | 139 | AddChassis, |
52 | 140 | DisableAndShutoffRackd, | ||
53 | 140 | IsImportBootImagesRunning, | 141 | IsImportBootImagesRunning, |
54 | 141 | RefreshRackControllerInfo, | 142 | RefreshRackControllerInfo, |
55 | 142 | ) | 143 | ) |
56 | 143 | from provisioningserver.rpc.exceptions import ( | 144 | from provisioningserver.rpc.exceptions import ( |
57 | 145 | CannotDisableAndShutoffRackd, | ||
58 | 144 | NoConnectionsAvailable, | 146 | NoConnectionsAvailable, |
59 | 145 | UnknownPowerType, | 147 | UnknownPowerType, |
60 | 146 | ) | 148 | ) |
61 | @@ -6992,11 +6994,34 @@ | |||
62 | 6992 | rackcontroller.delete() | 6994 | rackcontroller.delete() |
63 | 6993 | self.assertIsNone(reload_object(rackcontroller)) | 6995 | self.assertIsNone(reload_object(rackcontroller)) |
64 | 6994 | 6996 | ||
70 | 6995 | def test_prevents_delete_when_connected(self): | 6997 | def test_disables_and_disconn_when_secondary_connected(self): |
71 | 6996 | rackcontroller = factory.make_RackController() | 6998 | rackcontroller = factory.make_RackController() |
72 | 6997 | mock_filter = self.patch(RegionRackRPCConnection.objects, 'filter') | 6999 | factory.make_VLAN(secondary_rack=rackcontroller) |
73 | 6998 | mock_filter.return_value = [rackcontroller] | 7000 | |
74 | 6999 | self.assertRaises(ValidationError, rackcontroller.delete) | 7001 | self.useFixture(RegionEventLoopFixture("rpc")) |
75 | 7002 | self.useFixture(RunningEventLoopFixture()) | ||
76 | 7003 | fixture = self.useFixture(MockLiveRegionToClusterRPCFixture()) | ||
77 | 7004 | protocol = fixture.makeCluster( | ||
78 | 7005 | rackcontroller, DisableAndShutoffRackd) | ||
79 | 7006 | protocol.DisableAndShutoffRackd.return_value = defer.succeed({}) | ||
80 | 7007 | |||
81 | 7008 | rackcontroller.delete() | ||
82 | 7009 | self.expectThat(protocol.DisableAndShutoffRackd, MockCalledOnce()) | ||
83 | 7010 | |||
84 | 7011 | def test_disables_and_disconn_when_secondary_connected_fails(self): | ||
85 | 7012 | rackcontroller = factory.make_RackController() | ||
86 | 7013 | factory.make_VLAN(secondary_rack=rackcontroller) | ||
87 | 7014 | |||
88 | 7015 | self.useFixture(RegionEventLoopFixture("rpc")) | ||
89 | 7016 | self.useFixture(RunningEventLoopFixture()) | ||
90 | 7017 | fixture = self.useFixture(MockLiveRegionToClusterRPCFixture()) | ||
91 | 7018 | protocol = fixture.makeCluster( | ||
92 | 7019 | rackcontroller, DisableAndShutoffRackd) | ||
93 | 7020 | protocol.DisableAndShutoffRackd.return_value = defer.fail( | ||
94 | 7021 | CannotDisableAndShutoffRackd()) | ||
95 | 7022 | |||
96 | 7023 | self.assertRaises(CannotDisableAndShutoffRackd, rackcontroller.delete) | ||
97 | 7024 | self.expectThat(protocol.DisableAndShutoffRackd, MockCalledOnce()) | ||
98 | 7000 | 7025 | ||
99 | 7001 | def test_prevents_delete_when_primary_rack(self): | 7026 | def test_prevents_delete_when_primary_rack(self): |
100 | 7002 | rackcontroller = factory.make_RackController() | 7027 | rackcontroller = factory.make_RackController() |
101 | 7003 | 7028 | ||
102 | === modified file 'src/provisioningserver/rpc/cluster.py' | |||
103 | --- src/provisioningserver/rpc/cluster.py 2016-04-29 22:12:18 +0000 | |||
104 | +++ src/provisioningserver/rpc/cluster.py 2016-05-10 23:45:39 +0000 | |||
105 | @@ -478,3 +478,15 @@ | |||
106 | 478 | (b"protocol", amp.Unicode(optional=True)), | 478 | (b"protocol", amp.Unicode(optional=True)), |
107 | 479 | ] | 479 | ] |
108 | 480 | errors = {} | 480 | errors = {} |
109 | 481 | |||
110 | 482 | |||
111 | 483 | class DisableAndShutoffRackd(amp.Command): | ||
112 | 484 | """Disable and shutdown the rackd service. | ||
113 | 485 | |||
114 | 486 | :since: 2.0 | ||
115 | 487 | """ | ||
116 | 488 | arguments = [] | ||
117 | 489 | errors = { | ||
118 | 490 | exceptions.CannotDisableAndShutoffRackd: ( | ||
119 | 491 | b"CannotDisableAndShutoffRackd"), | ||
120 | 492 | } | ||
121 | 481 | 493 | ||
122 | === modified file 'src/provisioningserver/rpc/clusterservice.py' | |||
123 | --- src/provisioningserver/rpc/clusterservice.py 2016-04-29 22:12:18 +0000 | |||
124 | +++ src/provisioningserver/rpc/clusterservice.py 2016-05-10 23:45:39 +0000 | |||
125 | @@ -72,6 +72,10 @@ | |||
126 | 72 | get_maas_id, | 72 | get_maas_id, |
127 | 73 | set_maas_id, | 73 | set_maas_id, |
128 | 74 | ) | 74 | ) |
129 | 75 | from provisioningserver.utils.shell import ( | ||
130 | 76 | call_and_check, | ||
131 | 77 | ExternalProcessError, | ||
132 | 78 | ) | ||
133 | 75 | from provisioningserver.utils.twisted import ( | 79 | from provisioningserver.utils.twisted import ( |
134 | 76 | DeferredValue, | 80 | DeferredValue, |
135 | 77 | synchronous, | 81 | synchronous, |
136 | @@ -460,6 +464,26 @@ | |||
137 | 460 | maaslog.error(message) | 464 | maaslog.error(message) |
138 | 461 | return {} | 465 | return {} |
139 | 462 | 466 | ||
140 | 467 | @cluster.DisableAndShutoffRackd.responder | ||
141 | 468 | def disable_and_shutoff_rackd(self): | ||
142 | 469 | """DisableAndShutoffRackd() | ||
143 | 470 | |||
144 | 471 | Implementation of | ||
145 | 472 | :py:class:`~provisioningserver.rpc.cluster.DisableAndShutoffRackd`. | ||
146 | 473 | """ | ||
147 | 474 | maaslog.info("Rack deleted, disabling rackd service") | ||
148 | 475 | try: | ||
149 | 476 | # We can't use the --now flag as if the maas-rackd service is on | ||
150 | 477 | # but not enabled the service won't be stopped | ||
151 | 478 | call_and_check( | ||
152 | 479 | ['sudo', 'systemctl', 'disable', 'maas-rackd']) | ||
153 | 480 | call_and_check( | ||
154 | 481 | ['sudo', 'systemctl', 'stop', 'maas-rackd']) | ||
155 | 482 | except ExternalProcessError as e: | ||
156 | 483 | maaslog.error("Unable to disable and stop maas-rackd service") | ||
157 | 484 | raise exceptions.CannotDisableAndShutoffRackd(e.output_as_unicode) | ||
158 | 485 | return {} | ||
159 | 486 | |||
160 | 463 | 487 | ||
161 | 464 | @implementer(IConnection) | 488 | @implementer(IConnection) |
162 | 465 | class ClusterClient(Cluster): | 489 | class ClusterClient(Cluster): |
163 | 466 | 490 | ||
164 | === modified file 'src/provisioningserver/rpc/exceptions.py' | |||
165 | --- src/provisioningserver/rpc/exceptions.py 2016-04-11 16:23:26 +0000 | |||
166 | +++ src/provisioningserver/rpc/exceptions.py 2016-05-10 23:45:39 +0000 | |||
167 | @@ -7,6 +7,7 @@ | |||
168 | 7 | "AuthenticationFailed", | 7 | "AuthenticationFailed", |
169 | 8 | "CannotConfigureDHCP", | 8 | "CannotConfigureDHCP", |
170 | 9 | "CannotCreateHostMap", | 9 | "CannotCreateHostMap", |
171 | 10 | "CannotDisableAndShutoffRackd", | ||
172 | 10 | "CannotModifyHostMap", | 11 | "CannotModifyHostMap", |
173 | 11 | "CannotRegisterCluster", | 12 | "CannotRegisterCluster", |
174 | 12 | "CannotRemoveHostMap", | 13 | "CannotRemoveHostMap", |
175 | @@ -146,3 +147,7 @@ | |||
176 | 146 | 147 | ||
177 | 147 | class BootConfigNoResponse(Exception): | 148 | class BootConfigNoResponse(Exception): |
178 | 148 | """The region gave no response for the boot configuration.""" | 149 | """The region gave no response for the boot configuration.""" |
179 | 150 | |||
180 | 151 | |||
181 | 152 | class CannotDisableAndShutoffRackd(Exception): | ||
182 | 153 | """Rackd cannot be disabled and shutoff.""" | ||
183 | 149 | 154 | ||
184 | === modified file 'src/provisioningserver/rpc/tests/test_clusterservice.py' | |||
185 | --- src/provisioningserver/rpc/tests/test_clusterservice.py 2016-05-10 11:02:10 +0000 | |||
186 | +++ src/provisioningserver/rpc/tests/test_clusterservice.py 2016-05-10 23:45:39 +0000 | |||
187 | @@ -2392,3 +2392,28 @@ | |||
188 | 2392 | 'hostname': factory.make_hostname(), | 2392 | 'hostname': factory.make_hostname(), |
189 | 2393 | }) | 2393 | }) |
190 | 2394 | self.assertEquals({}, response.result) | 2394 | self.assertEquals({}, response.result) |
191 | 2395 | |||
192 | 2396 | |||
193 | 2397 | class TestClusterProtocol_DisableAndShutoffRackd(MAASTestCase): | ||
194 | 2398 | |||
195 | 2399 | def test__is_registered(self): | ||
196 | 2400 | protocol = Cluster() | ||
197 | 2401 | responder = protocol.locateResponder( | ||
198 | 2402 | cluster.DisableAndShutoffRackd.commandName) | ||
199 | 2403 | self.assertIsNotNone(responder) | ||
200 | 2404 | |||
201 | 2405 | def test_issues_restart(self): | ||
202 | 2406 | mock_call_and_check = self.patch(clusterservice, 'call_and_check') | ||
203 | 2407 | response = call_responder( | ||
204 | 2408 | Cluster(), cluster.DisableAndShutoffRackd, {}) | ||
205 | 2409 | self.assertEquals({}, response.result) | ||
206 | 2410 | self.assertEquals(2, mock_call_and_check.call_count) | ||
207 | 2411 | |||
208 | 2412 | @inlineCallbacks | ||
209 | 2413 | def test_raises_error_on_failure(self): | ||
210 | 2414 | mock_call_and_check = self.patch(clusterservice, 'call_and_check') | ||
211 | 2415 | mock_call_and_check.side_effect = ExternalProcessError( | ||
212 | 2416 | 1, 'systemctl', 'failure') | ||
213 | 2417 | with ExpectedException(exceptions.CannotDisableAndShutoffRackd): | ||
214 | 2418 | yield call_responder( | ||
215 | 2419 | Cluster(), cluster.DisableAndShutoffRackd, {}) |
This looks really good. Nice work.