1
# Copyright 2012 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Tests for node actions."""
6
from __future__ import (
15
from urlparse import urlparse
17
from django.core.urlresolvers import reverse
18
from maasserver.enum import (
21
NODE_STATUS_CHOICES_DICT,
23
from maasserver.exceptions import Redirect
24
from maasserver.node_action import (
32
from maasserver.testing.factory import factory
33
from maasserver.testing.testcase import TestCase
34
from provisioningserver.enum import POWER_TYPE
37
ALL_STATUSES = NODE_STATUS_CHOICES_DICT.keys()
40
class FakeNodeAction(NodeAction):
41
display = "Action label"
42
actionable_statuses = ALL_STATUSES
43
permission = NODE_PERMISSION.VIEW
45
# For testing: an inhibition for inhibit() to return.
46
fake_inhibition = None
49
return self.fake_inhibition
55
class TestNodeAction(TestCase):
57
def test_compile_node_actions_returns_available_actions(self):
59
class MyAction(FakeNodeAction):
60
display = factory.getRandomString()
62
actions = compile_node_actions(
63
factory.make_node(), factory.make_admin(), classes=[MyAction])
64
self.assertEqual([MyAction.display], actions.keys())
66
def test_compile_node_actions_checks_node_status(self):
68
class MyAction(FakeNodeAction):
69
actionable_statuses = (NODE_STATUS.READY, )
71
node = factory.make_node(status=NODE_STATUS.DECLARED)
72
actions = compile_node_actions(
73
node, factory.make_admin(), classes=[MyAction])
74
self.assertEqual({}, actions)
76
def test_compile_node_actions_checks_permission(self):
78
class MyAction(FakeNodeAction):
79
permission = NODE_PERMISSION.EDIT
81
node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
82
actions = compile_node_actions(
83
node, factory.make_user(), classes=[MyAction])
84
self.assertEqual({}, actions)
86
def test_compile_node_actions_includes_inhibited_actions(self):
88
class MyAction(FakeNodeAction):
89
fake_inhibition = factory.getRandomString()
91
actions = compile_node_actions(
92
factory.make_node(), factory.make_admin(), classes=[MyAction])
93
self.assertEqual([MyAction.display], actions.keys())
95
def test_compile_node_actions_maps_display_names(self):
97
class Action1(FakeNodeAction):
98
display = factory.getRandomString()
100
class Action2(FakeNodeAction):
101
display = factory.getRandomString()
103
actions = compile_node_actions(
104
factory.make_node(), factory.make_admin(),
105
classes=[Action1, Action2])
106
for label, action in actions.items():
107
self.assertEqual(label, action.display)
109
def test_compile_node_actions_maintains_order(self):
110
labels = [factory.getRandomString() for counter in range(4)]
112
type(b"Action%d" % counter, (FakeNodeAction,), {'display': label})
113
for counter, label in enumerate(labels)]
114
actions = compile_node_actions(
115
factory.make_node(), factory.make_admin(), classes=classes)
116
self.assertSequenceEqual(labels, actions.keys())
117
self.assertSequenceEqual(
118
labels, [action.display for action in actions.values()])
120
def test_is_permitted_allows_if_user_has_permission(self):
122
class MyAction(FakeNodeAction):
123
permission = NODE_PERMISSION.EDIT
125
node = factory.make_node(
126
status=NODE_STATUS.ALLOCATED, owner=factory.make_user())
127
self.assertTrue(MyAction(node, node.owner).is_permitted())
129
def test_is_permitted_disallows_if_user_lacks_permission(self):
131
class MyAction(FakeNodeAction):
132
permission = NODE_PERMISSION.EDIT
134
node = factory.make_node(
135
status=NODE_STATUS.ALLOCATED, owner=factory.make_user())
136
self.assertFalse(MyAction(node, factory.make_user()).is_permitted())
138
def test_inhibition_wraps_inhibit(self):
139
inhibition = factory.getRandomString()
140
action = FakeNodeAction(factory.make_node(), factory.make_user())
141
action.fake_inhibition = inhibition
142
self.assertEqual(inhibition, action.inhibition)
144
def test_inhibition_caches_inhibition(self):
145
# The inhibition property will call inhibit() only once. We can
146
# prove this by changing the string inhibit() returns; it won't
147
# affect the value of the property.
148
inhibition = factory.getRandomString()
149
action = FakeNodeAction(factory.make_node(), factory.make_user())
150
action.fake_inhibition = inhibition
151
self.assertEqual(inhibition, action.inhibition)
152
action.fake_inhibition = factory.getRandomString()
153
self.assertEqual(inhibition, action.inhibition)
155
def test_inhibition_caches_None(self):
156
# An inhibition of None is also faithfully cached. In other
157
# words, it doesn't get mistaken for an uninitialized cache or
159
action = FakeNodeAction(factory.make_node(), factory.make_user())
160
action.fake_inhibition = None
161
self.assertIsNone(action.inhibition)
162
action.fake_inhibition = factory.getRandomString()
163
self.assertIsNone(action.inhibition)
165
def test_Delete_inhibit_when_node_is_allocated(self):
166
node = factory.make_node(status=NODE_STATUS.ALLOCATED)
167
action = Delete(node, factory.make_admin())
168
inhibition = action.inhibit()
170
"You cannot delete this node because it's in use.", inhibition)
172
def test_Delete_does_not_inhibit_otherwise(self):
173
node = factory.make_node(status=NODE_STATUS.FAILED_TESTS)
174
action = Delete(node, factory.make_admin())
175
inhibition = action.inhibit()
176
self.assertIsNone(inhibition)
178
def test_Delete_redirects_to_node_delete_view(self):
179
node = factory.make_node()
180
action = Delete(node, factory.make_admin())
183
except Redirect as e:
186
reverse('node-delete', args=[node.system_id]),
187
urlparse(unicode(e)).path)
189
def test_AcceptAndCommission_starts_commissioning(self):
190
node = factory.make_node(
191
mac=True, status=NODE_STATUS.DECLARED,
192
power_type=POWER_TYPE.WAKE_ON_LAN)
193
action = AcceptAndCommission(node, factory.make_admin())
195
self.assertEqual(NODE_STATUS.COMMISSIONING, node.status)
197
'provisioningserver.tasks.power_on',
198
self.celery.tasks[0]['task'].name)
200
def test_RetryCommissioning_starts_commissioning(self):
201
node = factory.make_node(
202
mac=True, status=NODE_STATUS.FAILED_TESTS,
203
power_type=POWER_TYPE.WAKE_ON_LAN)
204
action = RetryCommissioning(node, factory.make_admin())
206
self.assertEqual(NODE_STATUS.COMMISSIONING, node.status)
208
'provisioningserver.tasks.power_on',
209
self.celery.tasks[0]['task'].name)
211
def test_StartNode_inhibit_allows_user_with_SSH_key(self):
212
user_with_key = factory.make_user()
213
factory.make_sshkey(user_with_key)
215
StartNode(factory.make_node(), user_with_key).inhibit())
217
def test_StartNode_inhibit_disallows_user_without_SSH_key(self):
218
user_without_key = factory.make_user()
219
action = StartNode(factory.make_node(), user_without_key)
220
inhibition = action.inhibit()
221
self.assertIsNotNone(inhibition)
222
self.assertIn("SSH key", inhibition)
224
def test_StartNode_acquires_and_starts_node(self):
225
node = factory.make_node(
226
mac=True, status=NODE_STATUS.READY,
227
power_type=POWER_TYPE.WAKE_ON_LAN)
228
user = factory.make_user()
229
StartNode(node, user).execute()
230
self.assertEqual(NODE_STATUS.ALLOCATED, node.status)
231
self.assertEqual(user, node.owner)
233
'provisioningserver.tasks.power_on',
234
self.celery.tasks[0]['task'].name)