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

« back to all changes in this revision

Viewing changes to ironic/tests/drivers/test_pxe.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:
20
20
import os
21
21
import tempfile
22
22
 
23
 
import fixtures
24
23
import mock
25
24
from oslo_config import cfg
26
25
from oslo_serialization import jsonutils as json
27
26
 
 
27
from ironic.common import boot_devices
28
28
from ironic.common import dhcp_factory
 
29
from ironic.common import driver_factory
29
30
from ironic.common import exception
30
31
from ironic.common.glance_service import base_image_service
31
32
from ironic.common import keystone
34
35
from ironic.common import utils
35
36
from ironic.conductor import task_manager
36
37
from ironic.conductor import utils as manager_utils
 
38
from ironic.drivers.modules import agent_base_vendor
37
39
from ironic.drivers.modules import deploy_utils
38
40
from ironic.drivers.modules import iscsi_deploy
39
41
from ironic.drivers.modules import pxe
 
42
from ironic.drivers import utils as driver_utils
40
43
from ironic.openstack.common import fileutils
41
44
from ironic.tests.conductor import utils as mgr_utils
42
45
from ironic.tests.db import base as db_base
47
50
 
48
51
INST_INFO_DICT = db_utils.get_test_pxe_instance_info()
49
52
DRV_INFO_DICT = db_utils.get_test_pxe_driver_info()
 
53
DRV_INTERNAL_INFO_DICT = db_utils.get_test_pxe_driver_internal_info()
50
54
 
51
55
 
52
56
class PXEValidateParametersTestCase(db_base.DbTestCase):
53
57
 
54
58
    def test__parse_deploy_info(self):
55
59
        # make sure we get back the expected things
56
 
        node = obj_utils.create_test_node(self.context,
57
 
                                          driver='fake_pxe',
58
 
                                          instance_info=INST_INFO_DICT,
59
 
                                          driver_info=DRV_INFO_DICT)
 
60
        node = obj_utils.create_test_node(
 
61
                    self.context,
 
62
                    driver='fake_pxe',
 
63
                    instance_info=INST_INFO_DICT,
 
64
                    driver_info=DRV_INFO_DICT,
 
65
                    driver_internal_info=DRV_INTERNAL_INFO_DICT,
 
66
               )
 
67
 
60
68
        info = pxe._parse_deploy_info(node)
61
69
        self.assertIsNotNone(info.get('deploy_ramdisk'))
62
70
        self.assertIsNotNone(info.get('deploy_kernel'))
67
75
    def test__parse_driver_info_missing_deploy_kernel(self):
68
76
        # make sure error is raised when info is missing
69
77
        info = dict(DRV_INFO_DICT)
70
 
        del info['pxe_deploy_kernel']
 
78
        del info['deploy_kernel']
71
79
        node = obj_utils.create_test_node(self.context, driver_info=info)
72
80
        self.assertRaises(exception.MissingParameterValue,
73
81
                pxe._parse_driver_info,
76
84
    def test__parse_driver_info_missing_deploy_ramdisk(self):
77
85
        # make sure error is raised when info is missing
78
86
        info = dict(DRV_INFO_DICT)
79
 
        del info['pxe_deploy_ramdisk']
 
87
        del info['deploy_ramdisk']
80
88
        node = obj_utils.create_test_node(self.context, driver_info=info)
81
89
        self.assertRaises(exception.MissingParameterValue,
82
90
                pxe._parse_driver_info,
91
99
        self.assertIsNotNone(info.get('deploy_ramdisk'))
92
100
        self.assertIsNotNone(info.get('deploy_kernel'))
93
101
 
 
102
    def test__parse_driver_info_backwards_compat(self):
 
103
        old_drv_info = {}
 
104
        old_drv_info['pxe_deploy_kernel'] = DRV_INFO_DICT['deploy_kernel']
 
105
        old_drv_info['pxe_deploy_ramdisk'] = DRV_INFO_DICT['deploy_ramdisk']
 
106
        node = obj_utils.create_test_node(self.context,
 
107
                                          driver='fake_pxe',
 
108
                                          driver_info=old_drv_info)
 
109
        info = pxe._parse_driver_info(node)
 
110
        self.assertIsNotNone(info.get('deploy_ramdisk'))
 
111
        self.assertIsNotNone(info.get('deploy_kernel'))
 
112
 
94
113
 
95
114
class PXEPrivateMethodsTestCase(db_base.DbTestCase):
96
115
 
100
119
              'driver': 'fake_pxe',
101
120
              'instance_info': INST_INFO_DICT,
102
121
              'driver_info': DRV_INFO_DICT,
 
122
              'driver_internal_info': DRV_INTERNAL_INFO_DICT,
103
123
        }
104
124
        mgr_utils.mock_the_extension_manager(driver="fake_pxe")
105
125
        self.node = obj_utils.create_test_node(self.context, **n)
106
126
 
107
127
    @mock.patch.object(base_image_service.BaseImageService, '_show')
108
 
    def test__get_image_info(self, show_mock):
 
128
    def _test__get_image_info(self, show_mock):
109
129
        properties = {'properties': {u'kernel_id': u'instance_kernel_uuid',
110
130
                     u'ramdisk_id': u'instance_ramdisk_uuid'}}
111
131
 
120
140
                                       self.node.uuid,
121
141
                                       'kernel')),
122
142
                         'deploy_ramdisk':
123
 
                         (DRV_INFO_DICT['pxe_deploy_ramdisk'],
 
143
                         (DRV_INFO_DICT['deploy_ramdisk'],
124
144
                           os.path.join(CONF.pxe.tftp_root,
125
145
                                        self.node.uuid,
126
146
                                        'deploy_ramdisk')),
127
147
                         'deploy_kernel':
128
 
                         (DRV_INFO_DICT['pxe_deploy_kernel'],
 
148
                         (DRV_INFO_DICT['deploy_kernel'],
129
149
                          os.path.join(CONF.pxe.tftp_root,
130
150
                                       self.node.uuid,
131
151
                                       'deploy_kernel'))}
132
152
        show_mock.return_value = properties
133
153
        image_info = pxe._get_image_info(self.node, self.context)
134
154
        show_mock.assert_called_once_with('glance://image_uuid',
135
 
                                           method='get')
 
155
                                          method='get')
136
156
        self.assertEqual(expected_info, image_info)
137
157
 
138
158
        # test with saved info
145
165
        self.assertEqual('instance_ramdisk_uuid',
146
166
                         self.node.instance_info.get('ramdisk'))
147
167
 
 
168
    def test__get_image_info(self):
 
169
        # Tests when 'is_whole_disk_image' exists in driver_internal_info
 
170
        self._test__get_image_info()
 
171
 
 
172
    def test__get_image_info_without_is_whole_disk_image(self):
 
173
        # Tests when 'is_whole_disk_image' doesn't exists in
 
174
        # driver_internal_info
 
175
        del self.node.driver_internal_info['is_whole_disk_image']
 
176
        self.node.save()
 
177
        self._test__get_image_info()
 
178
 
 
179
    @mock.patch.object(base_image_service.BaseImageService, '_show')
 
180
    def test__get_image_info_whole_disk_image(self, show_mock):
 
181
        properties = {'properties': None}
 
182
 
 
183
        expected_info = {'deploy_ramdisk':
 
184
                         (DRV_INFO_DICT['deploy_ramdisk'],
 
185
                           os.path.join(CONF.pxe.tftp_root,
 
186
                                        self.node.uuid,
 
187
                                        'deploy_ramdisk')),
 
188
                         'deploy_kernel':
 
189
                         (DRV_INFO_DICT['deploy_kernel'],
 
190
                          os.path.join(CONF.pxe.tftp_root,
 
191
                                       self.node.uuid,
 
192
                                       'deploy_kernel'))}
 
193
        show_mock.return_value = properties
 
194
        self.node.driver_internal_info['is_whole_disk_image'] = True
 
195
        image_info = pxe._get_image_info(self.node, self.context)
 
196
        self.assertEqual(expected_info, image_info)
 
197
 
148
198
    @mock.patch.object(iscsi_deploy, 'build_deploy_ramdisk_options')
149
199
    @mock.patch.object(pxe_utils, '_build_pxe_config')
150
200
    def _test_build_pxe_config_options(self, build_pxe_mock, deploy_opts_mock,
151
201
                                        ipxe_enabled=False):
152
202
        self.config(pxe_append_params='test_param', group='pxe')
153
203
        # NOTE: right '/' should be removed from url string
154
 
        self.config(api_url='http://192.168.122.184:6385/', group='conductor')
 
204
        self.config(api_url='http://192.168.122.184:6385', group='conductor')
155
205
        self.config(disk_devices='sda', group='pxe')
156
206
 
157
 
        fake_deploy_opts = {'iscsi_target_iqn': 'fake-iqn',
 
207
        fake_deploy_opts = {
 
208
                            'iscsi_target_iqn': 'fake-iqn',
158
209
                            'deployment_id': 'fake-deploy-id',
159
210
                            'deployment_key': 'fake-deploy-key',
160
211
                            'disk': 'fake-disk',
161
 
                            'ironic_api_url': 'fake-api-url'}
 
212
                            'ironic_api_url': 'fake-api-url',
 
213
                            'boot_option': 'netboot',
 
214
                            'boot_mode': 'bios',
 
215
                           }
162
216
 
163
217
        deploy_opts_mock.return_value = fake_deploy_opts
164
218
 
193
247
            'pxe_append_params': 'test_param',
194
248
            'aki_path': kernel,
195
249
            'deployment_aki_path': deploy_kernel,
196
 
            'tftp_server': tftp_server
 
250
            'tftp_server': tftp_server,
 
251
            'boot_option': 'netboot',
 
252
            'ipa-api-url': CONF.conductor.api_url,
 
253
            'ipa-driver-name': self.node.driver,
 
254
            'boot_mode': 'bios',
197
255
        }
198
256
 
199
257
        expected_options.update(fake_deploy_opts)
213
271
                      'ramdisk': ('ramdisk_id',
214
272
                                  os.path.join(root_dir,
215
273
                                               self.node.uuid,
216
 
                                               'ramdisk'))
217
 
                      }
 
274
                                               'ramdisk'))}
218
275
        options = pxe._build_pxe_config_options(self.node,
219
276
                                                image_info,
220
277
                                                self.context)
226
283
    def test__build_pxe_config_options_ipxe(self):
227
284
        self._test_build_pxe_config_options(ipxe_enabled=True)
228
285
 
 
286
    def test__build_pxe_config_options_without_is_whole_disk_image(self):
 
287
        del self.node.driver_internal_info['is_whole_disk_image']
 
288
        self.node.save()
 
289
        self._test_build_pxe_config_options(ipxe_enabled=False)
 
290
 
 
291
    @mock.patch.object(iscsi_deploy, 'build_deploy_ramdisk_options')
 
292
    @mock.patch.object(pxe_utils, '_build_pxe_config')
 
293
    def _test_build_pxe_config_options_whole_disk_image(self, build_pxe_mock,
 
294
                                       deploy_opts_mock, ipxe_enabled=False):
 
295
        self.config(pxe_append_params='test_param', group='pxe')
 
296
        # NOTE: right '/' should be removed from url string
 
297
        self.config(api_url='http://192.168.122.184:6385/', group='conductor')
 
298
        self.config(disk_devices='sda', group='pxe')
 
299
 
 
300
        fake_deploy_opts = {'iscsi_target_iqn': 'fake-iqn',
 
301
                            'deployment_id': 'fake-deploy-id',
 
302
                            'deployment_key': 'fake-deploy-key',
 
303
                            'disk': 'fake-disk',
 
304
                            'ironic_api_url': 'fake-api-url'}
 
305
 
 
306
        deploy_opts_mock.return_value = fake_deploy_opts
 
307
 
 
308
        tftp_server = CONF.pxe.tftp_server
 
309
 
 
310
        if ipxe_enabled:
 
311
            http_url = 'http://192.1.2.3:1234'
 
312
            self.config(ipxe_enabled=True, group='pxe')
 
313
            self.config(http_url=http_url, group='pxe')
 
314
 
 
315
            deploy_kernel = os.path.join(http_url, self.node.uuid,
 
316
                                         'deploy_kernel')
 
317
            deploy_ramdisk = os.path.join(http_url, self.node.uuid,
 
318
                                          'deploy_ramdisk')
 
319
            root_dir = CONF.pxe.http_root
 
320
        else:
 
321
            deploy_kernel = os.path.join(CONF.pxe.tftp_root, self.node.uuid,
 
322
                                         'deploy_kernel')
 
323
            deploy_ramdisk = os.path.join(CONF.pxe.tftp_root, self.node.uuid,
 
324
                                         'deploy_ramdisk')
 
325
            root_dir = CONF.pxe.tftp_root
 
326
 
 
327
        expected_options = {
 
328
            'deployment_ari_path': deploy_ramdisk,
 
329
            'pxe_append_params': 'test_param',
 
330
            'deployment_aki_path': deploy_kernel,
 
331
            'tftp_server': tftp_server,
 
332
        }
 
333
 
 
334
        expected_options.update(fake_deploy_opts)
 
335
 
 
336
        image_info = {'deploy_kernel': ('deploy_kernel',
 
337
                                        os.path.join(root_dir,
 
338
                                                     self.node.uuid,
 
339
                                                     'deploy_kernel')),
 
340
                      'deploy_ramdisk': ('deploy_ramdisk',
 
341
                                         os.path.join(root_dir,
 
342
                                                      self.node.uuid,
 
343
                                                      'deploy_ramdisk')),
 
344
                      }
 
345
        self.node.driver_internal_info['is_whole_disk_image'] = True
 
346
        options = pxe._build_pxe_config_options(self.node,
 
347
                                                image_info,
 
348
                                                self.context)
 
349
        self.assertEqual(expected_options, options)
 
350
 
229
351
    def test_get_token_file_path(self):
230
352
        node_uuid = self.node.uuid
231
353
        self.assertEqual('/tftpboot/token-' + node_uuid,
292
414
        mgr_utils.mock_the_extension_manager(driver="fake_pxe")
293
415
        instance_info = INST_INFO_DICT
294
416
        instance_info['deploy_key'] = 'fake-56789'
295
 
        self.node = obj_utils.create_test_node(self.context,
296
 
                                               driver='fake_pxe',
297
 
                                               instance_info=instance_info,
298
 
                                               driver_info=DRV_INFO_DICT)
 
417
        self.node = obj_utils.create_test_node(
 
418
                        self.context,
 
419
                        driver='fake_pxe',
 
420
                        instance_info=instance_info,
 
421
                        driver_info=DRV_INFO_DICT,
 
422
                        driver_internal_info=DRV_INTERNAL_INFO_DICT)
299
423
        self.port = obj_utils.create_test_port(self.context,
300
424
                                               node_id=self.node.id)
301
425
        self.config(group='conductor', api_url='http://127.0.0.1:1234/')
319
443
                                  shared=True) as task:
320
444
            task.driver.deploy.validate(task)
321
445
 
 
446
    @mock.patch.object(base_image_service.BaseImageService, '_show')
 
447
    def test_validate_good_whole_disk_image(self, mock_glance):
 
448
        with task_manager.acquire(self.context, self.node.uuid,
 
449
                                  shared=True) as task:
 
450
            task.node.driver_internal_info['is_whole_disk_image'] = True
 
451
            task.driver.deploy.validate(task)
 
452
 
322
453
    def test_validate_fail(self):
323
454
        info = dict(INST_INFO_DICT)
324
455
        del info['image_source']
353
484
            self.assertRaises(exception.InvalidParameterValue,
354
485
                              task.driver.deploy.validate, task)
355
486
 
 
487
    @mock.patch.object(base_image_service.BaseImageService, '_show')
 
488
    def test_validate_fail_invalid_boot_option(self, mock_glance):
 
489
        properties = {'capabilities': 'boot_option:foo,dog:wuff'}
 
490
        mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel',
 
491
                                                   'ramdisk_id': 'fake-initr'}}
 
492
        with task_manager.acquire(self.context, self.node.uuid,
 
493
                                  shared=True) as task:
 
494
            task.node.properties = properties
 
495
            self.assertRaises(exception.InvalidParameterValue,
 
496
                              task.driver.deploy.validate, task)
 
497
 
356
498
    def test_validate_fail_no_port(self):
357
499
        new_node = obj_utils.create_test_node(
358
500
                self.context,
435
577
                self.assertRaises(exception.InvalidParameterValue,
436
578
                                  task.driver.deploy.validate, task)
437
579
 
 
580
    @mock.patch.object(base_image_service.BaseImageService, '_show')
 
581
    def test_validate_invalid_root_device_hints(self, mock_glance):
 
582
        mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel',
 
583
                                                   'ramdisk_id': 'fake-initr'}}
 
584
        with task_manager.acquire(self.context, self.node.uuid,
 
585
                                  shared=True) as task:
 
586
            task.node.properties['root_device'] = {'size': 'not-int'}
 
587
            self.assertRaises(exception.InvalidParameterValue,
 
588
                              task.driver.deploy.validate, task)
 
589
 
438
590
    def test_vendor_passthru_validate_good(self):
439
591
        with task_manager.acquire(self.context, self.node.uuid,
440
592
                                  shared=True) as task:
479
631
            mock_cache_r_k.assert_called_once_with(self.context,
480
632
                                                   task.node, None)
481
633
 
 
634
    @mock.patch.object(pxe, '_get_image_info')
 
635
    @mock.patch.object(pxe, '_cache_ramdisk_kernel')
 
636
    @mock.patch.object(pxe, '_build_pxe_config_options')
 
637
    @mock.patch.object(pxe_utils, 'create_pxe_config')
 
638
    @mock.patch.object(pxe_utils, 'get_pxe_config_file_path')
 
639
    @mock.patch.object(deploy_utils, 'switch_pxe_config')
 
640
    def test_prepare_node_active_missing_root_uuid(self,
 
641
                                                   mock_switch,
 
642
                                                   mock_pxe_get_cfg,
 
643
                                                   mock_pxe_config,
 
644
                                                   mock_build_pxe,
 
645
                                                   mock_cache_r_k,
 
646
                                                   mock_img_info):
 
647
        mock_build_pxe.return_value = None
 
648
        mock_img_info.return_value = None
 
649
        self.node.provision_state = states.ACTIVE
 
650
        self.node.save()
 
651
 
 
652
        with task_manager.acquire(self.context, self.node.uuid) as task:
 
653
            task.driver.deploy.prepare(task)
 
654
            mock_img_info.assert_called_once_with(task.node,
 
655
                                                  self.context)
 
656
            mock_pxe_config.assert_called_once_with(
 
657
                task, None, CONF.pxe.pxe_config_template)
 
658
            mock_cache_r_k.assert_called_once_with(self.context,
 
659
                                                   task.node, None)
 
660
            self.assertFalse(mock_pxe_get_cfg.called)
 
661
            self.assertFalse(mock_switch.called)
 
662
 
 
663
    @mock.patch.object(pxe, '_get_image_info')
 
664
    @mock.patch.object(pxe, '_cache_ramdisk_kernel')
 
665
    @mock.patch.object(pxe, '_build_pxe_config_options')
 
666
    @mock.patch.object(pxe_utils, 'create_pxe_config')
 
667
    @mock.patch.object(pxe_utils, 'get_pxe_config_file_path')
 
668
    @mock.patch.object(deploy_utils, 'switch_pxe_config')
 
669
    @mock.patch.object(driver_utils, 'get_node_capability')
 
670
    def _test_prepare_node_active(self,
 
671
                                 mock_get_cap,
 
672
                                 mock_switch,
 
673
                                 mock_pxe_get_cfg,
 
674
                                 mock_pxe_config,
 
675
                                 mock_build_pxe,
 
676
                                 mock_cache_r_k,
 
677
                                 mock_img_info):
 
678
        mock_build_pxe.return_value = None
 
679
        mock_img_info.return_value = None
 
680
        mock_pxe_get_cfg.return_value = '/path'
 
681
        mock_get_cap.return_value = None
 
682
 
 
683
        self.node.provision_state = states.ACTIVE
 
684
        self.node.save()
 
685
 
 
686
        with task_manager.acquire(self.context, self.node.uuid) as task:
 
687
            task.driver.deploy.prepare(task)
 
688
            mock_img_info.assert_called_once_with(task.node,
 
689
                                                  self.context)
 
690
            mock_pxe_config.assert_called_once_with(
 
691
                task, None, CONF.pxe.pxe_config_template)
 
692
            mock_cache_r_k.assert_called_once_with(self.context,
 
693
                                                   task.node, None)
 
694
 
 
695
            mock_pxe_get_cfg.assert_called_once_with(task.node.uuid)
 
696
            iwdi = task.node.driver_internal_info.get('is_whole_disk_image')
 
697
            mock_switch.assert_called_once_with('/path', 'abcd', None, iwdi)
 
698
 
 
699
    def test_prepare_node_active(self):
 
700
        self.node.driver_internal_info = {'root_uuid_or_disk_id': 'abcd',
 
701
                                          'is_whole_disk_image': False}
 
702
        self.node.save()
 
703
        self._test_prepare_node_active()
 
704
 
 
705
    def test_prepare_node_active_without_is_whole_disk_image(self):
 
706
        self.node.driver_internal_info = {'root_uuid_or_disk_id': 'abcd'}
 
707
        self.node.save()
 
708
        self._test_prepare_node_active()
 
709
 
482
710
    @mock.patch.object(keystone, 'token_expires_soon')
483
711
    @mock.patch.object(deploy_utils, 'get_image_mb')
484
712
    @mock.patch.object(iscsi_deploy, '_get_image_file_path')
571
799
            self.assertEqual(states.DELETED, state)
572
800
            node_power_mock.assert_called_once_with(task, states.POWER_OFF)
573
801
 
 
802
    @mock.patch.object(pxe_utils, 'clean_up_pxe_config')
574
803
    @mock.patch.object(dhcp_factory.DHCPFactory, 'update_dhcp')
575
 
    def test_take_over(self, update_dhcp_mock):
 
804
    def test_take_over(self, update_dhcp_mock, clean_pxe_mock):
576
805
        with task_manager.acquire(
577
806
                self.context, self.node.uuid, shared=True) as task:
578
807
            dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
579
808
            task.driver.deploy.take_over(task)
580
 
            update_dhcp_mock.assert_called_once_with(
581
 
                task, dhcp_opts)
582
 
 
 
809
 
 
810
            # Assert we update the DHCP server
 
811
            update_dhcp_mock.assert_called_once_with(task, dhcp_opts)
 
812
 
 
813
            # Assert we don't clean the PXE config files in
 
814
            # case it's not local boot
 
815
            self.assertFalse(clean_pxe_mock.called)
 
816
 
 
817
    @mock.patch.object(pxe_utils, 'clean_up_pxe_config')
 
818
    @mock.patch.object(dhcp_factory.DHCPFactory, 'update_dhcp')
 
819
    def test_take_over_localboot(self, update_dhcp_mock, clean_pxe_mock):
 
820
        with task_manager.acquire(
 
821
                self.context, self.node.uuid, shared=True) as task:
 
822
            task.node.instance_info['capabilities'] = {"boot_option": "local"}
 
823
            task.driver.deploy.take_over(task)
 
824
 
 
825
            # Assert we are not attempting to update the DHCP
 
826
            # server in case it's local boot
 
827
            self.assertFalse(update_dhcp_mock.called)
 
828
 
 
829
            # Assert we are cleaning the PXE config files
 
830
            clean_pxe_mock.assert_called_once_with(task)
 
831
 
 
832
    @mock.patch.object(pxe_utils, 'clean_up_pxe_config')
 
833
    @mock.patch.object(manager_utils, 'node_set_boot_device')
583
834
    @mock.patch.object(deploy_utils, 'notify_deploy_complete')
584
835
    @mock.patch.object(deploy_utils, 'switch_pxe_config')
585
836
    @mock.patch.object(iscsi_deploy, 'InstanceImageCache')
586
 
    def test_continue_deploy_good(self, mock_image_cache, mock_switch_config,
587
 
            notify_mock):
 
837
    @mock.patch.object(deploy_utils, 'deploy_partition_image')
 
838
    def _test_pass_deploy_info_deploy(self, is_localboot, mock_deploy,
 
839
                                      mock_image_cache, mock_switch_config,
 
840
                                      notify_mock, mock_node_boot_dev,
 
841
                                      mock_clean_pxe):
588
842
        token_path = self._create_token_file()
 
843
 
 
844
        # set local boot
 
845
        if is_localboot:
 
846
            i_info = self.node.instance_info
 
847
            i_info['capabilities'] = '{"boot_option": "local"}'
 
848
            self.node.instance_info = i_info
 
849
 
589
850
        self.node.power_state = states.POWER_ON
590
851
        self.node.provision_state = states.DEPLOYWAIT
591
852
        self.node.target_provision_state = states.ACTIVE
592
853
        self.node.save()
593
854
 
594
855
        root_uuid = "12345678-1234-1234-1234-1234567890abcxyz"
595
 
        boot_mode = None
596
 
 
597
 
        def fake_deploy(**kwargs):
598
 
            return root_uuid
599
 
 
600
 
        self.useFixture(fixtures.MonkeyPatch(
601
 
                'ironic.drivers.modules.deploy_utils.deploy',
602
 
                fake_deploy))
603
 
 
604
 
        with task_manager.acquire(self.context, self.node.uuid) as task:
605
 
            task.driver.vendor._continue_deploy(
606
 
                    task, address='123456', iqn='aaa-bbb', key='fake-56789')
607
 
 
608
 
        self.node.refresh()
609
 
        self.assertEqual(states.ACTIVE, self.node.provision_state)
610
 
        self.assertEqual(states.NOSTATE, self.node.target_provision_state)
611
 
        self.assertEqual(states.POWER_ON, self.node.power_state)
612
 
        self.assertIsNone(self.node.last_error)
613
 
        self.assertFalse(os.path.exists(token_path))
614
 
        mock_image_cache.assert_called_once_with()
615
 
        mock_image_cache.return_value.clean_up.assert_called_once_with()
616
 
        pxe_config_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
617
 
        mock_switch_config.assert_called_once_with(pxe_config_path, root_uuid,
618
 
                                boot_mode)
619
 
        notify_mock.assert_called_once_with('123456')
620
 
 
621
 
    @mock.patch.object(iscsi_deploy, 'InstanceImageCache')
622
 
    def test_continue_deploy_fail(self, mock_image_cache):
623
 
        token_path = self._create_token_file()
624
 
        self.node.power_state = states.POWER_ON
625
 
        self.node.provision_state = states.DEPLOYWAIT
626
 
        self.node.target_provision_state = states.ACTIVE
627
 
        self.node.save()
628
 
 
629
 
        def fake_deploy(**kwargs):
630
 
            raise exception.InstanceDeployFailure("test deploy error")
631
 
 
632
 
        self.useFixture(fixtures.MonkeyPatch(
633
 
                'ironic.drivers.modules.deploy_utils.deploy',
634
 
                fake_deploy))
635
 
 
636
 
        with task_manager.acquire(self.context, self.node.uuid) as task:
637
 
            task.driver.vendor._continue_deploy(
638
 
                    task, address='123456', iqn='aaa-bbb', key='fake-56789')
639
 
 
640
 
        self.node.refresh()
641
 
        self.assertEqual(states.DEPLOYFAIL, self.node.provision_state)
642
 
        self.assertEqual(states.ACTIVE, self.node.target_provision_state)
643
 
        self.assertEqual(states.POWER_OFF, self.node.power_state)
644
 
        self.assertIsNotNone(self.node.last_error)
645
 
        self.assertFalse(os.path.exists(token_path))
646
 
        mock_image_cache.assert_called_once_with()
647
 
        mock_image_cache.return_value.clean_up.assert_called_once_with()
648
 
 
649
 
    @mock.patch.object(iscsi_deploy, 'InstanceImageCache')
650
 
    def test_continue_deploy_ramdisk_fails(self, mock_image_cache):
651
 
        token_path = self._create_token_file()
652
 
        self.node.power_state = states.POWER_ON
653
 
        self.node.provision_state = states.DEPLOYWAIT
654
 
        self.node.target_provision_state = states.ACTIVE
655
 
        self.node.save()
656
 
 
657
 
        def fake_deploy(**kwargs):
658
 
            pass
659
 
 
660
 
        self.useFixture(fixtures.MonkeyPatch(
661
 
                'ironic.drivers.modules.deploy_utils.deploy',
662
 
                fake_deploy))
663
 
 
664
 
        with task_manager.acquire(self.context, self.node.uuid) as task:
665
 
            task.driver.vendor._continue_deploy(
666
 
                    task, address='123456', iqn='aaa-bbb',
667
 
                    key='fake-56789', error='test ramdisk error')
668
 
 
669
 
        self.node.refresh()
670
 
        self.assertEqual(states.DEPLOYFAIL, self.node.provision_state)
671
 
        self.assertEqual(states.ACTIVE, self.node.target_provision_state)
672
 
        self.assertEqual(states.POWER_OFF, self.node.power_state)
673
 
        self.assertIsNotNone(self.node.last_error)
674
 
        self.assertFalse(os.path.exists(token_path))
675
 
        mock_image_cache.assert_called_once_with()
676
 
        mock_image_cache.return_value.clean_up.assert_called_once_with()
677
 
 
678
 
    def test_continue_deploy_invalid(self):
 
856
        mock_deploy.return_value = {'root uuid': root_uuid}
 
857
        boot_mode = None
 
858
        is_whole_disk_image = False
 
859
 
 
860
        with task_manager.acquire(self.context, self.node.uuid) as task:
 
861
            task.driver.vendor.pass_deploy_info(
 
862
                    task, address='123456', iqn='aaa-bbb', key='fake-56789')
 
863
 
 
864
        self.node.refresh()
 
865
        self.assertEqual(states.ACTIVE, self.node.provision_state)
 
866
        self.assertEqual(states.NOSTATE, self.node.target_provision_state)
 
867
        self.assertEqual(states.POWER_ON, self.node.power_state)
 
868
        self.assertIn('root_uuid_or_disk_id', self.node.driver_internal_info)
 
869
        self.assertIsNone(self.node.last_error)
 
870
        self.assertFalse(os.path.exists(token_path))
 
871
        mock_image_cache.assert_called_once_with()
 
872
        mock_image_cache.return_value.clean_up.assert_called_once_with()
 
873
        pxe_config_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
 
874
        notify_mock.assert_called_once_with('123456')
 
875
        if is_localboot:
 
876
            mock_node_boot_dev.assert_called_once_with(
 
877
                mock.ANY, boot_devices.DISK, persistent=True)
 
878
            mock_clean_pxe.assert_called_once_with(mock.ANY)
 
879
            self.assertFalse(mock_switch_config.called)
 
880
        else:
 
881
            mock_switch_config.assert_called_once_with(pxe_config_path,
 
882
                                                       root_uuid,
 
883
                                                       boot_mode,
 
884
                                                       is_whole_disk_image)
 
885
            self.assertFalse(mock_node_boot_dev.called)
 
886
            self.assertFalse(mock_clean_pxe.called)
 
887
 
 
888
    @mock.patch.object(pxe_utils, 'clean_up_pxe_config')
 
889
    @mock.patch.object(manager_utils, 'node_set_boot_device')
 
890
    @mock.patch.object(deploy_utils, 'notify_deploy_complete')
 
891
    @mock.patch.object(deploy_utils, 'switch_pxe_config')
 
892
    @mock.patch.object(iscsi_deploy, 'InstanceImageCache')
 
893
    @mock.patch.object(deploy_utils, 'deploy_disk_image')
 
894
    def _test_pass_deploy_info_whole_disk_image(self, is_localboot,
 
895
                                                mock_deploy,
 
896
                                                mock_image_cache,
 
897
                                                mock_switch_config,
 
898
                                                notify_mock,
 
899
                                                mock_node_boot_dev,
 
900
                                                mock_clean_pxe):
 
901
        token_path = self._create_token_file()
 
902
 
 
903
        # set local boot
 
904
        if is_localboot:
 
905
            i_info = self.node.instance_info
 
906
            i_info['capabilities'] = '{"boot_option": "local"}'
 
907
            self.node.instance_info = i_info
 
908
 
 
909
        self.node.power_state = states.POWER_ON
 
910
        self.node.provision_state = states.DEPLOYWAIT
 
911
        self.node.target_provision_state = states.ACTIVE
 
912
        self.node.save()
 
913
 
 
914
        boot_mode = None
 
915
        is_whole_disk_image = True
 
916
        disk_id = '0x12345678'
 
917
        mock_deploy.return_value = {'disk identifier': disk_id}
 
918
 
 
919
        with task_manager.acquire(self.context, self.node.uuid) as task:
 
920
            task.node.driver_internal_info['is_whole_disk_image'] = True
 
921
            task.driver.vendor.pass_deploy_info(task, address='123456',
 
922
                                                iqn='aaa-bbb',
 
923
                                                key='fake-56789')
 
924
 
 
925
        self.node.refresh()
 
926
        self.assertEqual(states.ACTIVE, self.node.provision_state)
 
927
        self.assertEqual(states.NOSTATE, self.node.target_provision_state)
 
928
        self.assertEqual(states.POWER_ON, self.node.power_state)
 
929
        self.assertIsNone(self.node.last_error)
 
930
        self.assertFalse(os.path.exists(token_path))
 
931
        mock_image_cache.assert_called_once_with()
 
932
        mock_image_cache.return_value.clean_up.assert_called_once_with()
 
933
        pxe_config_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
 
934
        notify_mock.assert_called_once_with('123456')
 
935
        if is_localboot:
 
936
            mock_node_boot_dev.assert_called_once_with(
 
937
                mock.ANY, boot_devices.DISK, persistent=True)
 
938
            mock_clean_pxe.assert_called_once_with(mock.ANY)
 
939
            self.assertFalse(mock_switch_config.called)
 
940
        else:
 
941
            mock_switch_config.assert_called_once_with(pxe_config_path,
 
942
                                                       disk_id,
 
943
                                                       boot_mode,
 
944
                                                       is_whole_disk_image)
 
945
            self.assertFalse(mock_node_boot_dev.called)
 
946
            self.assertFalse(mock_clean_pxe.called)
 
947
 
 
948
    def test_pass_deploy_info_deploy(self):
 
949
        self._test_pass_deploy_info_deploy(False)
 
950
 
 
951
    def test_pass_deploy_info_localboot(self):
 
952
        self._test_pass_deploy_info_deploy(True)
 
953
 
 
954
    def test_pass_deploy_info_whole_disk_image(self):
 
955
        self._test_pass_deploy_info_whole_disk_image(False)
 
956
 
 
957
    def test_pass_deploy_info_whole_disk_image_localboot(self):
 
958
        self._test_pass_deploy_info_whole_disk_image(True)
 
959
 
 
960
    def test_pass_deploy_info_invalid(self):
679
961
        self.node.power_state = states.POWER_ON
680
962
        self.node.provision_state = states.AVAILABLE
681
963
        self.node.target_provision_state = states.NOSTATE
683
965
 
684
966
        with task_manager.acquire(self.context, self.node.uuid) as task:
685
967
            self.assertRaises(exception.InvalidState,
686
 
                    task.driver.vendor._continue_deploy,
 
968
                    task.driver.vendor.pass_deploy_info,
687
969
                    task, address='123456', iqn='aaa-bbb',
688
970
                    key='fake-56789', error='test ramdisk error')
689
971
 
695
977
    def test_lock_elevated(self):
696
978
        with task_manager.acquire(self.context, self.node.uuid) as task:
697
979
            with mock.patch.object(task.driver.vendor,
698
 
                                   '_continue_deploy') as _cont_deploy_mock:
699
 
                task.driver.vendor._continue_deploy(
 
980
                                   'pass_deploy_info') as _cont_deploy_mock:
 
981
                task.driver.vendor.pass_deploy_info(
700
982
                    task, address='123456', iqn='aaa-bbb', key='fake-56789')
701
983
 
702
984
                # lock elevated w/o exception
703
985
                self.assertEqual(1, _cont_deploy_mock.call_count,
704
 
                            "_continue_deploy was not called once.")
 
986
                            "pass_deploy_info was not called once.")
705
987
 
706
988
    def test_vendor_routes(self):
707
 
        expected = ['pass_deploy_info']
 
989
        expected = ['heartbeat', 'pass_deploy_info']
708
990
        with task_manager.acquire(self.context, self.node.uuid,
709
991
                                  shared=True) as task:
710
992
            vendor_routes = task.driver.vendor.vendor_routes
711
993
            self.assertIsInstance(vendor_routes, dict)
712
 
            self.assertEqual(expected, list(vendor_routes))
 
994
            self.assertEqual(sorted(expected), sorted(list(vendor_routes)))
713
995
 
714
996
    def test_driver_routes(self):
 
997
        expected = ['lookup']
715
998
        with task_manager.acquire(self.context, self.node.uuid,
716
999
                                  shared=True) as task:
717
1000
            driver_routes = task.driver.vendor.driver_routes
718
1001
            self.assertIsInstance(driver_routes, dict)
719
 
            self.assertEqual({}, driver_routes)
 
1002
            self.assertEqual(sorted(expected), sorted(list(driver_routes)))
720
1003
 
721
1004
 
722
1005
@mock.patch.object(utils, 'unlink_without_raise')
730
1013
        mgr_utils.mock_the_extension_manager(driver="fake_pxe")
731
1014
        instance_info = INST_INFO_DICT
732
1015
        instance_info['deploy_key'] = 'fake-56789'
733
 
        self.node = obj_utils.create_test_node(self.context,
734
 
                                               driver='fake_pxe',
735
 
                                               instance_info=instance_info,
736
 
                                               driver_info=DRV_INFO_DICT)
 
1016
        self.node = obj_utils.create_test_node(
 
1017
                        self.context, driver='fake_pxe',
 
1018
                        instance_info=instance_info,
 
1019
                        driver_info=DRV_INFO_DICT,
 
1020
                        driver_internal_info=DRV_INTERNAL_INFO_DICT,
 
1021
                    )
737
1022
 
738
1023
    def test_clean_up(self, mock_image_info, mock_cache, mock_pxe_clean,
739
1024
                      mock_iscsi_clean, mock_unlink):
775
1060
        mgr_utils.mock_the_extension_manager(driver="fake_pxe")
776
1061
        instance_info = INST_INFO_DICT
777
1062
        instance_info['deploy_key'] = 'fake-56789'
778
 
        self.node = obj_utils.create_test_node(self.context,
779
 
                                               driver='fake_pxe',
780
 
                                               instance_info=instance_info,
781
 
                                               driver_info=DRV_INFO_DICT)
 
1063
        self.node = obj_utils.create_test_node(
 
1064
                        self.context, driver='fake_pxe',
 
1065
                        instance_info=instance_info,
 
1066
                        driver_info=DRV_INFO_DICT,
 
1067
                        driver_internal_info=DRV_INTERNAL_INFO_DICT,
 
1068
                    )
782
1069
        self.port = obj_utils.create_test_port(self.context,
783
1070
                                               node_id=self.node.id)
784
1071
 
845
1132
                     + self.files):
846
1133
            self.assertFalse(os.path.exists(path),
847
1134
                             '%s is not expected to exist' % path)
 
1135
 
 
1136
 
 
1137
class TestAgentVendorPassthru(db_base.DbTestCase):
 
1138
 
 
1139
    def setUp(self):
 
1140
        super(TestAgentVendorPassthru, self).setUp()
 
1141
        mgr_utils.mock_the_extension_manager()
 
1142
        self.driver = driver_factory.get_driver("fake")
 
1143
        self.driver.vendor = pxe.VendorPassthru()
 
1144
        self.node = obj_utils.create_test_node(
 
1145
                        self.context, driver='fake',
 
1146
                        instance_info=INST_INFO_DICT,
 
1147
                        driver_info=DRV_INFO_DICT,
 
1148
                        driver_internal_info=DRV_INTERNAL_INFO_DICT,
 
1149
                    )
 
1150
        self.node.driver_internal_info['agent_url'] = 'http://1.2.3.4:1234'
 
1151
        self.task = mock.Mock(spec=task_manager.TaskManager)
 
1152
        self.task.shared = False
 
1153
        self.task.node = self.node
 
1154
        self.task.driver = self.driver
 
1155
        self.task.context = self.context
 
1156
 
 
1157
    @mock.patch.object(agent_base_vendor.BaseAgentVendor,
 
1158
                       'reboot_and_finish_deploy')
 
1159
    @mock.patch.object(deploy_utils, 'switch_pxe_config')
 
1160
    @mock.patch.object(iscsi_deploy, 'do_agent_iscsi_deploy')
 
1161
    @mock.patch.object(pxe, '_destroy_token_file')
 
1162
    def test_continue_deploy_netboot(self, destroy_token_file_mock,
 
1163
                                     do_agent_iscsi_deploy_mock,
 
1164
                                     switch_pxe_config_mock,
 
1165
                                     reboot_and_finish_deploy_mock):
 
1166
 
 
1167
        uuid_dict_returned = {'root uuid': 'some-root-uuid'}
 
1168
        do_agent_iscsi_deploy_mock.return_value = uuid_dict_returned
 
1169
        self.driver.vendor.continue_deploy(self.task)
 
1170
        destroy_token_file_mock.assert_called_once_with(self.node)
 
1171
        do_agent_iscsi_deploy_mock.assert_called_once_with(
 
1172
            self.task, self.driver.vendor._client)
 
1173
        tftp_config = '/tftpboot/%s/config' % self.node.uuid
 
1174
        switch_pxe_config_mock.assert_called_once_with(tftp_config,
 
1175
                                                       'some-root-uuid',
 
1176
                                                       None, False)
 
1177
        reboot_and_finish_deploy_mock.assert_called_once_with(self.task)
 
1178
 
 
1179
    @mock.patch.object(agent_base_vendor.BaseAgentVendor,
 
1180
                       'reboot_and_finish_deploy')
 
1181
    @mock.patch.object(pxe_utils, 'clean_up_pxe_config')
 
1182
    @mock.patch.object(agent_base_vendor.BaseAgentVendor,
 
1183
                       'configure_local_boot')
 
1184
    @mock.patch.object(iscsi_deploy, 'do_agent_iscsi_deploy')
 
1185
    @mock.patch.object(pxe, '_destroy_token_file')
 
1186
    def test_continue_deploy_localboot(self, destroy_token_file_mock,
 
1187
                                       do_agent_iscsi_deploy_mock,
 
1188
                                       configure_local_boot_mock,
 
1189
                                       clean_up_pxe_config_mock,
 
1190
                                       reboot_and_finish_deploy_mock):
 
1191
 
 
1192
        self.node.instance_info = {
 
1193
            'capabilities': {'boot_option': 'local'}}
 
1194
        self.node.save()
 
1195
        uuid_dict_returned = {'root uuid': 'some-root-uuid'}
 
1196
        do_agent_iscsi_deploy_mock.return_value = uuid_dict_returned
 
1197
 
 
1198
        self.driver.vendor.continue_deploy(self.task)
 
1199
        destroy_token_file_mock.assert_called_once_with(self.node)
 
1200
        do_agent_iscsi_deploy_mock.assert_called_once_with(
 
1201
            self.task, self.driver.vendor._client)
 
1202
        configure_local_boot_mock.assert_called_once_with(
 
1203
            self.task, root_uuid='some-root-uuid', efi_system_part_uuid=None)
 
1204
        clean_up_pxe_config_mock.assert_called_once_with(self.task)
 
1205
        reboot_and_finish_deploy_mock.assert_called_once_with(self.task)
 
1206
 
 
1207
    @mock.patch.object(agent_base_vendor.BaseAgentVendor,
 
1208
                       'reboot_and_finish_deploy')
 
1209
    @mock.patch.object(pxe_utils, 'clean_up_pxe_config')
 
1210
    @mock.patch.object(agent_base_vendor.BaseAgentVendor,
 
1211
                       'configure_local_boot')
 
1212
    @mock.patch.object(iscsi_deploy, 'do_agent_iscsi_deploy')
 
1213
    @mock.patch.object(pxe, '_destroy_token_file')
 
1214
    def test_continue_deploy_localboot_uefi(self, destroy_token_file_mock,
 
1215
                                            do_agent_iscsi_deploy_mock,
 
1216
                                            configure_local_boot_mock,
 
1217
                                            clean_up_pxe_config_mock,
 
1218
                                            reboot_and_finish_deploy_mock):
 
1219
 
 
1220
        self.node.instance_info = {
 
1221
            'capabilities': {'boot_option': 'local'}}
 
1222
        self.node.save()
 
1223
        uuid_dict_returned = {'root uuid': 'some-root-uuid',
 
1224
            'efi system partition uuid': 'efi-part-uuid'}
 
1225
        do_agent_iscsi_deploy_mock.return_value = uuid_dict_returned
 
1226
 
 
1227
        self.driver.vendor.continue_deploy(self.task)
 
1228
        destroy_token_file_mock.assert_called_once_with(self.node)
 
1229
        do_agent_iscsi_deploy_mock.assert_called_once_with(
 
1230
            self.task, self.driver.vendor._client)
 
1231
        configure_local_boot_mock.assert_called_once_with(
 
1232
            self.task, root_uuid='some-root-uuid',
 
1233
            efi_system_part_uuid='efi-part-uuid')
 
1234
        clean_up_pxe_config_mock.assert_called_once_with(self.task)
 
1235
        reboot_and_finish_deploy_mock.assert_called_once_with(self.task)