
Web Services Management (WS-Management and WS-Man) requests/commands can
fail or return invalid results when issued to an Integrated Dell Remote
Access Controller (iDRAC) whose Lifecycle Controller remote service is
not "ready". Specifically, that applies to the WS-Man Enumerate and
Invoke operations.
A Dell technical white paper [0], "Lifecycle Controller Integration --
Best Practices Guide", states that for Lifecycle Controller firmware
1.5.0 and later "The Lifecycle Controller remote service must be in a
'ready' state before running any other WSMAN commands." That applies to
almost all of the workflows and use cases documented by that paper and
supported by this project, openstack/python-dracclient. That document
describes how to determine the readiness of the Lifecycle Controller
remote service. A project commit [1] implements that.
This refactors that patch in preparation for changing the internal
implementation of the project's APIs so that they follow that best
practice. The implementation of is_idrac_ready() and
wait_until_idrac_is_ready() have been relocated further down the call
stack, to the iDRAC specialization of the WS-Man client defined by class
dracclient.client.WSManClient. Those methods continue to be available
through the API provided by class dracclient.client.Client.
No changes have been made to this project's APIs nor to any functional
behavior.
[0]
http://en.community.dell.com/techcenter/extras/m/white_papers/20442332
[1]
39253bb272
Change-Id: I87996bbca129995f6c84848ebdb0c33cfedeea53
Partial-Bug: #1697558
Related-Bug: #1691808
205 lines
8.0 KiB
Python
205 lines
8.0 KiB
Python
#
|
|
# 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.
|
|
|
|
from dracclient.resources import uris
|
|
from dracclient import utils
|
|
from dracclient import wsman
|
|
|
|
|
|
class LifecycleControllerManagement(object):
|
|
|
|
def __init__(self, client):
|
|
"""Creates LifecycleControllerManagement object
|
|
|
|
:param client: an instance of WSManClient
|
|
"""
|
|
self.client = client
|
|
|
|
def get_version(self):
|
|
"""Returns the Lifecycle controller version
|
|
|
|
:returns: Lifecycle controller version as a tuple of integers
|
|
:raises: WSManRequestFailure on request failures
|
|
:raises: WSManInvalidResponse when receiving invalid response
|
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
|
interface
|
|
"""
|
|
|
|
filter_query = ('select LifecycleControllerVersion '
|
|
'from DCIM_SystemView')
|
|
doc = self.client.enumerate(uris.DCIM_SystemView,
|
|
filter_query=filter_query)
|
|
lc_version_str = utils.find_xml(doc, 'LifecycleControllerVersion',
|
|
uris.DCIM_SystemView).text
|
|
|
|
return tuple(map(int, (lc_version_str.split('.'))))
|
|
|
|
|
|
class LCConfiguration(object):
|
|
|
|
def __init__(self, client):
|
|
"""Creates LifecycleControllerManagement object
|
|
|
|
:param client: an instance of WSManClient
|
|
"""
|
|
self.client = client
|
|
|
|
def list_lifecycle_settings(self):
|
|
"""List the LC configuration settings
|
|
|
|
:returns: a dictionary with the LC settings using InstanceID as the
|
|
key. The attributes are either LCEnumerableAttribute,
|
|
LCStringAttribute or LCIntegerAttribute objects.
|
|
:raises: WSManRequestFailure on request failures
|
|
:raises: WSManInvalidResponse when receiving invalid response
|
|
:raises: DRACOperationFailed on error reported back by the DRAC
|
|
interface
|
|
"""
|
|
result = {}
|
|
namespaces = [(uris.DCIM_LCEnumeration, LCEnumerableAttribute),
|
|
(uris.DCIM_LCString, LCStringAttribute)]
|
|
for (namespace, attr_cls) in namespaces:
|
|
attribs = self._get_config(namespace, attr_cls)
|
|
result.update(attribs)
|
|
return result
|
|
|
|
def _get_config(self, resource, attr_cls):
|
|
result = {}
|
|
|
|
doc = self.client.enumerate(resource)
|
|
|
|
items = doc.find('.//{%s}Items' % wsman.NS_WSMAN)
|
|
for item in items:
|
|
attribute = attr_cls.parse(item)
|
|
result[attribute.instance_id] = attribute
|
|
|
|
return result
|
|
|
|
|
|
class LCAttribute(object):
|
|
"""Generic LC attribute class"""
|
|
|
|
def __init__(self, name, instance_id, current_value, pending_value,
|
|
read_only):
|
|
"""Creates LCAttribute object
|
|
|
|
:param name: name of the LC attribute
|
|
:param instance_id: InstanceID of the LC attribute
|
|
:param current_value: current value of the LC attribute
|
|
:param pending_value: pending value of the LC attribute, reflecting
|
|
an unprocessed change (eg. config job not completed)
|
|
:param read_only: indicates whether this LC attribute can be changed
|
|
"""
|
|
self.name = name
|
|
self.instance_id = instance_id
|
|
self.current_value = current_value
|
|
self.pending_value = pending_value
|
|
self.read_only = read_only
|
|
|
|
def __eq__(self, other):
|
|
return self.__dict__ == other.__dict__
|
|
|
|
@classmethod
|
|
def parse(cls, namespace, lifecycle_attr_xml):
|
|
"""Parses XML and creates LCAttribute object"""
|
|
|
|
name = utils.get_wsman_resource_attr(
|
|
lifecycle_attr_xml, namespace, 'AttributeName')
|
|
instance_id = utils.get_wsman_resource_attr(
|
|
lifecycle_attr_xml, namespace, 'InstanceID')
|
|
current_value = utils.get_wsman_resource_attr(
|
|
lifecycle_attr_xml, namespace, 'CurrentValue', nullable=True)
|
|
pending_value = utils.get_wsman_resource_attr(
|
|
lifecycle_attr_xml, namespace, 'PendingValue', nullable=True)
|
|
read_only = utils.get_wsman_resource_attr(
|
|
lifecycle_attr_xml, namespace, 'IsReadOnly')
|
|
|
|
return cls(name, instance_id, current_value, pending_value,
|
|
(read_only == 'true'))
|
|
|
|
|
|
class LCEnumerableAttribute(LCAttribute):
|
|
"""Enumerable LC attribute class"""
|
|
|
|
namespace = uris.DCIM_LCEnumeration
|
|
|
|
def __init__(self, name, instance_id, current_value, pending_value,
|
|
read_only, possible_values):
|
|
"""Creates LCEnumerableAttribute object
|
|
|
|
:param name: name of the LC attribute
|
|
:param current_value: current value of the LC attribute
|
|
:param pending_value: pending value of the LC attribute, reflecting
|
|
an unprocessed change (eg. config job not completed)
|
|
:param read_only: indicates whether this LC attribute can be changed
|
|
:param possible_values: list containing the allowed values for the LC
|
|
attribute
|
|
"""
|
|
super(LCEnumerableAttribute, self).__init__(name, instance_id,
|
|
current_value,
|
|
pending_value, read_only)
|
|
self.possible_values = possible_values
|
|
|
|
@classmethod
|
|
def parse(cls, lifecycle_attr_xml):
|
|
"""Parses XML and creates LCEnumerableAttribute object"""
|
|
|
|
lifecycle_attr = LCAttribute.parse(cls.namespace, lifecycle_attr_xml)
|
|
possible_values = [attr.text for attr
|
|
in utils.find_xml(lifecycle_attr_xml,
|
|
'PossibleValues',
|
|
cls.namespace, find_all=True)]
|
|
|
|
return cls(lifecycle_attr.name, lifecycle_attr.instance_id,
|
|
lifecycle_attr.current_value, lifecycle_attr.pending_value,
|
|
lifecycle_attr.read_only, possible_values)
|
|
|
|
|
|
class LCStringAttribute(LCAttribute):
|
|
"""String LC attribute class"""
|
|
|
|
namespace = uris.DCIM_LCString
|
|
|
|
def __init__(self, name, instance_id, current_value, pending_value,
|
|
read_only, min_length, max_length):
|
|
"""Creates LCStringAttribute object
|
|
|
|
:param name: name of the LC attribute
|
|
:param instance_id: InstanceID of the LC attribute
|
|
:param current_value: current value of the LC attribute
|
|
:param pending_value: pending value of the LC attribute, reflecting
|
|
an unprocessed change (eg. config job not completed)
|
|
:param read_only: indicates whether this LC attribute can be changed
|
|
:param min_length: minimum length of the string
|
|
:param max_length: maximum length of the string
|
|
"""
|
|
super(LCStringAttribute, self).__init__(name, instance_id,
|
|
current_value, pending_value,
|
|
read_only)
|
|
self.min_length = min_length
|
|
self.max_length = max_length
|
|
|
|
@classmethod
|
|
def parse(cls, lifecycle_attr_xml):
|
|
"""Parses XML and creates LCStringAttribute object"""
|
|
|
|
lifecycle_attr = LCAttribute.parse(cls.namespace, lifecycle_attr_xml)
|
|
min_length = int(utils.get_wsman_resource_attr(
|
|
lifecycle_attr_xml, cls.namespace, 'MinLength'))
|
|
max_length = int(utils.get_wsman_resource_attr(
|
|
lifecycle_attr_xml, cls.namespace, 'MaxLength'))
|
|
|
|
return cls(lifecycle_attr.name, lifecycle_attr.instance_id,
|
|
lifecycle_attr.current_value, lifecycle_attr.pending_value,
|
|
lifecycle_attr.read_only, min_length, max_length)
|