From 714a7cfda97c6e1ea1fe08a58ddec23e96dfd5bc Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Thu, 8 Sep 2016 16:57:34 +0200 Subject: [PATCH] DRAC: list unfinished jobs After set_bios_config is invoked, there is no way to check the progress of the remote job on the DRAC card. list_unfinished_jobs on the vendor-passthru interface provides a way to check its status. Change-Id: I963504dbbec36e44312ccbf4455c45d6ec60908b Closes-Bug: #1621515 --- ironic/drivers/drac.py | 1 + .../drivers/modules/drac/vendor_passthru.py | 19 ++++++++ .../unit/drivers/modules/drac/test_job.py | 45 +++++++++++++++++++ ...list-unfinished-jobs-10400419b6bc3c6e.yaml | 6 +++ 4 files changed, 71 insertions(+) create mode 100644 releasenotes/notes/drac-list-unfinished-jobs-10400419b6bc3c6e.yaml diff --git a/ironic/drivers/drac.py b/ironic/drivers/drac.py index a8f1275433..a102ef20cd 100644 --- a/ironic/drivers/drac.py +++ b/ironic/drivers/drac.py @@ -53,6 +53,7 @@ class PXEDracDriver(base.BaseDriver): 'set_bios_config': self.drac_vendor, 'commit_bios_config': self.drac_vendor, 'abandon_bios_config': self.drac_vendor, + 'list_unfinished_jobs': self.drac_vendor, } self.driver_passthru_mapping = {'lookup': self.iscsi_vendor} self.vendor = utils.MixinVendorInterface(self.mapping, diff --git a/ironic/drivers/modules/drac/vendor_passthru.py b/ironic/drivers/modules/drac/vendor_passthru.py index 3fd8cbe683..4337d5318e 100644 --- a/ironic/drivers/modules/drac/vendor_passthru.py +++ b/ironic/drivers/modules/drac/vendor_passthru.py @@ -19,6 +19,7 @@ from ironic.conductor import task_manager from ironic.drivers import base from ironic.drivers.modules.drac import bios as drac_bios from ironic.drivers.modules.drac import common as drac_common +from ironic.drivers.modules.drac import job as drac_job class DracVendorPassthru(base.VendorInterface): @@ -111,3 +112,21 @@ class DracVendorPassthru(base.VendorInterface): :raises: DracOperationError on an error from python-dracclient. """ drac_bios.abandon_config(task) + + @base.passthru(['GET'], async=False, + description=('List unfinished config jobs of the node. ' + 'Required argument: a TaskManager instance ' + 'containing the node to act on.')) + def list_unfinished_jobs(self, task, **kwargs): + """List unfinished config jobs of the node. + + :param task: a TaskManager instance containing the node to act on. + :param kwargs: not used. + :returns: a dictionary containing the ``unfinished_jobs`` key; this key + points to a list of dicts, with each dict representing a Job + object. + :raises: DracOperationError on an error from python-dracclient. + """ + jobs = drac_job.list_unfinished_jobs(task.node) + # FIXME(mgould) Do this without calling private methods. + return {'unfinished_jobs': [job._asdict() for job in jobs]} diff --git a/ironic/tests/unit/drivers/modules/drac/test_job.py b/ironic/tests/unit/drivers/modules/drac/test_job.py index 789d2938c3..a566926553 100644 --- a/ironic/tests/unit/drivers/modules/drac/test_job.py +++ b/ironic/tests/unit/drivers/modules/drac/test_job.py @@ -19,6 +19,7 @@ from dracclient import exceptions as drac_exceptions import mock from ironic.common import exception +from ironic.conductor import task_manager from ironic.drivers.modules.drac import common as drac_common from ironic.drivers.modules.drac import job as drac_job from ironic.tests.unit.conductor import mgr_utils @@ -113,3 +114,47 @@ class DracJobTestCase(db_base.DbTestCase): self.assertRaises(exception.DracOperationError, drac_job.validate_job_queue, self.node) + + +@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, + autospec=True) +class DracVendorPassthruJobTestCase(db_base.DbTestCase): + + def setUp(self): + super(DracVendorPassthruJobTestCase, self).setUp() + mgr_utils.mock_the_extension_manager(driver='fake_drac') + self.node = obj_utils.create_test_node(self.context, + driver='fake_drac', + driver_info=INFO_DICT) + self.job_dict = { + 'id': 'JID_001436912645', + 'name': 'ConfigBIOS:BIOS.Setup.1-1', + 'start_time': '00000101000000', + 'until_time': 'TIME_NA', + 'message': 'Job in progress', + 'state': 'Running', + 'percent_complete': 34} + self.job = test_utils.dict_to_namedtuple(values=self.job_dict) + + def test_list_unfinished_jobs(self, mock_get_drac_client): + mock_client = mock.Mock() + mock_get_drac_client.return_value = mock_client + mock_client.list_jobs.return_value = [self.job] + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + resp = task.driver.vendor.list_unfinished_jobs(task) + + mock_client.list_jobs.assert_called_once_with(only_unfinished=True) + self.assertEqual([self.job_dict], resp['unfinished_jobs']) + + def test_list_unfinished_jobs_fail(self, mock_get_drac_client): + mock_client = mock.Mock() + mock_get_drac_client.return_value = mock_client + exc = exception.DracOperationError('boom') + mock_client.list_jobs.side_effect = exc + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.assertRaises(exception.DracOperationError, + task.driver.vendor.list_unfinished_jobs, task) diff --git a/releasenotes/notes/drac-list-unfinished-jobs-10400419b6bc3c6e.yaml b/releasenotes/notes/drac-list-unfinished-jobs-10400419b6bc3c6e.yaml new file mode 100644 index 0000000000..f446d515f3 --- /dev/null +++ b/releasenotes/notes/drac-list-unfinished-jobs-10400419b6bc3c6e.yaml @@ -0,0 +1,6 @@ +--- +features: + - Adds ``list_unfinished_jobs`` method to the vendor-passthru interface of + the DRAC driver. It provides a way to check the status of the remote config + job after a BIOS configuration change was submitted using the + ``set_bios_config`` method.