Add job management
Change-Id: Id5c8462c9f797f5d506fcacab5736a7768dbd99d
This commit is contained in:
parent
9153b850b2
commit
400f9317ba
@ -19,6 +19,7 @@ import logging
|
|||||||
|
|
||||||
from dracclient import exceptions
|
from dracclient import exceptions
|
||||||
from dracclient.resources import bios
|
from dracclient.resources import bios
|
||||||
|
from dracclient.resources import job
|
||||||
from dracclient import utils
|
from dracclient import utils
|
||||||
from dracclient import wsman
|
from dracclient import wsman
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ class DRACClient(object):
|
|||||||
"""
|
"""
|
||||||
self.client = WSManClient(host, username, password, port, path,
|
self.client = WSManClient(host, username, password, port, path,
|
||||||
protocol)
|
protocol)
|
||||||
|
self._job_mgmt = job.JobManagement(self.client)
|
||||||
self._power_mgmt = bios.PowerManagement(self.client)
|
self._power_mgmt = bios.PowerManagement(self.client)
|
||||||
|
|
||||||
def get_power_state(self):
|
def get_power_state(self):
|
||||||
@ -69,6 +71,98 @@ class DRACClient(object):
|
|||||||
"""
|
"""
|
||||||
self._power_mgmt.set_power_state(target_state)
|
self._power_mgmt.set_power_state(target_state)
|
||||||
|
|
||||||
|
def list_jobs(self, only_unfinished=False):
|
||||||
|
"""Returns a list of jobs from the job queue
|
||||||
|
|
||||||
|
:param only_unfinished: indicates whether only unfinished jobs should
|
||||||
|
be returned
|
||||||
|
:returns: a list of Job objects
|
||||||
|
:raises: WSManRequestFailure on request failures
|
||||||
|
:raises: WSManInvalidResponse when receiving invalid response
|
||||||
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||||
|
interface
|
||||||
|
"""
|
||||||
|
return self._job_mgmt.list_jobs(only_unfinished)
|
||||||
|
|
||||||
|
def get_job(self, job_id):
|
||||||
|
"""Returns a job from the job queue
|
||||||
|
|
||||||
|
:param job_id: id of the job
|
||||||
|
:returns: a Job object on successful query, None otherwise
|
||||||
|
:raises: WSManRequestFailure on request failures
|
||||||
|
:raises: WSManInvalidResponse when receiving invalid response
|
||||||
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||||
|
interface
|
||||||
|
"""
|
||||||
|
return self._job_mgmt.get_job(job_id)
|
||||||
|
|
||||||
|
def create_config_job(self, resource_uri, cim_creation_class_name,
|
||||||
|
cim_name, target,
|
||||||
|
cim_system_creation_class_name='DCIM_ComputerSystem',
|
||||||
|
cim_system_name='DCIM:ComputerSystem',
|
||||||
|
reboot=False):
|
||||||
|
"""Creates a config job
|
||||||
|
|
||||||
|
In CIM (Common Information Model), weak association is used to name an
|
||||||
|
instance of one class in the context of an instance of another class.
|
||||||
|
SystemName and SystemCreationClassName are the attributes of the
|
||||||
|
scoping system, while Name and CreationClassName are the attributes of
|
||||||
|
the instance of the class, on which the CreateTargetedConfigJob method
|
||||||
|
is invoked.
|
||||||
|
|
||||||
|
:param: resource_uri: URI of resource to invoke
|
||||||
|
:param: cim_creation_class_name: creation class name of the CIM object
|
||||||
|
:param: cim_name: name of the CIM object
|
||||||
|
:param: target: target device
|
||||||
|
:param: cim_system_creation_class_name: creation class name of the
|
||||||
|
scoping system
|
||||||
|
:param: cim_system_name: name of the scoping system
|
||||||
|
:param: reboot: indicates whether a RebootJob should be also be
|
||||||
|
created or not
|
||||||
|
:returns: id of the created job
|
||||||
|
:raises: WSManRequestFailure on request failures
|
||||||
|
:raises: WSManInvalidResponse when receiving invalid response
|
||||||
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||||
|
interface
|
||||||
|
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||||
|
"""
|
||||||
|
return self._job_mgmt.create_config_job(
|
||||||
|
resource_uri, cim_creation_class_name, cim_name, target,
|
||||||
|
cim_system_creation_class_name, cim_system_name, reboot)
|
||||||
|
|
||||||
|
def delete_pending_config(
|
||||||
|
self, resource_uri, cim_creation_class_name, cim_name, target,
|
||||||
|
cim_system_creation_class_name='DCIM_ComputerSystem',
|
||||||
|
cim_system_name='DCIM:ComputerSystem'):
|
||||||
|
"""Cancels pending configuration
|
||||||
|
|
||||||
|
Configuration can only be canceled until a config job hasn't been
|
||||||
|
submitted.
|
||||||
|
|
||||||
|
In CIM (Common Information Model), weak association is used to name an
|
||||||
|
instance of one class in the context of an instance of another class.
|
||||||
|
SystemName and SystemCreationClassName are the attributes of the
|
||||||
|
scoping system, while Name and CreationClassName are the attributes of
|
||||||
|
the instance of the class, on which the CreateTargetedConfigJob method
|
||||||
|
is invoked.
|
||||||
|
|
||||||
|
:param: resource_uri: URI of resource to invoke
|
||||||
|
:param: cim_creation_class_name: creation class name of the CIM object
|
||||||
|
:param: cim_name: name of the CIM object
|
||||||
|
:param: target: target device
|
||||||
|
:param: cim_system_creation_class_name: creation class name of the
|
||||||
|
scoping system
|
||||||
|
:param: cim_system_name: name of the scoping system
|
||||||
|
:raises: WSManRequestFailure on request failures
|
||||||
|
:raises: WSManInvalidResponse when receiving invalid response
|
||||||
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||||
|
interface
|
||||||
|
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||||
|
"""
|
||||||
|
self._job_mgmt.delete_pending_config(
|
||||||
|
resource_uri, cim_creation_class_name, cim_name, target,
|
||||||
|
cim_system_creation_class_name, cim_system_name)
|
||||||
|
|
||||||
|
|
||||||
class WSManClient(wsman.Client):
|
class WSManClient(wsman.Client):
|
||||||
"""Wrapper for wsman.Client with return value checking"""
|
"""Wrapper for wsman.Client with return value checking"""
|
||||||
|
192
dracclient/resources/job.py
Normal file
192
dracclient/resources/job.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import collections
|
||||||
|
|
||||||
|
from dracclient.resources import uris
|
||||||
|
from dracclient import utils
|
||||||
|
from dracclient import wsman
|
||||||
|
|
||||||
|
Job = collections.namedtuple('Job', ['id', 'name', 'start_time', 'until_time',
|
||||||
|
'message', 'state', 'percent_complete'])
|
||||||
|
|
||||||
|
|
||||||
|
class JobManagement(object):
|
||||||
|
|
||||||
|
def __init__(self, client):
|
||||||
|
"""Creates JobManagement object
|
||||||
|
|
||||||
|
:param client: an instance of WSManClient
|
||||||
|
"""
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
def list_jobs(self, only_unfinished=False):
|
||||||
|
"""Returns a list of jobs from the job queue
|
||||||
|
|
||||||
|
:param only_unfinished: indicates whether only unfinished jobs should
|
||||||
|
be returned
|
||||||
|
:returns: a list of Job objects
|
||||||
|
:raises: WSManRequestFailure on request failures
|
||||||
|
:raises: WSManInvalidResponse when receiving invalid response
|
||||||
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||||
|
interface
|
||||||
|
"""
|
||||||
|
|
||||||
|
filter_query = None
|
||||||
|
if only_unfinished:
|
||||||
|
filter_query = ('select * from DCIM_LifecycleJob '
|
||||||
|
'where Name != "CLEARALL" and '
|
||||||
|
'JobStatus != "Reboot Completed" and '
|
||||||
|
'JobStatus != "Completed" and '
|
||||||
|
'JobStatus != "Completed with Errors" and '
|
||||||
|
'JobStatus != "Failed"')
|
||||||
|
|
||||||
|
doc = self.client.enumerate(uris.DCIM_LifecycleJob,
|
||||||
|
filter_query=filter_query)
|
||||||
|
|
||||||
|
drac_jobs = utils.find_xml(doc, 'DCIM_LifecycleJob',
|
||||||
|
uris.DCIM_LifecycleJob, find_all=True)
|
||||||
|
|
||||||
|
return [self._parse_drac_job(drac_job) for drac_job in drac_jobs]
|
||||||
|
|
||||||
|
def get_job(self, job_id):
|
||||||
|
"""Returns a job from the job queue
|
||||||
|
|
||||||
|
:param job_id: id of the job
|
||||||
|
:returns: a Job object on successful query, None otherwise
|
||||||
|
:raises: WSManRequestFailure on request failures
|
||||||
|
:raises: WSManInvalidResponse when receiving invalid response
|
||||||
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||||
|
interface
|
||||||
|
"""
|
||||||
|
|
||||||
|
filter_query = ('select * from DCIM_LifecycleJob where InstanceID="%s"'
|
||||||
|
% job_id)
|
||||||
|
|
||||||
|
doc = self.client.enumerate(uris.DCIM_LifecycleJob,
|
||||||
|
filter_query=filter_query)
|
||||||
|
|
||||||
|
drac_job = utils.find_xml(doc, 'DCIM_LifecycleJob',
|
||||||
|
uris.DCIM_LifecycleJob)
|
||||||
|
|
||||||
|
if drac_job:
|
||||||
|
return self._parse_drac_job(drac_job)
|
||||||
|
|
||||||
|
def create_config_job(self, resource_uri, cim_creation_class_name,
|
||||||
|
cim_name, target,
|
||||||
|
cim_system_creation_class_name='DCIM_ComputerSystem',
|
||||||
|
cim_system_name='DCIM:ComputerSystem',
|
||||||
|
reboot=False):
|
||||||
|
"""Creates a config job
|
||||||
|
|
||||||
|
In CIM (Common Information Model), weak association is used to name an
|
||||||
|
instance of one class in the context of an instance of another class.
|
||||||
|
SystemName and SystemCreationClassName are the attributes of the
|
||||||
|
scoping system, while Name and CreationClassName are the attributes of
|
||||||
|
the instance of the class, on which the CreateTargetedConfigJob method
|
||||||
|
is invoked.
|
||||||
|
|
||||||
|
:param: resource_uri: URI of resource to invoke
|
||||||
|
:param: cim_creation_class_name: creation class name of the CIM object
|
||||||
|
:param: cim_name: name of the CIM object
|
||||||
|
:param: target: target device
|
||||||
|
:param: cim_system_creation_class_name: creation class name of the
|
||||||
|
scoping system
|
||||||
|
:param: cim_system_name: name of the scoping system
|
||||||
|
:param: reboot: indicates whether a RebootJob should be also be
|
||||||
|
created or not
|
||||||
|
:returns: id of the created job
|
||||||
|
:raises: WSManRequestFailure on request failures
|
||||||
|
:raises: WSManInvalidResponse when receiving invalid response
|
||||||
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||||
|
interface
|
||||||
|
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||||
|
"""
|
||||||
|
|
||||||
|
selectors = {'SystemCreationClassName': cim_system_creation_class_name,
|
||||||
|
'SystemName': cim_system_name,
|
||||||
|
'CreationClassName': cim_creation_class_name,
|
||||||
|
'Name': cim_name}
|
||||||
|
|
||||||
|
properties = {'Target': target,
|
||||||
|
'ScheduledStartTime': 'TIME_NOW'}
|
||||||
|
|
||||||
|
if reboot:
|
||||||
|
properties['RebootJobType'] = '3'
|
||||||
|
|
||||||
|
doc = self.client.invoke(resource_uri, 'CreateTargetedConfigJob',
|
||||||
|
selectors, properties,
|
||||||
|
expected_return_value=utils.RET_CREATED)
|
||||||
|
|
||||||
|
query = ('.//{%(namespace)s}%(item)s[@%(attribute_name)s='
|
||||||
|
'"%(attribute_value)s"]' %
|
||||||
|
{'namespace': wsman.NS_WSMAN, 'item': 'Selector',
|
||||||
|
'attribute_name': 'Name',
|
||||||
|
'attribute_value': 'InstanceID'})
|
||||||
|
job_id = doc.find(query).text
|
||||||
|
return job_id
|
||||||
|
|
||||||
|
def delete_pending_config(
|
||||||
|
self, resource_uri, cim_creation_class_name, cim_name, target,
|
||||||
|
cim_system_creation_class_name='DCIM_ComputerSystem',
|
||||||
|
cim_system_name='DCIM:ComputerSystem'):
|
||||||
|
"""Cancels pending configuration
|
||||||
|
|
||||||
|
Configuration can only be canceled until a config job hasn't been
|
||||||
|
submitted.
|
||||||
|
|
||||||
|
In CIM (Common Information Model), weak association is used to name an
|
||||||
|
instance of one class in the context of an instance of another class.
|
||||||
|
SystemName and SystemCreationClassName are the attributes of the
|
||||||
|
scoping system, while Name and CreationClassName are the attributes of
|
||||||
|
the instance of the class, on which the CreateTargetedConfigJob method
|
||||||
|
is invoked.
|
||||||
|
|
||||||
|
:param: resource_uri: URI of resource to invoke
|
||||||
|
:param: cim_creation_class_name: creation class name of the CIM object
|
||||||
|
:param: cim_name: name of the CIM object
|
||||||
|
:param: target: target device
|
||||||
|
:param: cim_system_creation_class_name: creation class name of the
|
||||||
|
scoping system
|
||||||
|
:param: cim_system_name: name of the scoping system
|
||||||
|
:raises: WSManRequestFailure on request failures
|
||||||
|
:raises: WSManInvalidResponse when receiving invalid response
|
||||||
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||||
|
interface
|
||||||
|
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||||
|
"""
|
||||||
|
|
||||||
|
selectors = {'SystemCreationClassName': cim_system_creation_class_name,
|
||||||
|
'SystemName': cim_system_name,
|
||||||
|
'CreationClassName': cim_creation_class_name,
|
||||||
|
'Name': cim_name}
|
||||||
|
|
||||||
|
properties = {'Target': target}
|
||||||
|
|
||||||
|
self.client.invoke(resource_uri, 'DeletePendingConfiguration',
|
||||||
|
selectors, properties,
|
||||||
|
expected_return_value=utils.RET_SUCCESS)
|
||||||
|
|
||||||
|
def _parse_drac_job(self, drac_job):
|
||||||
|
return Job(id=self._get_job_attr(drac_job, 'InstanceID'),
|
||||||
|
name=self._get_job_attr(drac_job, 'Name'),
|
||||||
|
start_time=self._get_job_attr(drac_job, 'JobStartTime'),
|
||||||
|
until_time=self._get_job_attr(drac_job, 'JobUntilTime'),
|
||||||
|
message=self._get_job_attr(drac_job, 'Message'),
|
||||||
|
state=self._get_job_attr(drac_job, 'JobStatus'),
|
||||||
|
percent_complete=self._get_job_attr(drac_job,
|
||||||
|
'PercentComplete'))
|
||||||
|
|
||||||
|
def _get_job_attr(self, drac_job, attr_name):
|
||||||
|
return utils.get_wsman_resource_attr(drac_job, uris.DCIM_LifecycleJob,
|
||||||
|
attr_name)
|
@ -40,5 +40,8 @@ DCIM_ComputerSystem = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2'
|
|||||||
DCIM_LifecycleJob = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
|
DCIM_LifecycleJob = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
|
||||||
'DCIM_LifecycleJob')
|
'DCIM_LifecycleJob')
|
||||||
|
|
||||||
|
DCIM_RAIDService = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
|
||||||
|
'DCIM_RAIDService')
|
||||||
|
|
||||||
DCIM_SystemView = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
|
DCIM_SystemView = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
|
||||||
'DCIM_SystemView')
|
'DCIM_SystemView')
|
||||||
|
@ -11,13 +11,17 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import lxml.etree
|
||||||
|
import mock
|
||||||
import requests_mock
|
import requests_mock
|
||||||
|
|
||||||
import dracclient.client
|
import dracclient.client
|
||||||
from dracclient import exceptions
|
from dracclient import exceptions
|
||||||
|
import dracclient.resources.job
|
||||||
from dracclient.resources import uris
|
from dracclient.resources import uris
|
||||||
from dracclient.tests import base
|
from dracclient.tests import base
|
||||||
from dracclient.tests import utils as test_utils
|
from dracclient.tests import utils as test_utils
|
||||||
|
from dracclient import utils
|
||||||
|
|
||||||
|
|
||||||
@requests_mock.Mocker()
|
@requests_mock.Mocker()
|
||||||
@ -57,6 +61,185 @@ class ClientPowerManagementTestCase(base.BaseTest):
|
|||||||
self.drac_client.set_power_state, 'foo')
|
self.drac_client.set_power_state, 'foo')
|
||||||
|
|
||||||
|
|
||||||
|
class ClientJobManagementTestCase(base.BaseTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ClientJobManagementTestCase, self).setUp()
|
||||||
|
self.drac_client = dracclient.client.DRACClient(
|
||||||
|
**test_utils.FAKE_ENDPOINT)
|
||||||
|
|
||||||
|
@requests_mock.Mocker()
|
||||||
|
def test_list_jobs(self, mock_requests):
|
||||||
|
mock_requests.post(
|
||||||
|
'https://1.2.3.4:443/wsman',
|
||||||
|
text=test_utils.JobEnumerations[uris.DCIM_LifecycleJob]['ok'])
|
||||||
|
|
||||||
|
jobs = self.drac_client.list_jobs()
|
||||||
|
|
||||||
|
self.assertEqual(6, len(jobs))
|
||||||
|
|
||||||
|
@mock.patch.object(dracclient.client.WSManClient, 'enumerate',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def test_list_jobs_only_unfinished(self, mock_enumerate):
|
||||||
|
expected_filter_query = ('select * from DCIM_LifecycleJob '
|
||||||
|
'where Name != "CLEARALL" and '
|
||||||
|
'JobStatus != "Reboot Completed" and '
|
||||||
|
'JobStatus != "Completed" and '
|
||||||
|
'JobStatus != "Completed with Errors" and '
|
||||||
|
'JobStatus != "Failed"')
|
||||||
|
mock_enumerate.return_value = lxml.etree.fromstring(
|
||||||
|
test_utils.JobEnumerations[uris.DCIM_LifecycleJob]['ok'])
|
||||||
|
|
||||||
|
self.drac_client.list_jobs(only_unfinished=True)
|
||||||
|
|
||||||
|
mock_enumerate.assert_called_once_with(
|
||||||
|
mock.ANY, uris.DCIM_LifecycleJob,
|
||||||
|
filter_query=expected_filter_query)
|
||||||
|
|
||||||
|
@mock.patch.object(dracclient.client.WSManClient, 'enumerate',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def test_get_job(self, mock_enumerate):
|
||||||
|
expected_filter_query = ('select * from DCIM_LifecycleJob'
|
||||||
|
' where InstanceID="42"')
|
||||||
|
# NOTE: This is the first job in the xml. Filtering the job is the
|
||||||
|
# responsibility of the controller, so not testing it.
|
||||||
|
expected_job = dracclient.resources.job.Job(id='JID_CLEARALL',
|
||||||
|
name='CLEARALL',
|
||||||
|
start_time='TIME_NA',
|
||||||
|
until_time='TIME_NA',
|
||||||
|
message='NA',
|
||||||
|
state='Pending',
|
||||||
|
percent_complete='0')
|
||||||
|
mock_enumerate.return_value = lxml.etree.fromstring(
|
||||||
|
test_utils.JobEnumerations[uris.DCIM_LifecycleJob]['ok'])
|
||||||
|
|
||||||
|
job = self.drac_client.get_job(42)
|
||||||
|
|
||||||
|
mock_enumerate.assert_called_once_with(
|
||||||
|
mock.ANY, uris.DCIM_LifecycleJob,
|
||||||
|
filter_query=expected_filter_query)
|
||||||
|
self.assertEqual(expected_job, job)
|
||||||
|
|
||||||
|
@mock.patch.object(dracclient.client.WSManClient, 'enumerate',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def test_get_job_not_found(self, mock_enumerate):
|
||||||
|
expected_filter_query = ('select * from DCIM_LifecycleJob'
|
||||||
|
' where InstanceID="42"')
|
||||||
|
mock_enumerate.return_value = lxml.etree.fromstring(
|
||||||
|
test_utils.JobEnumerations[uris.DCIM_LifecycleJob]['not_found'])
|
||||||
|
|
||||||
|
job = self.drac_client.get_job(42)
|
||||||
|
|
||||||
|
mock_enumerate.assert_called_once_with(
|
||||||
|
mock.ANY, uris.DCIM_LifecycleJob,
|
||||||
|
filter_query=expected_filter_query)
|
||||||
|
self.assertIsNone(job)
|
||||||
|
|
||||||
|
@mock.patch.object(dracclient.client.WSManClient, 'invoke',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def test_create_config_job(self, mock_invoke):
|
||||||
|
cim_creation_class_name = 'DCIM_BIOSService'
|
||||||
|
cim_name = 'DCIM:BIOSService'
|
||||||
|
target = 'BIOS.Setup.1-1'
|
||||||
|
expected_selectors = {'CreationClassName': cim_creation_class_name,
|
||||||
|
'Name': cim_name,
|
||||||
|
'SystemCreationClassName': 'DCIM_ComputerSystem',
|
||||||
|
'SystemName': 'DCIM:ComputerSystem'}
|
||||||
|
expected_properties = {'Target': target,
|
||||||
|
'ScheduledStartTime': 'TIME_NOW'}
|
||||||
|
mock_invoke.return_value = lxml.etree.fromstring(
|
||||||
|
test_utils.JobInvocations[uris.DCIM_BIOSService][
|
||||||
|
'CreateTargetedConfigJob']['ok'])
|
||||||
|
|
||||||
|
job_id = self.drac_client.create_config_job(
|
||||||
|
uris.DCIM_BIOSService, cim_creation_class_name, cim_name, target)
|
||||||
|
|
||||||
|
mock_invoke.assert_called_once_with(
|
||||||
|
mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob',
|
||||||
|
expected_selectors, expected_properties,
|
||||||
|
expected_return_value=utils.RET_CREATED)
|
||||||
|
self.assertEqual('JID_442507917525', job_id)
|
||||||
|
|
||||||
|
@requests_mock.Mocker()
|
||||||
|
def test_create_config_job_failed(self, mock_requests):
|
||||||
|
cim_creation_class_name = 'DCIM_BIOSService'
|
||||||
|
cim_name = 'DCIM:BIOSService'
|
||||||
|
target = 'BIOS.Setup.1-1'
|
||||||
|
mock_requests.post(
|
||||||
|
'https://1.2.3.4:443/wsman',
|
||||||
|
text=test_utils.JobInvocations[uris.DCIM_BIOSService][
|
||||||
|
'CreateTargetedConfigJob']['error'])
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.DRACOperationFailed, self.drac_client.create_config_job,
|
||||||
|
uris.DCIM_BIOSService, cim_creation_class_name, cim_name, target)
|
||||||
|
|
||||||
|
@mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_create_config_job_with_reboot(self, mock_invoke):
|
||||||
|
cim_creation_class_name = 'DCIM_BIOSService'
|
||||||
|
cim_name = 'DCIM:BIOSService'
|
||||||
|
target = 'BIOS.Setup.1-1'
|
||||||
|
expected_selectors = {'CreationClassName': cim_creation_class_name,
|
||||||
|
'Name': cim_name,
|
||||||
|
'SystemCreationClassName': 'DCIM_ComputerSystem',
|
||||||
|
'SystemName': 'DCIM:ComputerSystem'}
|
||||||
|
expected_properties = {'Target': target,
|
||||||
|
'RebootJobType': '3',
|
||||||
|
'ScheduledStartTime': 'TIME_NOW'}
|
||||||
|
mock_invoke.return_value = lxml.etree.fromstring(
|
||||||
|
test_utils.JobInvocations[uris.DCIM_BIOSService][
|
||||||
|
'CreateTargetedConfigJob']['ok'])
|
||||||
|
|
||||||
|
job_id = self.drac_client.create_config_job(
|
||||||
|
uris.DCIM_BIOSService, cim_creation_class_name, cim_name, target,
|
||||||
|
reboot=True)
|
||||||
|
|
||||||
|
mock_invoke.assert_called_once_with(
|
||||||
|
mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob',
|
||||||
|
expected_selectors, expected_properties,
|
||||||
|
expected_return_value=utils.RET_CREATED)
|
||||||
|
self.assertEqual('JID_442507917525', job_id)
|
||||||
|
|
||||||
|
@mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_delete_pending_config(self, mock_invoke):
|
||||||
|
cim_creation_class_name = 'DCIM_BIOSService'
|
||||||
|
cim_name = 'DCIM:BIOSService'
|
||||||
|
target = 'BIOS.Setup.1-1'
|
||||||
|
expected_selectors = {'CreationClassName': cim_creation_class_name,
|
||||||
|
'Name': cim_name,
|
||||||
|
'SystemCreationClassName': 'DCIM_ComputerSystem',
|
||||||
|
'SystemName': 'DCIM:ComputerSystem'}
|
||||||
|
expected_properties = {'Target': target}
|
||||||
|
mock_invoke.return_value = lxml.etree.fromstring(
|
||||||
|
test_utils.JobInvocations[uris.DCIM_BIOSService][
|
||||||
|
'DeletePendingConfiguration']['ok'])
|
||||||
|
|
||||||
|
self.drac_client.delete_pending_config(
|
||||||
|
uris.DCIM_BIOSService, cim_creation_class_name, cim_name, target)
|
||||||
|
|
||||||
|
mock_invoke.assert_called_once_with(
|
||||||
|
mock.ANY, uris.DCIM_BIOSService, 'DeletePendingConfiguration',
|
||||||
|
expected_selectors, expected_properties,
|
||||||
|
expected_return_value=utils.RET_SUCCESS)
|
||||||
|
|
||||||
|
@requests_mock.Mocker()
|
||||||
|
def test_delete_pending_config_failed(self, mock_requests):
|
||||||
|
cim_creation_class_name = 'DCIM_BIOSService'
|
||||||
|
cim_name = 'DCIM:BIOSService'
|
||||||
|
target = 'BIOS.Setup.1-1'
|
||||||
|
mock_requests.post(
|
||||||
|
'https://1.2.3.4:443/wsman',
|
||||||
|
text=test_utils.JobInvocations[uris.DCIM_BIOSService][
|
||||||
|
'DeletePendingConfiguration']['error'])
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.DRACOperationFailed,
|
||||||
|
self.drac_client.delete_pending_config, uris.DCIM_BIOSService,
|
||||||
|
cim_creation_class_name, cim_name, target)
|
||||||
|
|
||||||
|
|
||||||
@requests_mock.Mocker()
|
@requests_mock.Mocker()
|
||||||
class WSManClientTestCase(base.BaseTest):
|
class WSManClientTestCase(base.BaseTest):
|
||||||
|
|
||||||
|
@ -59,3 +59,27 @@ BIOSInvocations = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JobEnumerations = {
|
||||||
|
uris.DCIM_LifecycleJob: {
|
||||||
|
'ok': load_wsman_xml('lifecycle_job-enum-ok'),
|
||||||
|
'not_found': load_wsman_xml('lifecycle_job-enum-not_found'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
JobInvocations = {
|
||||||
|
uris.DCIM_BIOSService: {
|
||||||
|
'CreateTargetedConfigJob': {
|
||||||
|
'ok': load_wsman_xml(
|
||||||
|
'bios_service-invoke-create_targeted_config_job-ok'),
|
||||||
|
'error': load_wsman_xml(
|
||||||
|
'bios_service-invoke-create_targeted_config_job-error'),
|
||||||
|
},
|
||||||
|
'DeletePendingConfiguration': {
|
||||||
|
'ok': load_wsman_xml(
|
||||||
|
'bios_service-invoke-delete_pending_configuration-ok'),
|
||||||
|
'error': load_wsman_xml(
|
||||||
|
'bios_service-invoke-delete_pending_configuration-error'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
<s:Envelope xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_BIOSService"
|
||||||
|
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
|
||||||
|
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
|
||||||
|
<s:Header>
|
||||||
|
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
|
||||||
|
<wsa:Action>http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_BIOSService/CreateTargetedConfigJobResponse</wsa:Action>
|
||||||
|
<wsa:RelatesTo>uuid:80cf5e1b-b109-4ef5-87c8-5b03ce6ba117</wsa:RelatesTo>
|
||||||
|
<wsa:MessageID>uuid:e57fa514-2189-1189-8ec1-a36fc6fe83b0</wsa:MessageID>
|
||||||
|
</s:Header>
|
||||||
|
<s:Body>
|
||||||
|
<n1:CreateTargetedConfigJob_OUTPUT>
|
||||||
|
<n1:Message>Configuration job already created, cannot create another config job on specified target until existing job is completed or is cancelled</n1:Message>
|
||||||
|
<n1:MessageID>BIOS007</n1:MessageID>
|
||||||
|
<n1:ReturnValue>2</n1:ReturnValue>
|
||||||
|
</n1:CreateTargetedConfigJob_OUTPUT>
|
||||||
|
</s:Body>
|
||||||
|
</s:Envelope>
|
@ -0,0 +1,28 @@
|
|||||||
|
<s:Envelope xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_BIOSService"
|
||||||
|
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
|
||||||
|
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
|
||||||
|
xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
|
||||||
|
<s:Header>
|
||||||
|
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
|
||||||
|
<wsa:Action>http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_BIOSService/CreateTargetedConfigJobResponse</wsa:Action>
|
||||||
|
<wsa:RelatesTo>uuid:fc2fdae5-6ac2-4338-9b2e-e69b813af829</wsa:RelatesTo>
|
||||||
|
<wsa:MessageID>uuid:d7d89957-2189-1189-8ec0-a36fc6fe83b0</wsa:MessageID>
|
||||||
|
</s:Header>
|
||||||
|
<s:Body>
|
||||||
|
<n1:CreateTargetedConfigJob_OUTPUT>
|
||||||
|
<n1:Job>
|
||||||
|
<wsa:EndpointReference>
|
||||||
|
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
|
||||||
|
<wsa:ReferenceParameters>
|
||||||
|
<wsman:ResourceURI>http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_LifecycleJob</wsman:ResourceURI>
|
||||||
|
<wsman:SelectorSet>
|
||||||
|
<wsman:Selector Name="InstanceID">JID_442507917525</wsman:Selector>
|
||||||
|
<wsman:Selector Name="__cimnamespace">root/dcim</wsman:Selector>
|
||||||
|
</wsman:SelectorSet>
|
||||||
|
</wsa:ReferenceParameters>
|
||||||
|
</wsa:EndpointReference>
|
||||||
|
</n1:Job>
|
||||||
|
<n1:ReturnValue>4096</n1:ReturnValue>
|
||||||
|
</n1:CreateTargetedConfigJob_OUTPUT>
|
||||||
|
</s:Body>
|
||||||
|
</s:Envelope>
|
@ -0,0 +1,17 @@
|
|||||||
|
<s:Envelope xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_BIOSService"
|
||||||
|
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
|
||||||
|
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
|
||||||
|
<s:Header>
|
||||||
|
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
|
||||||
|
<wsa:Action>http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_BIOSService/DeletePendingConfigurationResponse</wsa:Action>
|
||||||
|
<wsa:RelatesTo>uuid:6bd49922-f63b-44d9-abf7-b902f08ec932</wsa:RelatesTo>
|
||||||
|
<wsa:MessageID>uuid:87a16ea4-2189-1189-8eb5-a36fc6fe83b0</wsa:MessageID>
|
||||||
|
</s:Header>
|
||||||
|
<s:Body>
|
||||||
|
<n1:DeletePendingConfiguration_OUTPUT>
|
||||||
|
<n1:Message>Configuration job already created, pending data cannot be deleted</n1:Message>
|
||||||
|
<n1:MessageID>BIOS011</n1:MessageID>
|
||||||
|
<n1:ReturnValue>2</n1:ReturnValue>
|
||||||
|
</n1:DeletePendingConfiguration_OUTPUT>
|
||||||
|
</s:Body>
|
||||||
|
</s:Envelope>
|
@ -0,0 +1,15 @@
|
|||||||
|
<s:Envelope xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_BIOSService" xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
|
||||||
|
<s:Header>
|
||||||
|
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
|
||||||
|
<wsa:Action>http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_BIOSService/DeletePendingConfigurationResponse</wsa:Action>
|
||||||
|
<wsa:RelatesTo>uuid:33b4948b-79e6-42ff-8670-19fbb25702c8</wsa:RelatesTo>
|
||||||
|
<wsa:MessageID>uuid:d578f646-2189-1189-8ebe-a36fc6fe83b0</wsa:MessageID>
|
||||||
|
</s:Header>
|
||||||
|
<s:Body>
|
||||||
|
<n1:DeletePendingConfiguration_OUTPUT>
|
||||||
|
<n1:Message>The command was successful.</n1:Message>
|
||||||
|
<n1:MessageID>BIOS001</n1:MessageID>
|
||||||
|
<n1:ReturnValue>0</n1:ReturnValue>
|
||||||
|
</n1:DeletePendingConfiguration_OUTPUT>
|
||||||
|
</s:Body>
|
||||||
|
</s:Envelope>
|
@ -0,0 +1,19 @@
|
|||||||
|
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
|
||||||
|
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
|
||||||
|
xmlns:wsen="http://schemas.xmlsoap.org/ws/2004/09/enumeration"
|
||||||
|
xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"
|
||||||
|
xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_LifecycleJob">
|
||||||
|
<s:Header>
|
||||||
|
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
|
||||||
|
<wsa:Action>http://schemas.xmlsoap.org/ws/2004/09/enumeration/EnumerateResponse</wsa:Action>
|
||||||
|
<wsa:RelatesTo>uuid:2453cc4f-104c-104c-8002-fd0aa2bdb228</wsa:RelatesTo>
|
||||||
|
<wsa:MessageID>uuid:28c00f71-1051-1051-8003-fcc71555dbe0</wsa:MessageID>
|
||||||
|
</s:Header>
|
||||||
|
<s:Body>
|
||||||
|
<wsen:EnumerateResponse>
|
||||||
|
<wsman:Items/>
|
||||||
|
<wsen:EnumerationContext/>
|
||||||
|
<wsman:EndOfSequence/>
|
||||||
|
</wsen:EnumerateResponse>
|
||||||
|
</s:Body>
|
||||||
|
</s:Envelope>
|
76
dracclient/tests/wsman_mocks/lifecycle_job-enum-ok.xml
Normal file
76
dracclient/tests/wsman_mocks/lifecycle_job-enum-ok.xml
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsen="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_LifecycleJob">
|
||||||
|
<s:Header>
|
||||||
|
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
|
||||||
|
<wsa:Action>http://schemas.xmlsoap.org/ws/2004/09/enumeration/EnumerateResponse</wsa:Action>
|
||||||
|
<wsa:RelatesTo>uuid:f3ee3b82-210b-110b-8002-fd0aa2bdb228</wsa:RelatesTo>
|
||||||
|
<wsa:MessageID>uuid:f4327d65-210b-110b-836c-0581b4d9bed4</wsa:MessageID>
|
||||||
|
</s:Header>
|
||||||
|
<s:Body>
|
||||||
|
<wsen:EnumerateResponse>
|
||||||
|
<wsman:Items>
|
||||||
|
<n1:DCIM_LifecycleJob>
|
||||||
|
<n1:InstanceID>JID_CLEARALL</n1:InstanceID>
|
||||||
|
<n1:JobStartTime>TIME_NA</n1:JobStartTime>
|
||||||
|
<n1:JobStatus>Pending</n1:JobStatus>
|
||||||
|
<n1:JobUntilTime>TIME_NA</n1:JobUntilTime>
|
||||||
|
<n1:Message>NA</n1:Message>
|
||||||
|
<n1:MessageID>NA</n1:MessageID>
|
||||||
|
<n1:Name>CLEARALL</n1:Name>
|
||||||
|
<n1:PercentComplete>0</n1:PercentComplete>
|
||||||
|
</n1:DCIM_LifecycleJob>
|
||||||
|
<n1:DCIM_LifecycleJob>
|
||||||
|
<n1:InstanceID>JID_001436912645</n1:InstanceID>
|
||||||
|
<n1:JobStartTime>00000101000000</n1:JobStartTime>
|
||||||
|
<n1:JobStatus>Completed</n1:JobStatus>
|
||||||
|
<n1:JobUntilTime>TIME_NA</n1:JobUntilTime>
|
||||||
|
<n1:Message>Job completed successfully</n1:Message>
|
||||||
|
<n1:MessageID>PR19</n1:MessageID>
|
||||||
|
<n1:Name>ConfigBIOS:BIOS.Setup.1-1</n1:Name>
|
||||||
|
<n1:PercentComplete>100</n1:PercentComplete>
|
||||||
|
</n1:DCIM_LifecycleJob>
|
||||||
|
<n1:DCIM_LifecycleJob>
|
||||||
|
<n1:InstanceID>JID_001436960861</n1:InstanceID>
|
||||||
|
<n1:JobStartTime>00000101000000</n1:JobStartTime>
|
||||||
|
<n1:JobStatus>Completed</n1:JobStatus>
|
||||||
|
<n1:JobUntilTime>TIME_NA</n1:JobUntilTime>
|
||||||
|
<n1:Message>Job completed successfully</n1:Message>
|
||||||
|
<n1:MessageID>PR19</n1:MessageID>
|
||||||
|
<n1:Name>ConfigBIOS:BIOS.Setup.1-1</n1:Name>
|
||||||
|
<n1:PercentComplete>100</n1:PercentComplete>
|
||||||
|
</n1:DCIM_LifecycleJob>
|
||||||
|
<n1:DCIM_LifecycleJob>
|
||||||
|
<n1:InstanceID>JID_001436966148</n1:InstanceID>
|
||||||
|
<n1:JobStartTime>00000101000000</n1:JobStartTime>
|
||||||
|
<n1:JobStatus>Completed</n1:JobStatus>
|
||||||
|
<n1:JobUntilTime>TIME_NA</n1:JobUntilTime>
|
||||||
|
<n1:Message>Job completed successfully</n1:Message>
|
||||||
|
<n1:MessageID>PR19</n1:MessageID>
|
||||||
|
<n1:Name>ConfigBIOS:BIOS.Setup.1-1</n1:Name>
|
||||||
|
<n1:PercentComplete>100</n1:PercentComplete>
|
||||||
|
</n1:DCIM_LifecycleJob>
|
||||||
|
<n1:DCIM_LifecycleJob>
|
||||||
|
<n1:InstanceID>JID_001436980372</n1:InstanceID>
|
||||||
|
<n1:JobStartTime>00000101000000</n1:JobStartTime>
|
||||||
|
<n1:JobStatus>Completed</n1:JobStatus>
|
||||||
|
<n1:JobUntilTime>TIME_NA</n1:JobUntilTime>
|
||||||
|
<n1:Message>Job completed successfully</n1:Message>
|
||||||
|
<n1:MessageID>PR19</n1:MessageID>
|
||||||
|
<n1:Name>ConfigBIOS:BIOS.Setup.1-1</n1:Name>
|
||||||
|
<n1:PercentComplete>100</n1:PercentComplete>
|
||||||
|
</n1:DCIM_LifecycleJob>
|
||||||
|
<n1:DCIM_LifecycleJob>
|
||||||
|
<n1:InstanceID>JID_001436981582</n1:InstanceID>
|
||||||
|
<n1:JobStartTime>00000101000000</n1:JobStartTime>
|
||||||
|
<n1:JobStatus>Running</n1:JobStatus>
|
||||||
|
<n1:JobUntilTime>TIME_NA</n1:JobUntilTime>
|
||||||
|
<n1:Message>Job in progress</n1:Message>
|
||||||
|
<n1:MessageID>PR20</n1:MessageID>
|
||||||
|
<n1:Name>ConfigBIOS:BIOS.Setup.1-1</n1:Name>
|
||||||
|
<n1:PercentComplete>34</n1:PercentComplete>
|
||||||
|
</n1:DCIM_LifecycleJob>
|
||||||
|
</wsman:Items>
|
||||||
|
<wsen:EnumerationContext/>
|
||||||
|
<wsman:EndOfSequence/>
|
||||||
|
</wsen:EnumerateResponse>
|
||||||
|
</s:Body>
|
||||||
|
</s:Envelope>
|
@ -40,3 +40,9 @@ def find_xml(doc, item, namespace, find_all=False):
|
|||||||
if find_all:
|
if find_all:
|
||||||
return doc.findall(query)
|
return doc.findall(query)
|
||||||
return doc.find(query)
|
return doc.find(query)
|
||||||
|
|
||||||
|
|
||||||
|
def get_wsman_resource_attr(doc, resource_uri, attr_name):
|
||||||
|
"""Find an attribute of a resource in an ElementTree object"""
|
||||||
|
|
||||||
|
return find_xml(doc, attr_name, resource_uri).text.strip()
|
||||||
|
Loading…
Reference in New Issue
Block a user