55
class TestTopLevelFunctions(base.TestCase):
58
super(TestTopLevelFunctions, self).setUp()
59
self.valid_name = 'my-host'
60
self.valid_uuid = uuidutils.generate_uuid()
61
self.invalid_name = 'Mr Plow'
62
self.invalid_uuid = '636-555-3226-'
63
self.node = post_get_test_node()
65
def test_is_valid_name(self):
66
self.assertTrue(api_node.is_valid_name(self.valid_name))
67
self.assertFalse(api_node.is_valid_name(self.invalid_name))
68
self.assertFalse(api_node.is_valid_name(self.valid_uuid))
69
self.assertFalse(api_node.is_valid_name(self.invalid_uuid))
71
@mock.patch.object(pecan, 'request')
72
@mock.patch.object(api_node, 'allow_logical_names')
73
@mock.patch.object(objects.Node, 'get_by_uuid')
74
@mock.patch.object(objects.Node, 'get_by_name')
75
def test__get_rpc_node_expect_uuid(self, mock_gbn, mock_gbu, mock_aln,
77
mock_aln.return_value = True
78
self.node['uuid'] = self.valid_uuid
79
mock_gbu.return_value = self.node
80
self.assertEqual(self.node, api_node._get_rpc_node(self.valid_uuid))
81
self.assertEqual(1, mock_gbu.call_count)
82
self.assertEqual(0, mock_gbn.call_count)
84
@mock.patch.object(pecan, 'request')
85
@mock.patch.object(api_v1.node, 'allow_logical_names')
86
@mock.patch.object(objects.Node, 'get_by_uuid')
87
@mock.patch.object(objects.Node, 'get_by_name')
88
def test__get_rpc_node_expect_name(self, mock_gbn, mock_gbu, mock_aln,
90
mock_aln.return_value = True
91
self.node['name'] = self.valid_name
92
mock_gbn.return_value = self.node
93
self.assertEqual(self.node, api_node._get_rpc_node(self.valid_name))
94
self.assertEqual(0, mock_gbu.call_count)
95
self.assertEqual(1, mock_gbn.call_count)
97
@mock.patch.object(pecan, 'request')
98
@mock.patch.object(api_v1.node, 'allow_logical_names')
99
@mock.patch.object(objects.Node, 'get_by_uuid')
100
@mock.patch.object(objects.Node, 'get_by_name')
101
def test__get_rpc_node_invalid_name(self, mock_gbn, mock_gbu,
103
mock_aln.return_value = True
104
self.assertRaises(exception.InvalidUuidOrName,
105
api_node._get_rpc_node,
108
@mock.patch.object(pecan, 'request')
109
@mock.patch.object(api_v1.node, 'allow_logical_names')
110
@mock.patch.object(objects.Node, 'get_by_uuid')
111
@mock.patch.object(objects.Node, 'get_by_name')
112
def test__get_rpc_node_invalid_uuid(self, mock_gbn, mock_gbu,
114
mock_aln.return_value = True
115
self.assertRaises(exception.InvalidUuidOrName,
116
api_node._get_rpc_node,
54
120
class TestNodeObject(base.TestCase):
56
122
def test_node_init(self):
75
141
unassociated_nodes = []
76
142
for id in range(3):
77
143
node = obj_utils.create_test_node(self.context,
78
uuid=utils.generate_uuid())
144
uuid=uuidutils.generate_uuid())
79
145
unassociated_nodes.append(node.uuid)
81
147
# created some associated nodes
82
148
associated_nodes = []
83
149
for id in range(4):
84
150
node = obj_utils.create_test_node(
85
self.context, uuid=utils.generate_uuid(),
86
instance_uuid=utils.generate_uuid())
87
associated_nodes.append(node['uuid'])
151
self.context, uuid=uuidutils.generate_uuid(),
152
instance_uuid=uuidutils.generate_uuid())
153
associated_nodes.append(node.uuid)
88
154
return {'associated': associated_nodes,
89
155
'unassociated': unassociated_nodes}
101
167
self.assertIn('power_state', data['nodes'][0])
102
168
self.assertIn('provision_state', data['nodes'][0])
103
169
self.assertIn('uuid', data['nodes'][0])
104
self.assertEqual(node['uuid'], data['nodes'][0]["uuid"])
170
self.assertEqual(node.uuid, data['nodes'][0]["uuid"])
105
171
self.assertNotIn('driver', data['nodes'][0])
106
172
self.assertNotIn('driver_info', data['nodes'][0])
107
173
self.assertNotIn('driver_internal_info', data['nodes'][0])
132
198
self.assertIn('chassis_uuid', data)
133
199
self.assertIn('reservation', data)
134
200
self.assertIn('maintenance_reason', data)
201
self.assertIn('name', data)
202
self.assertIn('inspection_finished_at', data)
203
self.assertIn('inspection_started_at', data)
135
204
# never expose the chassis_id
136
205
self.assertNotIn('chassis_id', data)
138
207
def test_detail(self):
139
208
node = obj_utils.create_test_node(self.context)
140
data = self.get_json('/nodes/detail')
141
self.assertEqual(node['uuid'], data['nodes'][0]["uuid"])
209
data = self.get_json('/nodes/detail',
210
headers={api_base.Version.string: str(api_v1.MAX_VER)})
211
self.assertEqual(node.uuid, data['nodes'][0]["uuid"])
212
self.assertIn('name', data['nodes'][0])
142
213
self.assertIn('driver', data['nodes'][0])
143
214
self.assertIn('driver_info', data['nodes'][0])
144
215
self.assertIn('extra', data['nodes'][0])
150
221
self.assertIn('target_power_state', data['nodes'][0])
151
222
self.assertIn('target_provision_state', data['nodes'][0])
152
223
self.assertIn('provision_updated_at', data['nodes'][0])
224
self.assertIn('inspection_finished_at', data['nodes'][0])
225
self.assertIn('inspection_started_at', data['nodes'][0])
153
226
# never expose the chassis_id
154
227
self.assertNotIn('chassis_id', data['nodes'][0])
156
229
def test_detail_against_single(self):
157
230
node = obj_utils.create_test_node(self.context)
158
response = self.get_json('/nodes/%s/detail' % node['uuid'],
231
response = self.get_json('/nodes/%s/detail' % node.uuid,
159
232
expect_errors=True)
160
233
self.assertEqual(404, response.status_int)
163
236
node = obj_utils.create_test_node(self.context,
164
237
provision_state=states.AVAILABLE)
166
data = self.get_json('/nodes/%s' % node['uuid'],
239
data = self.get_json('/nodes/%s' % node.uuid,
167
240
headers={api_base.Version.string: str(api_v1.MIN_VER)})
168
241
self.assertEqual(states.NOSTATE, data['provision_state'])
170
data = self.get_json('/nodes/%s' % node['uuid'],
243
data = self.get_json('/nodes/%s' % node.uuid,
171
244
headers={api_base.Version.string: "1.2"})
172
245
self.assertEqual(states.AVAILABLE, data['provision_state'])
247
def test_hide_fields_in_newer_versions(self):
248
some_time = datetime.datetime(2015, 3, 18, 19, 20)
249
node = obj_utils.create_test_node(self.context,
250
inspection_started_at=some_time)
251
data = self.get_json('/nodes/%s' % node.uuid,
252
headers={api_base.Version.string: str(api_v1.MIN_VER)})
253
self.assertNotIn('inspection_finished_at', data)
254
self.assertNotIn('inspection_started_at', data)
256
data = self.get_json('/nodes/%s' % node.uuid,
257
headers={api_base.Version.string: "1.6"})
258
started = timeutils.parse_isotime(
259
data['inspection_started_at']).replace(tzinfo=None)
260
self.assertEqual(some_time, started)
261
self.assertEqual(None, data['inspection_finished_at'])
174
263
def test_hide_driver_internal_info(self):
175
264
node = obj_utils.create_test_node(self.context,
176
265
driver_internal_info={"foo": "bar"})
177
data = self.get_json('/nodes/%s' % node['uuid'],
266
data = self.get_json('/nodes/%s' % node.uuid,
178
267
headers={api_base.Version.string: str(api_v1.MIN_VER)})
179
268
self.assertNotIn('driver_internal_info', data)
181
data = self.get_json('/nodes/%s' % node['uuid'],
270
data = self.get_json('/nodes/%s' % node.uuid,
182
271
headers={api_base.Version.string: "1.3"})
183
272
self.assertEqual({"foo": "bar"}, data['driver_internal_info'])
274
def test_unset_logical_names(self):
275
node = obj_utils.create_test_node(self.context,
277
data = self.get_json('/nodes/%s' % node.uuid,
278
headers={api_base.Version.string: "1.4"})
279
self.assertNotIn('name', data)
281
data = self.get_json('/nodes/%s' % node.uuid,
282
headers={api_base.Version.string: "1.5"})
283
self.assertEqual('fish', data['name'])
185
285
def test_many(self):
187
287
for id in range(5):
188
288
node = obj_utils.create_test_node(self.context,
189
uuid=utils.generate_uuid())
289
uuid=uuidutils.generate_uuid())
190
290
nodes.append(node.uuid)
191
291
data = self.get_json('/nodes')
192
292
self.assertEqual(len(nodes), len(data['nodes']))
194
294
uuids = [n['uuid'] for n in data['nodes']]
195
295
self.assertEqual(sorted(nodes), sorted(uuids))
297
def test_many_have_names(self):
301
name = 'node-%s' % id
302
node = obj_utils.create_test_node(self.context,
303
uuid=uuidutils.generate_uuid(),
305
nodes.append(node.uuid)
306
node_names.append(name)
307
data = self.get_json('/nodes',
308
headers={api_base.Version.string: "1.5"})
309
names = [n['name'] for n in data['nodes']]
310
self.assertEqual(len(nodes), len(data['nodes']))
311
self.assertEqual(sorted(node_names), sorted(names))
197
313
def test_links(self):
198
uuid = utils.generate_uuid()
314
uuid = uuidutils.generate_uuid()
199
315
obj_utils.create_test_node(self.context, uuid=uuid)
200
316
data = self.get_json('/nodes/%s' % uuid)
201
317
self.assertIn('links', data.keys())
289
405
self.assertEqual(fake_error, data['last_error'])
290
406
self.assertFalse(data['console_enabled'])
408
@mock.patch.object(timeutils, 'utcnow')
409
def test_node_states_by_name(self, mock_utcnow):
410
fake_state = 'fake-state'
411
fake_error = 'fake-error'
412
test_time = datetime.datetime(1971, 3, 9, 0, 0)
413
mock_utcnow.return_value = test_time
414
node = obj_utils.create_test_node(self.context,
416
power_state=fake_state,
417
target_power_state=fake_state,
418
provision_state=fake_state,
419
target_provision_state=fake_state,
420
provision_updated_at=test_time,
421
last_error=fake_error)
422
data = self.get_json('/nodes/%s/states' % node.name,
423
headers={api_base.Version.string: "1.5"})
424
self.assertEqual(fake_state, data['power_state'])
425
self.assertEqual(fake_state, data['target_power_state'])
426
self.assertEqual(fake_state, data['provision_state'])
427
self.assertEqual(fake_state, data['target_provision_state'])
428
prov_up_at = timeutils.parse_isotime(
429
data['provision_updated_at']).replace(tzinfo=None)
430
self.assertEqual(test_time, prov_up_at)
431
self.assertEqual(fake_error, data['last_error'])
432
self.assertFalse(data['console_enabled'])
292
434
def test_node_by_instance_uuid(self):
293
node = obj_utils.create_test_node(self.context,
294
uuid=utils.generate_uuid(),
295
instance_uuid=utils.generate_uuid())
435
node = obj_utils.create_test_node(
437
uuid=uuidutils.generate_uuid(),
438
instance_uuid=uuidutils.generate_uuid())
296
439
instance_uuid = node.instance_uuid
298
data = self.get_json('/nodes?instance_uuid=%s' % instance_uuid)
441
data = self.get_json('/nodes?instance_uuid=%s' % instance_uuid,
442
headers={api_base.Version.string: "1.5"})
300
444
self.assertThat(data['nodes'], HasLength(1))
301
445
self.assertEqual(node['instance_uuid'],
302
446
data['nodes'][0]["instance_uuid"])
304
448
def test_node_by_instance_uuid_wrong_uuid(self):
305
obj_utils.create_test_node(self.context, uuid=utils.generate_uuid(),
306
instance_uuid=utils.generate_uuid())
307
wrong_uuid = utils.generate_uuid()
449
obj_utils.create_test_node(
450
self.context, uuid=uuidutils.generate_uuid(),
451
instance_uuid=uuidutils.generate_uuid())
452
wrong_uuid = uuidutils.generate_uuid()
309
454
data = self.get_json('/nodes?instance_uuid=%s' % wrong_uuid)
382
527
self.assertIn('associated=True', data['next'])
384
529
def test_detail_with_instance_uuid(self):
385
node = obj_utils.create_test_node(self.context,
386
uuid=utils.generate_uuid(),
387
instance_uuid=utils.generate_uuid())
530
node = obj_utils.create_test_node(
532
uuid=uuidutils.generate_uuid(),
533
instance_uuid=uuidutils.generate_uuid())
388
534
instance_uuid = node.instance_uuid
390
536
data = self.get_json('/nodes/detail?instance_uuid=%s' % instance_uuid)
452
599
self.assertEqual(expected_data, data)
453
600
mock_gci.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
602
@mock.patch.object(rpcapi.ConductorAPI, 'get_console_information')
603
def test_get_console_information_by_name(self, mock_gci):
604
node = obj_utils.create_test_node(self.context, name='spam')
605
expected_console_info = {'test': 'test-data'}
606
expected_data = {'console_enabled': True,
607
'console_info': expected_console_info}
608
mock_gci.return_value = expected_console_info
609
data = self.get_json('/nodes/%s/states/console' % node.name,
610
headers={api_base.Version.string: "1.5"})
611
self.assertEqual(expected_data, data)
612
mock_gci.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
455
614
def test_get_console_information_console_disabled(self):
456
615
node = obj_utils.create_test_node(self.context)
457
616
expected_data = {'console_enabled': False,
485
644
mock_gbd.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
487
646
@mock.patch.object(rpcapi.ConductorAPI, 'get_boot_device')
647
def test_get_boot_device_by_name(self, mock_gbd):
648
node = obj_utils.create_test_node(self.context, name='spam')
649
expected_data = {'boot_device': boot_devices.PXE, 'persistent': True}
650
mock_gbd.return_value = expected_data
651
data = self.get_json('/nodes/%s/management/boot_device' % node.name,
652
headers={api_base.Version.string: "1.5"})
653
self.assertEqual(expected_data, data)
654
mock_gbd.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
656
@mock.patch.object(rpcapi.ConductorAPI, 'get_boot_device')
488
657
def test_get_boot_device_iface_not_supported(self, mock_gbd):
489
658
node = obj_utils.create_test_node(self.context)
490
659
mock_gbd.side_effect = exception.UnsupportedDriverExtension(
506
675
mock_gsbd.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
508
677
@mock.patch.object(rpcapi.ConductorAPI, 'get_supported_boot_devices')
678
def test_get_supported_boot_devices_by_name(self, mock_gsbd):
679
mock_gsbd.return_value = [boot_devices.PXE]
680
node = obj_utils.create_test_node(self.context, name='spam')
681
data = self.get_json(
682
'/nodes/%s/management/boot_device/supported' % node.name,
683
headers={api_base.Version.string: "1.5"})
684
expected_data = {'supported_boot_devices': [boot_devices.PXE]}
685
self.assertEqual(expected_data, data)
686
mock_gsbd.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
688
@mock.patch.object(rpcapi.ConductorAPI, 'get_supported_boot_devices')
509
689
def test_get_supported_boot_devices_iface_not_supported(self, mock_gsbd):
510
690
node = obj_utils.create_test_node(self.context)
511
691
mock_gsbd.side_effect = exception.UnsupportedDriverExtension(
516
696
self.assertTrue(ret.json['error_message'])
517
697
mock_gsbd.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
699
@mock.patch.object(rpcapi.ConductorAPI, 'validate_driver_interfaces')
700
def test_validate_by_uuid_using_deprecated_interface(self, mock_vdi):
701
# Note(mrda): The 'node_uuid' interface is deprecated in favour
702
# of the 'node' interface
703
node = obj_utils.create_test_node(self.context)
704
self.get_json('/nodes/validate?node_uuid=%s' % node.uuid)
705
mock_vdi.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
707
@mock.patch.object(rpcapi.ConductorAPI, 'validate_driver_interfaces')
708
def test_validate_by_uuid(self, mock_vdi):
709
node = obj_utils.create_test_node(self.context)
710
self.get_json('/nodes/validate?node=%s' % node.uuid,
711
headers={api_base.Version.string: "1.5"})
712
mock_vdi.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
714
@mock.patch.object(rpcapi.ConductorAPI, 'validate_driver_interfaces')
715
def test_validate_by_name_unsupported(self, mock_vdi):
716
node = obj_utils.create_test_node(self.context, name='spam')
717
ret = self.get_json('/nodes/validate?node=%s' % node.name,
719
self.assertEqual(406, ret.status_code)
720
self.assertFalse(mock_vdi.called)
722
@mock.patch.object(rpcapi.ConductorAPI, 'validate_driver_interfaces')
723
def test_validate_by_name(self, mock_vdi):
724
node = obj_utils.create_test_node(self.context, name='spam')
725
self.get_json('/nodes/validate?node=%s' % node.name,
726
headers={api_base.Version.string: "1.5"})
727
# note that this should be node.uuid here as we get that from the
728
# rpc_node lookup and pass that downwards
729
mock_vdi.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
520
732
class TestPatch(test_api_base.FunctionalTest):
523
735
super(TestPatch, self).setUp()
524
736
self.chassis = obj_utils.create_test_chassis(self.context)
525
self.node = obj_utils.create_test_node(self.context)
737
self.node = obj_utils.create_test_node(self.context, name='node-57')
738
self.node_no_name = obj_utils.create_test_node(self.context,
739
uuid='deadbeef-0000-1111-2222-333333333333')
526
740
p = mock.patch.object(rpcapi.ConductorAPI, 'get_topic_for')
527
741
self.mock_gtf = p.start()
528
742
self.mock_gtf.return_value = 'test-topic'
551
765
self.mock_update_node.assert_called_once_with(
552
766
mock.ANY, mock.ANY, 'test-topic')
768
def test_update_by_name_unsupported(self):
769
self.mock_update_node.return_value = self.node
773
.updated_at) = "2013-12-03T06:20:41.184720+00:00"
774
response = self.patch_json(
775
'/nodes/%s' % self.node.name,
776
[{'path': '/instance_uuid',
777
'value': 'aaaaaaaa-1111-bbbb-2222-cccccccccccc',
780
self.assertEqual(400, response.status_code)
781
self.assertFalse(self.mock_update_node.called)
783
def test_update_ok_by_name(self):
784
self.mock_update_node.return_value = self.node
788
.updated_at) = "2013-12-03T06:20:41.184720+00:00"
789
response = self.patch_json(
790
'/nodes/%s' % self.node.name,
791
[{'path': '/instance_uuid',
792
'value': 'aaaaaaaa-1111-bbbb-2222-cccccccccccc',
794
headers={api_base.Version.string: "1.5"})
795
self.assertEqual('application/json', response.content_type)
796
self.assertEqual(200, response.status_code)
797
self.assertEqual(self.mock_update_node.return_value.updated_at,
798
timeutils.parse_isotime(response.json['updated_at']))
799
self.mock_update_node.assert_called_once_with(
800
mock.ANY, mock.ANY, 'test-topic')
554
802
def test_update_state(self):
555
response = self.patch_json('/nodes/%s' % self.node['uuid'],
803
response = self.patch_json('/nodes/%s' % self.node.uuid,
556
804
[{'power_state': 'new state'}],
557
805
expect_errors=True)
558
806
self.assertEqual('application/json', response.content_type)
593
841
def test_update_fails_bad_state(self):
594
842
fake_err = 'Fake Power State'
595
843
self.mock_update_node.side_effect = exception.NodeInWrongPowerState(
596
node=self.node['uuid'], pstate=fake_err)
844
node=self.node.uuid, pstate=fake_err)
598
response = self.patch_json('/nodes/%s' % self.node['uuid'],
846
response = self.patch_json('/nodes/%s' % self.node.uuid,
599
847
[{'path': '/instance_uuid',
600
848
'value': 'aaaaaaaa-1111-bbbb-2222-cccccccccccc',
601
849
'op': 'replace'}],
684
932
mock.ANY, mock.ANY, 'test-topic')
686
934
def test_patch_ports_subresource(self):
687
response = self.patch_json('/nodes/%s/ports' % self.node['uuid'],
935
response = self.patch_json('/nodes/%s/ports' % self.node.uuid,
688
936
[{'path': '/extra/foo', 'value': 'bar',
689
937
'op': 'add'}], expect_errors=True)
690
938
self.assertEqual(403, response.status_int)
790
1038
self.mock_update_node.assert_called_once_with(
791
1039
mock.ANY, mock.ANY, 'test-topic')
1041
def test_replace_maintenance_by_name(self):
1042
self.mock_update_node.return_value = self.node
1044
response = self.patch_json(
1045
'/nodes/%s' % self.node.name,
1046
[{'path': '/maintenance', 'op': 'replace',
1048
headers={api_base.Version.string: "1.5"})
1049
self.assertEqual('application/json', response.content_type)
1050
self.assertEqual(200, response.status_code)
1052
self.mock_update_node.assert_called_once_with(
1053
mock.ANY, mock.ANY, 'test-topic')
793
1055
def test_replace_consoled_enabled(self):
794
response = self.patch_json('/nodes/%s' % self.node['uuid'],
1056
response = self.patch_json('/nodes/%s' % self.node.uuid,
795
1057
[{'path': '/console_enabled',
796
1058
'op': 'replace', 'value': True}],
797
1059
expect_errors=True)
809
1071
self.assertEqual(400, response.status_code)
810
1072
self.assertTrue(response.json['error_message'])
1074
def test_patch_add_name_ok(self):
1075
self.mock_update_node.return_value = self.node_no_name
1076
test_name = 'guido-van-rossum'
1077
response = self.patch_json('/nodes/%s' % self.node_no_name.uuid,
1080
'value': test_name}],
1081
headers={api_base.Version.string: "1.5"})
1082
self.assertEqual('application/json', response.content_type)
1083
self.assertEqual(200, response.status_code)
1085
def test_patch_add_name_invalid(self):
1086
self.mock_update_node.return_value = self.node_no_name
1087
test_name = 'I-AM-INVALID'
1088
response = self.patch_json('/nodes/%s' % self.node_no_name.uuid,
1091
'value': test_name}],
1092
headers={api_base.Version.string: "1.5"},
1094
self.assertEqual('application/json', response.content_type)
1095
self.assertEqual(400, response.status_code)
1096
self.assertTrue(response.json['error_message'])
1098
def test_patch_name_replace_ok(self):
1099
self.mock_update_node.return_value = self.node
1100
test_name = 'guido-van-rossum'
1101
response = self.patch_json('/nodes/%s' % self.node.uuid,
1104
'value': test_name}],
1105
headers={api_base.Version.string: "1.5"})
1106
self.assertEqual('application/json', response.content_type)
1107
self.assertEqual(200, response.status_code)
1109
def test_patch_add_replace_invalid(self):
1110
self.mock_update_node.return_value = self.node_no_name
1111
test_name = 'Guido Van Error'
1112
response = self.patch_json('/nodes/%s' % self.node.uuid,
1115
'value': test_name}],
1116
headers={api_base.Version.string: "1.5"},
1118
self.assertEqual('application/json', response.content_type)
1119
self.assertEqual(400, response.status_code)
1120
self.assertTrue(response.json['error_message'])
1122
def test_patch_duplicate_name(self):
1123
node = obj_utils.create_test_node(self.context,
1124
uuid=uuidutils.generate_uuid())
1125
test_name = "this-is-my-node"
1126
self.mock_update_node.side_effect = exception.DuplicateName(test_name)
1127
response = self.patch_json('/nodes/%s' % node.uuid,
1130
'value': test_name}],
1131
headers={api_base.Version.string: "1.5"},
1133
self.assertEqual('application/json', response.content_type)
1134
self.assertEqual(409, response.status_code)
1135
self.assertTrue(response.json['error_message'])
813
1138
class TestPost(test_api_base.FunctionalTest):
891
1216
self.assertEqual(expected_return_value, response.body)
892
1217
self.assertEqual(expected_status, response.status_code)
1219
def _test_vendor_passthru_ok_by_name(self, mock_vendor, return_value=None,
1221
expected_status = 202 if is_async else 200
1222
expected_return_value = json.dumps(return_value)
1224
node = obj_utils.create_test_node(self.context, name='node-109')
1225
info = {'foo': 'bar'}
1226
mock_vendor.return_value = (return_value, is_async)
1227
response = self.post_json('/nodes/%s/vendor_passthru/test' % node.name,
1229
headers={api_base.Version.string: "1.5"})
1230
mock_vendor.assert_called_once_with(
1231
mock.ANY, node.uuid, 'test', 'POST', info, 'test-topic')
1232
self.assertEqual(expected_return_value, response.body)
1233
self.assertEqual(expected_status, response.status_code)
894
1235
@mock.patch.object(rpcapi.ConductorAPI, 'vendor_passthru')
895
1236
def test_vendor_passthru_async(self, mock_vendor):
896
1237
self._test_vendor_passthru_ok(mock_vendor)
913
1254
self.assertEqual(return_value[0], response.json)
915
1256
@mock.patch.object(rpcapi.ConductorAPI, 'vendor_passthru')
1257
def test_vendor_passthru_by_name(self, mock_vendor):
1258
self._test_vendor_passthru_ok_by_name(mock_vendor)
1260
@mock.patch.object(rpcapi.ConductorAPI, 'vendor_passthru')
916
1261
def test_vendor_passthru_get(self, mocked_vendor_passthru):
917
1262
node = obj_utils.create_test_node(self.context)
918
1263
return_value = ('foo', 'sync')
1059
1404
self.delete('/nodes/%s' % node.uuid)
1060
1405
mock_dn.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
1407
@mock.patch.object(rpcapi.ConductorAPI, 'destroy_node')
1408
def test_delete_node_by_name_unsupported(self, mock_dn):
1409
node = obj_utils.create_test_node(self.context, name='foo')
1410
self.delete('/nodes/%s' % node.name,
1412
self.assertFalse(mock_dn.called)
1414
@mock.patch.object(rpcapi.ConductorAPI, 'destroy_node')
1415
def test_delete_node_by_name(self, mock_dn):
1416
node = obj_utils.create_test_node(self.context, name='foo')
1417
self.delete('/nodes/%s' % node.name,
1418
headers={api_base.Version.string: "1.5"})
1419
mock_dn.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
1062
1421
@mock.patch.object(objects.Node, 'get_by_uuid')
1063
1422
def test_delete_node_not_found(self, mock_gbu):
1064
1423
node = obj_utils.get_test_node(self.context)
1070
1429
self.assertTrue(response.json['error_message'])
1071
1430
mock_gbu.assert_called_once_with(mock.ANY, node.uuid)
1432
@mock.patch.object(objects.Node, 'get_by_name')
1433
def test_delete_node_not_found_by_name_unsupported(self, mock_gbn):
1434
node = obj_utils.get_test_node(self.context, name='foo')
1435
mock_gbn.side_effect = exception.NodeNotFound(node=node.name)
1437
response = self.delete('/nodes/%s' % node.name,
1439
self.assertEqual(400, response.status_int)
1440
self.assertFalse(mock_gbn.called)
1442
@mock.patch.object(objects.Node, 'get_by_name')
1443
def test_delete_node_not_found_by_name(self, mock_gbn):
1444
node = obj_utils.get_test_node(self.context, name='foo')
1445
mock_gbn.side_effect = exception.NodeNotFound(node=node.name)
1447
response = self.delete('/nodes/%s' % node.name,
1448
headers={api_base.Version.string: "1.5"},
1450
self.assertEqual(404, response.status_int)
1451
self.assertEqual('application/json', response.content_type)
1452
self.assertTrue(response.json['error_message'])
1453
mock_gbn.assert_called_once_with(mock.ANY, node.name)
1073
1455
def test_delete_ports_subresource(self):
1074
1456
node = obj_utils.create_test_node(self.context)
1075
1457
response = self.delete('/nodes/%s/ports' % node.uuid,
1103
1485
mock_update.assert_called_once_with(mock.ANY, mock.ANY,
1104
1486
topic='test-topic')
1488
@mock.patch.object(objects.Node, 'get_by_name')
1489
@mock.patch.object(rpcapi.ConductorAPI, 'update_node')
1490
def test_delete_node_maintenance_mode_by_name(self, mock_update,
1492
node = obj_utils.create_test_node(self.context, maintenance=True,
1493
maintenance_reason='blah',
1495
mock_get.return_value = node
1496
response = self.delete('/nodes/%s/maintenance' % node.name,
1497
headers={api_base.Version.string: "1.5"})
1498
self.assertEqual(202, response.status_int)
1499
self.assertEqual('', response.body)
1500
self.assertEqual(False, node.maintenance)
1501
self.assertEqual(None, node.maintenance_reason)
1502
mock_get.assert_called_once_with(mock.ANY, node.name)
1503
mock_update.assert_called_once_with(mock.ANY, mock.ANY,
1107
1507
class TestPut(test_api_base.FunctionalTest):
1140
1543
self.assertEqual(urlparse.urlparse(response.location).path,
1141
1544
expected_location)
1546
def test_power_state_by_name_unsupported(self):
1547
response = self.put_json('/nodes/%s/states/power' % self.node.name,
1548
{'target': states.POWER_ON},
1550
self.assertEqual(400, response.status_code)
1552
def test_power_state_by_name(self):
1553
response = self.put_json('/nodes/%s/states/power' % self.node.name,
1554
{'target': states.POWER_ON},
1555
headers={api_base.Version.string: "1.5"})
1556
self.assertEqual(202, response.status_code)
1557
self.assertEqual('', response.body)
1558
self.mock_cnps.assert_called_once_with(mock.ANY,
1562
# Check location header
1563
self.assertIsNotNone(response.location)
1564
expected_location = '/v1/nodes/%s/states' % self.node.name
1565
self.assertEqual(urlparse.urlparse(response.location).path,
1143
1568
def test_power_invalid_state_request(self):
1144
1569
ret = self.put_json('/nodes/%s/states/power' % self.node.uuid,
1145
1570
{'target': 'not-supported'}, expect_errors=True)
1163
1588
self.assertEqual(urlparse.urlparse(ret.location).path,
1164
1589
expected_location)
1591
def test_provision_by_name_unsupported(self):
1592
ret = self.put_json('/nodes/%s/states/provision' % self.node.name,
1593
{'target': states.ACTIVE},
1595
self.assertEqual(400, ret.status_code)
1597
def test_provision_by_name(self):
1598
ret = self.put_json('/nodes/%s/states/provision' % self.node.name,
1599
{'target': states.ACTIVE},
1600
headers={api_base.Version.string: "1.5"})
1601
self.assertEqual(202, ret.status_code)
1602
self.assertEqual('', ret.body)
1603
self.mock_dnd.assert_called_once_with(
1604
mock.ANY, self.node.uuid, False, None, 'test-topic')
1166
1606
def test_provision_with_deploy_configdrive(self):
1167
1607
ret = self.put_json('/nodes/%s/states/provision' % self.node.uuid,
1168
1608
{'target': states.ACTIVE, 'configdrive': 'foo'})
1278
1718
states.VERBS['provide'],
1721
def test_inspect_already_in_progress(self):
1723
node.provision_state = states.INSPECTING
1724
node.target_provision_state = states.MANAGEABLE
1725
node.reservation = 'fake-host'
1727
ret = self.put_json('/nodes/%s/states/provision' % node.uuid,
1728
{'target': states.MANAGEABLE},
1730
self.assertEqual(409, ret.status_code) # Conflict
1281
1732
@mock.patch.object(rpcapi.ConductorAPI, 'do_provisioning_action')
1282
1733
def test_manage_from_available(self, mock_dpa):
1283
1734
self.node.provision_state = states.AVAILABLE
1319
1770
self.assertEqual(urlparse.urlparse(ret.location).path,
1320
1771
expected_location)
1773
@mock.patch.object(rpcapi.ConductorAPI, 'set_console_mode')
1774
def test_set_console_by_name_unsupported(self, mock_scm):
1775
ret = self.put_json('/nodes/%s/states/console' % self.node.name,
1776
{'enabled': "true"},
1778
self.assertEqual(400, ret.status_code)
1780
@mock.patch.object(rpcapi.ConductorAPI, 'set_console_mode')
1781
def test_set_console_by_name(self, mock_scm):
1782
ret = self.put_json('/nodes/%s/states/console' % self.node.name,
1783
{'enabled': "true"},
1784
headers={api_base.Version.string: "1.5"})
1785
self.assertEqual(202, ret.status_code)
1786
self.assertEqual('', ret.body)
1787
mock_scm.assert_called_once_with(mock.ANY, self.node.uuid,
1322
1790
def test_set_console_mode_disabled(self):
1323
1791
with mock.patch.object(rpcapi.ConductorAPI,
1324
1792
'set_console_mode') as mock_scm:
1389
1857
topic='test-topic')
1391
1859
@mock.patch.object(rpcapi.ConductorAPI, 'set_boot_device')
1860
def test_set_boot_device_by_name(self, mock_sbd):
1861
device = boot_devices.PXE
1862
ret = self.put_json('/nodes/%s/management/boot_device'
1863
% self.node.name, {'boot_device': device},
1864
headers={api_base.Version.string: "1.5"})
1865
self.assertEqual(204, ret.status_code)
1866
self.assertEqual('', ret.body)
1867
mock_sbd.assert_called_once_with(mock.ANY, self.node.uuid,
1868
device, persistent=False,
1871
@mock.patch.object(rpcapi.ConductorAPI, 'set_boot_device')
1392
1872
def test_set_boot_device_not_supported(self, mock_sbd):
1393
1873
mock_sbd.side_effect = exception.UnsupportedDriverExtension(
1394
1874
extension='management', driver='test-driver')
1422
1902
self.assertEqual('application/json', ret.content_type)
1423
1903
self.assertEqual(400, ret.status_code)
1425
def _test_set_node_maintenance_mode(self, mock_update, mock_get, reason):
1905
def _test_set_node_maintenance_mode(self, mock_update, mock_get, reason,
1906
node_ident, is_by_name=False):
1426
1907
request_body = {}
1428
1909
request_body['reason'] = reason
1430
1911
self.node.maintenance = False
1431
1912
mock_get.return_value = self.node
1432
ret = self.put_json('/nodes/%s/maintenance' % self.node.uuid,
1914
headers = {api_base.Version.string: "1.5"}
1917
ret = self.put_json('/nodes/%s/maintenance' % node_ident,
1918
request_body, headers=headers)
1434
1919
self.assertEqual(202, ret.status_code)
1435
1920
self.assertEqual('', ret.body)
1436
1921
self.assertEqual(True, self.node.maintenance)
1437
1922
self.assertEqual(reason, self.node.maintenance_reason)
1438
mock_get.assert_called_once_with(mock.ANY, self.node.uuid)
1923
mock_get.assert_called_once_with(mock.ANY, node_ident)
1439
1924
mock_update.assert_called_once_with(mock.ANY, mock.ANY,
1440
1925
topic='test-topic')
1443
1928
@mock.patch.object(rpcapi.ConductorAPI, 'update_node')
1444
1929
def test_set_node_maintenance_mode(self, mock_update, mock_get):
1445
1930
self._test_set_node_maintenance_mode(mock_update, mock_get,
1931
'fake_reason', self.node.uuid)
1448
1933
@mock.patch.object(objects.Node, 'get_by_uuid')
1449
1934
@mock.patch.object(rpcapi.ConductorAPI, 'update_node')
1450
1935
def test_set_node_maintenance_mode_no_reason(self, mock_update, mock_get):
1451
self._test_set_node_maintenance_mode(mock_update, mock_get, None)
1936
self._test_set_node_maintenance_mode(mock_update, mock_get, None,
1939
@mock.patch.object(objects.Node, 'get_by_name')
1940
@mock.patch.object(rpcapi.ConductorAPI, 'update_node')
1941
def test_set_node_maintenance_mode_by_name(self, mock_update, mock_get):
1942
self._test_set_node_maintenance_mode(mock_update, mock_get,
1943
'fake_reason', self.node.name,
1946
@mock.patch.object(objects.Node, 'get_by_name')
1947
@mock.patch.object(rpcapi.ConductorAPI, 'update_node')
1948
def test_set_node_maintenance_mode_no_reason_by_name(self, mock_update,
1950
self._test_set_node_maintenance_mode(mock_update, mock_get, None,
1951
self.node.name, is_by_name=True)