Library for managing machines with Dell iDRAC cards
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
python-dracclient/dracclient/utils.py

241 lines
9.0 KiB

#
# 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.
"""
Common functionalities shared between different DRAC modules.
"""
from dracclient import constants
from dracclient import exceptions
NS_XMLSchema_Instance = 'http://www.w3.org/2001/XMLSchema-instance'
# ReturnValue constants
RET_SUCCESS = '0'
RET_ERROR = '2'
RET_CREATED = '4096'
REBOOT_REQUIRED = {
'yes': constants.RebootRequired.true,
'no': constants.RebootRequired.false,
'optional': constants.RebootRequired.optional
}
def find_xml(doc, item, namespace, find_all=False):
"""Find the first or all elements in an ElementTree object.
:param doc: the element tree object.
:param item: the element name.
:param namespace: the namespace of the element.
:param find_all: Boolean value, if True find all elements, if False
find only the first one. Defaults to False.
:returns: if find_all is False the element object will be returned
if found, None if not found. If find_all is True a list of
element objects will be returned or an empty list if no
elements were found.
"""
query = ('.//{%(namespace)s}%(item)s' % {'namespace': namespace,
'item': item})
if find_all:
return doc.findall(query)
return doc.find(query)
def _is_attr_non_nil(elem):
"""Return whether an element is non-nil.
:param elem: the element object.
:returns: whether the element is nil.
"""
return elem.attrib.get('{%s}nil' % NS_XMLSchema_Instance) != 'true'
def get_wsman_resource_attr(doc, resource_uri, attr_name, nullable=False,
allow_missing=False):
"""Find an attribute of a resource in an ElementTree object.
:param doc: the element tree object.
:param resource_uri: the resource URI of the namespace.
:param attr_name: the name of the attribute.
:param nullable: enables checking if the element contains an
XMLSchema-instance namespaced nil attribute that has a
value of True. In this case, it will return None.
:param allow_missing: if set to True, attributes missing from the XML
document will return None instead of raising
DRACMissingResponseField.
:raises: DRACMissingResponseField if the attribute is missing from the XML
doc and allow_missing is False.
:raises: DRACEmptyResponseField if the attribute is present in the XML doc
but it has no text and nullable is False.
:returns: value of the attribute
"""
item = find_xml(doc, attr_name, resource_uri)
if item is None:
if allow_missing:
return
else:
raise exceptions.DRACMissingResponseField(attr=attr_name)
if not nullable:
if item.text is None:
raise exceptions.DRACEmptyResponseField(attr=attr_name)
return item.text.strip()
else:
if _is_attr_non_nil(item):
return item.text.strip()
def get_all_wsman_resource_attrs(doc, resource_uri, attr_name, nullable=False):
"""Find all instances of an attribute of a resource in an ElementTree.
:param doc: the element tree object.
:param resource_uri: the resource URI of the namespace.
:param attr_name: the name of the attribute.
:param nullable: enables checking if any of the elements contain an
XMLSchema-instance namespaced nil attribute that has a
value of True. In this case, these elements will not be
returned.
:raises: DRACEmptyResponseField if any of the attributes in the XML doc
have no text and nullable is False.
:returns: a list containing the value of each of the instances of the
attribute.
"""
items = find_xml(doc, attr_name, resource_uri, find_all=True)
if not nullable:
for item in items:
if item.text is None:
raise exceptions.DRACEmptyResponseField(attr=attr_name)
return [item.text.strip() for item in items]
else:
return [item.text.strip() for item in items if _is_attr_non_nil(item)]
def build_return_dict(doc, resource_uri,
is_commit_required_value=None,
is_reboot_required_value=None,
commit_required_value=None):
"""Builds a dictionary to be returned
Build a dictionary to be returned from WSMAN operations that are not
read-only.
:param doc: the element tree object.
:param resource_uri: the resource URI of the namespace.
:param is_commit_required_value: The value to be returned for
is_commit_required, or None if the value should be determined
from the doc.
:param is_reboot_required_value: The value to be returned for
is_reboot_required, or None if the value should be determined
from the doc.
:param commit_required_value: The value to be returned for
commit_required, or None if the value should be determined
from the doc.
:returns: a dictionary containing:
- is_commit_required: indicates if a commit is required.
- is_reboot_required: indicates if a reboot is required.
- commit_required: a deprecated key indicating if a commit is
required. This key actually has a value that indicates if a
reboot is required.
"""
if is_reboot_required_value is not None and \
is_reboot_required_value not in constants.RebootRequired.all():
msg = ("is_reboot_required_value must be a member of the "
"RebootRequired enumeration or None. The passed value was "
"%(is_reboot_required_value)s" % {
'is_reboot_required_value': is_reboot_required_value})
raise exceptions.InvalidParameterValue(reason=msg)
result = {}
if is_commit_required_value is None:
is_commit_required_value = is_commit_required(doc, resource_uri)
result['is_commit_required'] = is_commit_required_value
if is_reboot_required_value is None:
is_reboot_required_value = reboot_required(doc, resource_uri)
result['is_reboot_required'] = is_reboot_required_value
# Include commit_required in the response for backwards compatibility
# TBD: Remove this parameter in the future
if commit_required_value is None:
commit_required_value = is_reboot_required(doc, resource_uri)
result['commit_required'] = commit_required_value
return result
def is_commit_required(doc, resource_uri):
"""Check the response document if commit is required.
If SetResult contains "pending" in the response then a commit is required.
:param doc: the element tree object.
:param resource_uri: the resource URI of the namespace.
:returns: a boolean value indicating commit is required or not.
"""
commit_required = find_xml(doc, 'SetResult', resource_uri)
return "pendingvalue" in commit_required.text.lower()
def is_reboot_required(doc, resource_uri):
"""Check the response document if reboot is requested.
RebootRequired attribute in the response indicates whether a config job
needs to be created and the node needs to be rebooted, so that the
Lifecycle controller can commit the pending changes.
:param doc: the element tree object.
:param resource_uri: the resource URI of the namespace.
:returns: a boolean value indicating reboot was requested or not.
"""
reboot_required = find_xml(doc, 'RebootRequired', resource_uri)
return reboot_required.text.lower() == 'yes'
def reboot_required(doc, resource_uri):
"""Check the response document if reboot is requested.
RebootRequired attribute in the response indicates whether node needs to
be rebooted, so that the pending changes can be committed.
:param doc: the element tree object.
:param resource_uri: the resource URI of the namespace.
:returns: True if reboot is required, False if it is not, and the string
"optional" if reboot is optional.
"""
reboot_required_value = find_xml(doc, 'RebootRequired', resource_uri)
return REBOOT_REQUIRED[reboot_required_value.text.lower()]
def validate_integer_value(value, attr_name, error_msgs):
"""Validate integer value"""
if value is None:
error_msgs.append("'%s' is not supplied" % attr_name)
return
try:
int(value)
except ValueError:
error_msgs.append("'%s' is not an integer value" % attr_name)