~ubuntu-branches/ubuntu/vivid/ironic/vivid-updates

« back to all changes in this revision

Viewing changes to ironic/tests/api/v1/test_nodes.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2015-03-30 11:14:57 UTC
  • mfrom: (1.2.6)
  • Revision ID: package-import@ubuntu.com-20150330111457-kr4ju3guf22m4vbz
Tags: 2015.1~b3-0ubuntu1
* New upstream release.
  + d/control: 
    - Align with upstream dependencies.
    - Add dh-python to build-dependencies.
    - Add psmisc as a dependency. (LP: #1358820)
  + d/p/fix-requirements.patch: Rediffed.
  + d/ironic-conductor.init.in: Fixed typos in LSB headers,
    thanks to JJ Asghar. (LP: #1429962)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
import json
20
20
 
21
21
import mock
22
 
from oslo.utils import timeutils
23
22
from oslo_config import cfg
 
23
from oslo_utils import timeutils
 
24
from oslo_utils import uuidutils
 
25
import pecan
24
26
from six.moves.urllib import parse as urlparse
25
27
from testtools.matchers import HasLength
26
28
from wsme import types as wtypes
31
33
from ironic.common import boot_devices
32
34
from ironic.common import exception
33
35
from ironic.common import states
34
 
from ironic.common import utils
35
36
from ironic.conductor import rpcapi
36
37
from ironic import objects
37
38
from ironic.tests.api import base as test_api_base
51
52
    return node
52
53
 
53
54
 
 
55
class TestTopLevelFunctions(base.TestCase):
 
56
 
 
57
    def setUp(self):
 
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()
 
64
 
 
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))
 
70
 
 
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,
 
76
                                       mock_pr):
 
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)
 
83
 
 
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,
 
89
                                       mock_pr):
 
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)
 
96
 
 
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,
 
102
                                        mock_aln, mock_pr):
 
103
        mock_aln.return_value = True
 
104
        self.assertRaises(exception.InvalidUuidOrName,
 
105
                          api_node._get_rpc_node,
 
106
                          self.invalid_name)
 
107
 
 
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,
 
113
                                        mock_aln, mock_pr):
 
114
        mock_aln.return_value = True
 
115
        self.assertRaises(exception.InvalidUuidOrName,
 
116
                          api_node._get_rpc_node,
 
117
                          self.invalid_uuid)
 
118
 
 
119
 
54
120
class TestNodeObject(base.TestCase):
55
121
 
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)
80
146
 
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}
90
156
 
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])
119
185
 
120
186
    def test_get_one(self):
121
187
        node = obj_utils.create_test_node(self.context)
122
 
        data = self.get_json('/nodes/%s' % node['uuid'],
 
188
        data = self.get_json('/nodes/%s' % node.uuid,
123
189
                 headers={api_base.Version.string: str(api_v1.MAX_VER)})
124
190
        self.assertEqual(node.uuid, data['uuid'])
125
191
        self.assertIn('driver', data)
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)
137
206
 
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])
155
228
 
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)
161
234
 
163
236
        node = obj_utils.create_test_node(self.context,
164
237
                                          provision_state=states.AVAILABLE)
165
238
 
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'])
169
242
 
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'])
173
246
 
 
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)
 
255
 
 
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'])
 
262
 
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)
180
269
 
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'])
184
273
 
 
274
    def test_unset_logical_names(self):
 
275
        node = obj_utils.create_test_node(self.context,
 
276
                                          name="fish")
 
277
        data = self.get_json('/nodes/%s' % node.uuid,
 
278
                headers={api_base.Version.string: "1.4"})
 
279
        self.assertNotIn('name', data)
 
280
 
 
281
        data = self.get_json('/nodes/%s' % node.uuid,
 
282
                headers={api_base.Version.string: "1.5"})
 
283
        self.assertEqual('fish', data['name'])
 
284
 
185
285
    def test_many(self):
186
286
        nodes = []
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))
196
296
 
 
297
    def test_many_have_names(self):
 
298
        nodes = []
 
299
        node_names = []
 
300
        for id in range(5):
 
301
            name = 'node-%s' % id
 
302
            node = obj_utils.create_test_node(self.context,
 
303
                                              uuid=uuidutils.generate_uuid(),
 
304
                                              name=name)
 
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))
 
312
 
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())
209
325
        nodes = []
210
326
        for id in range(5):
211
327
            node = obj_utils.create_test_node(self.context,
212
 
                                              uuid=utils.generate_uuid())
 
328
                                              uuid=uuidutils.generate_uuid())
213
329
            nodes.append(node.uuid)
214
330
        data = self.get_json('/nodes/?limit=3')
215
331
        self.assertEqual(3, len(data['nodes']))
222
338
        nodes = []
223
339
        for id in range(5):
224
340
            node = obj_utils.create_test_node(self.context,
225
 
                                              uuid=utils.generate_uuid())
 
341
                                              uuid=uuidutils.generate_uuid())
226
342
            nodes.append(node.uuid)
227
343
        data = self.get_json('/nodes')
228
344
        self.assertEqual(3, len(data['nodes']))
240
356
 
241
357
        for id_ in range(2):
242
358
            obj_utils.create_test_port(self.context, node_id=node.id,
243
 
                                       uuid=utils.generate_uuid(),
 
359
                                       uuid=uuidutils.generate_uuid(),
244
360
                                       address='52:54:00:cf:2d:3%s' % id_)
245
361
 
246
362
        data = self.get_json('/nodes/%s/ports' % node.uuid)
289
405
        self.assertEqual(fake_error, data['last_error'])
290
406
        self.assertFalse(data['console_enabled'])
291
407
 
 
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,
 
415
                                          name='eggs',
 
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'])
 
433
 
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(
 
436
            self.context,
 
437
            uuid=uuidutils.generate_uuid(),
 
438
            instance_uuid=uuidutils.generate_uuid())
296
439
        instance_uuid = node.instance_uuid
297
440
 
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"})
299
443
 
300
444
        self.assertThat(data['nodes'], HasLength(1))
301
445
        self.assertEqual(node['instance_uuid'],
302
446
                         data['nodes'][0]["instance_uuid"])
303
447
 
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()
308
453
 
309
454
        data = self.get_json('/nodes?instance_uuid=%s' % wrong_uuid)
310
455
 
382
527
        self.assertIn('associated=True', data['next'])
383
528
 
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(
 
531
            self.context,
 
532
            uuid=uuidutils.generate_uuid(),
 
533
            instance_uuid=uuidutils.generate_uuid())
388
534
        instance_uuid = node.instance_uuid
389
535
 
390
536
        data = self.get_json('/nodes/detail?instance_uuid=%s' % instance_uuid)
403
549
        nodes = []
404
550
        for id in range(5):
405
551
            node = obj_utils.create_test_node(self.context,
406
 
                                              uuid=utils.generate_uuid(),
 
552
                                              uuid=uuidutils.generate_uuid(),
407
553
                                              maintenance=id % 2)
408
554
            nodes.append(node)
409
555
 
426
572
 
427
573
    def test_maintenance_nodes_associated(self):
428
574
        self._create_association_test_nodes()
429
 
        node = obj_utils.create_test_node(self.context,
430
 
                                          instance_uuid=utils.generate_uuid(),
431
 
                                          maintenance=True)
 
575
        node = obj_utils.create_test_node(
 
576
            self.context,
 
577
            instance_uuid=uuidutils.generate_uuid(),
 
578
            maintenance=True)
432
579
 
433
580
        data = self.get_json('/nodes?associated=true&maintenance=false')
434
581
        uuids = [n['uuid'] for n in data['nodes']]
452
599
            self.assertEqual(expected_data, data)
453
600
            mock_gci.assert_called_once_with(mock.ANY, node.uuid, 'test-topic')
454
601
 
 
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')
 
613
 
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')
486
645
 
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')
 
655
 
 
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')
507
676
 
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')
 
687
 
 
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')
518
698
 
 
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')
 
706
 
 
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')
 
713
 
 
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,
 
718
                            expect_errors=True)
 
719
        self.assertEqual(406, ret.status_code)
 
720
        self.assertFalse(mock_vdi.called)
 
721
 
 
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')
 
730
 
519
731
 
520
732
class TestPatch(test_api_base.FunctionalTest):
521
733
 
522
734
    def setUp(self):
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'
540
754
         .mock_update_node
541
755
         .return_value
542
756
         .updated_at) = "2013-12-03T06:20:41.184720+00:00"
543
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
757
        response = self.patch_json('/nodes/%s' % self.node.uuid,
544
758
                             [{'path': '/instance_uuid',
545
759
                               'value': 'aaaaaaaa-1111-bbbb-2222-cccccccccccc',
546
760
                               'op': 'replace'}])
551
765
        self.mock_update_node.assert_called_once_with(
552
766
                mock.ANY, mock.ANY, 'test-topic')
553
767
 
 
768
    def test_update_by_name_unsupported(self):
 
769
        self.mock_update_node.return_value = self.node
 
770
        (self
 
771
         .mock_update_node
 
772
         .return_value
 
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',
 
778
                  'op': 'replace'}],
 
779
                expect_errors=True)
 
780
        self.assertEqual(400, response.status_code)
 
781
        self.assertFalse(self.mock_update_node.called)
 
782
 
 
783
    def test_update_ok_by_name(self):
 
784
        self.mock_update_node.return_value = self.node
 
785
        (self
 
786
         .mock_update_node
 
787
         .return_value
 
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',
 
793
                  'op': 'replace'}],
 
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')
 
801
 
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)
564
812
        self.mock_update_node.side_effect = exception.InvalidParameterValue(
565
813
                                                fake_err)
566
814
 
567
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
815
        response = self.patch_json('/nodes/%s' % self.node.uuid,
568
816
                                   [{'path': '/driver_info/this',
569
817
                                     'value': 'foo',
570
818
                                     'op': 'add'},
581
829
    def test_update_fails_bad_driver(self):
582
830
        self.mock_gtf.side_effect = exception.NoValidHost('Fake Error')
583
831
 
584
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
832
        response = self.patch_json('/nodes/%s' % self.node.uuid,
585
833
                                   [{'path': '/driver',
586
834
                                     'value': 'bad-driver',
587
835
                                     'op': 'replace'}],
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)
597
845
 
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'}],
609
857
    def test_add_ok(self):
610
858
        self.mock_update_node.return_value = self.node
611
859
 
612
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
860
        response = self.patch_json('/nodes/%s' % self.node.uuid,
613
861
                                   [{'path': '/extra/foo',
614
862
                                     'value': 'bar',
615
863
                                     'op': 'add'}])
621
869
 
622
870
    def test_add_root(self):
623
871
        self.mock_update_node.return_value = self.node
624
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
872
        response = self.patch_json('/nodes/%s' % self.node.uuid,
625
873
                             [{'path': '/instance_uuid',
626
874
                               'value': 'aaaaaaaa-1111-bbbb-2222-cccccccccccc',
627
875
                               'op': 'add'}])
631
879
                mock.ANY, mock.ANY, 'test-topic')
632
880
 
633
881
    def test_add_root_non_existent(self):
634
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
882
        response = self.patch_json('/nodes/%s' % self.node.uuid,
635
883
                               [{'path': '/foo', 'value': 'bar', 'op': 'add'}],
636
884
                               expect_errors=True)
637
885
        self.assertEqual('application/json', response.content_type)
641
889
    def test_remove_ok(self):
642
890
        self.mock_update_node.return_value = self.node
643
891
 
644
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
892
        response = self.patch_json('/nodes/%s' % self.node.uuid,
645
893
                                   [{'path': '/extra',
646
894
                                     'op': 'remove'}])
647
895
        self.assertEqual('application/json', response.content_type)
651
899
                mock.ANY, mock.ANY, 'test-topic')
652
900
 
653
901
    def test_remove_non_existent_property_fail(self):
654
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
902
        response = self.patch_json('/nodes/%s' % self.node.uuid,
655
903
                             [{'path': '/extra/non-existent', 'op': 'remove'}],
656
904
                             expect_errors=True)
657
905
        self.assertEqual('application/json', response.content_type)
660
908
 
661
909
    def test_update_state_in_progress(self):
662
910
        node = obj_utils.create_test_node(self.context,
663
 
                                          uuid=utils.generate_uuid(),
 
911
                                          uuid=uuidutils.generate_uuid(),
664
912
                                          target_power_state=states.POWER_OFF)
665
913
        response = self.patch_json('/nodes/%s' % node.uuid,
666
914
                                   [{'path': '/extra/foo', 'value': 'bar',
671
919
 
672
920
    def test_add_state_in_deployfail(self):
673
921
        node = obj_utils.create_test_node(self.context,
674
 
                                          uuid=utils.generate_uuid(),
 
922
                                          uuid=uuidutils.generate_uuid(),
675
923
                                          provision_state=states.DEPLOYFAIL,
676
924
                                          target_provision_state=states.ACTIVE)
677
925
        self.mock_update_node.return_value = node
684
932
                mock.ANY, mock.ANY, 'test-topic')
685
933
 
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)
698
946
        self.assertTrue(response.json['error_message'])
699
947
 
700
948
    def test_remove_mandatory_field(self):
701
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
949
        response = self.patch_json('/nodes/%s' % self.node.uuid,
702
950
                                   [{'path': '/driver', 'op': 'remove'}],
703
951
                                   expect_errors=True)
704
952
        self.assertEqual('application/json', response.content_type)
753
1001
        self.assertTrue(response.json['error_message'])
754
1002
 
755
1003
    def test_replace_non_existent_chassis_uuid(self):
756
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
1004
        response = self.patch_json('/nodes/%s' % self.node.uuid,
757
1005
                             [{'path': '/chassis_uuid',
758
1006
                               'value': 'eeeeeeee-dddd-cccc-bbbb-aaaaaaaaaaaa',
759
1007
                               'op': 'replace'}], expect_errors=True)
762
1010
        self.assertTrue(response.json['error_message'])
763
1011
 
764
1012
    def test_remove_internal_field(self):
765
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
1013
        response = self.patch_json('/nodes/%s' % self.node.uuid,
766
1014
                                   [{'path': '/last_error', 'op': 'remove'}],
767
1015
                                   expect_errors=True)
768
1016
        self.assertEqual('application/json', response.content_type)
770
1018
        self.assertTrue(response.json['error_message'])
771
1019
 
772
1020
    def test_replace_internal_field(self):
773
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
1021
        response = self.patch_json('/nodes/%s' % self.node.uuid,
774
1022
                                   [{'path': '/power_state', 'op': 'replace',
775
1023
                                     'value': 'fake-state'}],
776
1024
                                   expect_errors=True)
790
1038
        self.mock_update_node.assert_called_once_with(
791
1039
                mock.ANY, mock.ANY, 'test-topic')
792
1040
 
 
1041
    def test_replace_maintenance_by_name(self):
 
1042
        self.mock_update_node.return_value = self.node
 
1043
 
 
1044
        response = self.patch_json(
 
1045
                '/nodes/%s' % self.node.name,
 
1046
                [{'path': '/maintenance', 'op': 'replace',
 
1047
                  'value': 'true'}],
 
1048
                headers={api_base.Version.string: "1.5"})
 
1049
        self.assertEqual('application/json', response.content_type)
 
1050
        self.assertEqual(200, response.status_code)
 
1051
 
 
1052
        self.mock_update_node.assert_called_once_with(
 
1053
                mock.ANY, mock.ANY, 'test-topic')
 
1054
 
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)
801
1063
 
802
1064
    def test_replace_provision_updated_at(self):
803
1065
        test_time = '2000-01-01 00:00:00'
804
 
        response = self.patch_json('/nodes/%s' % self.node['uuid'],
 
1066
        response = self.patch_json('/nodes/%s' % self.node.uuid,
805
1067
                                   [{'path': '/provision_updated_at',
806
1068
                                     'op': 'replace', 'value': test_time}],
807
1069
                                   expect_errors=True)
809
1071
        self.assertEqual(400, response.status_code)
810
1072
        self.assertTrue(response.json['error_message'])
811
1073
 
 
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,
 
1078
                                   [{'path': '/name',
 
1079
                                     'op': 'add',
 
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)
 
1084
 
 
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,
 
1089
                                   [{'path': '/name',
 
1090
                                     'op': 'add',
 
1091
                                     'value': test_name}],
 
1092
                                   headers={api_base.Version.string: "1.5"},
 
1093
                                   expect_errors=True)
 
1094
        self.assertEqual('application/json', response.content_type)
 
1095
        self.assertEqual(400, response.status_code)
 
1096
        self.assertTrue(response.json['error_message'])
 
1097
 
 
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,
 
1102
                                   [{'path': '/name',
 
1103
                                     'op': 'replace',
 
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)
 
1108
 
 
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,
 
1113
                                   [{'path': '/name',
 
1114
                                     'op': 'replace',
 
1115
                                     'value': test_name}],
 
1116
                                   headers={api_base.Version.string: "1.5"},
 
1117
                                   expect_errors=True)
 
1118
        self.assertEqual('application/json', response.content_type)
 
1119
        self.assertEqual(400, response.status_code)
 
1120
        self.assertTrue(response.json['error_message'])
 
1121
 
 
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,
 
1128
                                   [{'path': '/name',
 
1129
                                     'op': 'replace',
 
1130
                                     'value': test_name}],
 
1131
                                   headers={api_base.Version.string: "1.5"},
 
1132
                                   expect_errors=True)
 
1133
        self.assertEqual('application/json', response.content_type)
 
1134
        self.assertEqual(409, response.status_code)
 
1135
        self.assertTrue(response.json['error_message'])
 
1136
 
812
1137
 
813
1138
class TestPost(test_api_base.FunctionalTest):
814
1139
 
891
1216
        self.assertEqual(expected_return_value, response.body)
892
1217
        self.assertEqual(expected_status, response.status_code)
893
1218
 
 
1219
    def _test_vendor_passthru_ok_by_name(self, mock_vendor, return_value=None,
 
1220
                                         is_async=True):
 
1221
        expected_status = 202 if is_async else 200
 
1222
        expected_return_value = json.dumps(return_value)
 
1223
 
 
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,
 
1228
                                  info,
 
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)
 
1234
 
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)
914
1255
 
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)
 
1259
 
 
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')
1061
1406
 
 
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,
 
1411
                    expect_errors=True)
 
1412
        self.assertFalse(mock_dn.called)
 
1413
 
 
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')
 
1420
 
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)
1072
1431
 
 
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)
 
1436
 
 
1437
        response = self.delete('/nodes/%s' % node.name,
 
1438
                               expect_errors=True)
 
1439
        self.assertEqual(400, response.status_int)
 
1440
        self.assertFalse(mock_gbn.called)
 
1441
 
 
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)
 
1446
 
 
1447
        response = self.delete('/nodes/%s' % node.name,
 
1448
                headers={api_base.Version.string: "1.5"},
 
1449
                expect_errors=True)
 
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)
 
1454
 
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')
1105
1487
 
 
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,
 
1491
                                                  mock_get):
 
1492
        node = obj_utils.create_test_node(self.context, maintenance=True,
 
1493
                                          maintenance_reason='blah',
 
1494
                                          name='foo')
 
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,
 
1504
                                            topic='test-topic')
 
1505
 
1106
1506
 
1107
1507
class TestPut(test_api_base.FunctionalTest):
1108
1508
 
1110
1510
        super(TestPut, self).setUp()
1111
1511
        self.chassis = obj_utils.create_test_chassis(self.context)
1112
1512
        self.node = obj_utils.create_test_node(self.context,
1113
 
                provision_state=states.AVAILABLE)
 
1513
                provision_state=states.AVAILABLE, name='node-39')
1114
1514
        p = mock.patch.object(rpcapi.ConductorAPI, 'get_topic_for')
1115
1515
        self.mock_gtf = p.start()
1116
1516
        self.mock_gtf.return_value = 'test-topic'
1124
1524
        p = mock.patch.object(rpcapi.ConductorAPI, 'do_node_tear_down')
1125
1525
        self.mock_dntd = p.start()
1126
1526
        self.addCleanup(p.stop)
 
1527
        p = mock.patch.object(rpcapi.ConductorAPI, 'inspect_hardware')
 
1528
        self.mock_dnih = p.start()
 
1529
        self.addCleanup(p.stop)
1127
1530
 
1128
1531
    def test_power_state(self):
1129
1532
        response = self.put_json('/nodes/%s/states/power' % self.node.uuid,
1140
1543
        self.assertEqual(urlparse.urlparse(response.location).path,
1141
1544
                         expected_location)
1142
1545
 
 
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},
 
1549
                                 expect_errors=True)
 
1550
        self.assertEqual(400, response.status_code)
 
1551
 
 
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,
 
1559
                                               self.node.uuid,
 
1560
                                               states.POWER_ON,
 
1561
                                               'test-topic')
 
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,
 
1566
                         expected_location)
 
1567
 
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)
1165
1590
 
 
1591
    def test_provision_by_name_unsupported(self):
 
1592
        ret = self.put_json('/nodes/%s/states/provision' % self.node.name,
 
1593
                            {'target': states.ACTIVE},
 
1594
                            expect_errors=True)
 
1595
        self.assertEqual(400, ret.status_code)
 
1596
 
 
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')
 
1605
 
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'],
1279
1719
                                         'test-topic')
1280
1720
 
 
1721
    def test_inspect_already_in_progress(self):
 
1722
        node = self.node
 
1723
        node.provision_state = states.INSPECTING
 
1724
        node.target_provision_state = states.MANAGEABLE
 
1725
        node.reservation = 'fake-host'
 
1726
        node.save()
 
1727
        ret = self.put_json('/nodes/%s/states/provision' % node.uuid,
 
1728
                            {'target': states.MANAGEABLE},
 
1729
                            expect_errors=True)
 
1730
        self.assertEqual(409, ret.status_code)  # Conflict
 
1731
 
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)
1321
1772
 
 
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"},
 
1777
                            expect_errors=True)
 
1778
        self.assertEqual(400, ret.status_code)
 
1779
 
 
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,
 
1788
                                             True, 'test-topic')
 
1789
 
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')
1390
1858
 
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,
 
1869
                                         topic='test-topic')
 
1870
 
 
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)
1424
1904
 
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 = {}
1427
1908
        if reason:
1428
1909
            request_body['reason'] = reason
1429
1910
 
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,
1433
 
                            request_body)
 
1913
        if is_by_name:
 
1914
            headers = {api_base.Version.string: "1.5"}
 
1915
        else:
 
1916
            headers = {}
 
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')
1441
1926
 
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,
1446
 
                                             'fake_reason')
 
1931
                                             'fake_reason', self.node.uuid)
1447
1932
 
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,
 
1937
                                             self.node.uuid)
 
1938
 
 
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,
 
1944
                                             is_by_name=True)
 
1945
 
 
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,
 
1949
                                                         mock_get):
 
1950
        self._test_set_node_maintenance_mode(mock_update, mock_get, None,
 
1951
                                             self.node.name, is_by_name=True)