Skip to content

Commit

Permalink
Added common functions for hyphen-underscore property name conversions
Browse files Browse the repository at this point in the history
Details:

* Added a function underscore_properties() that converts single or lists of
  resources from property names with hyphens (as used by the HMC) to property
  names with underscores (as used by Ansble).

* Added a function hyphen_properties() that converts a single resource
  property dict from property names with underscores (as used by Ansble) to
  property names with hyphens (as used by the HMC).

* Added unit test cases for both functions.

Signed-off-by: Andreas Maier <[email protected]>
  • Loading branch information
andy-maier committed Jul 3, 2024
1 parent df3c585 commit 92d98b9
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 1 deletion.
2 changes: 2 additions & 0 deletions minimum-constraints-install.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pytz==2019.1
# TODO-ZHMC: Use zhmcclient 1.17.0 once releaed.
# zhmcclient==1.17.0

immutable-views==0.6.0


# Indirect dependencies for install that are needed for some reason (must be consistent with requirements.txt)

Expand Down
68 changes: 67 additions & 1 deletion plugins/module_utils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,16 @@
import platform
import sys
import re
from collections.abc import Mapping

try:
from zhmcclient import Session, ClientAuthError
from immutable_views import DictView
IMP_IMMUTABLE_VIEWS_ERR = None
except ImportError:
IMP_IMMUTABLE_VIEWS_ERR = traceback.format_exc()

try:
from zhmcclient import Session, ClientAuthError, BaseResource
IMP_ZHMCCLIENT_ERR = None
except ImportError:
IMP_ZHMCCLIENT_ERR = traceback.format_exc()
Expand Down Expand Up @@ -94,6 +101,9 @@ def common_fail_on_import_errors(module):
"""
Check for import errors in this module.
"""
if IMP_IMMUTABLE_VIEWS_ERR is not None:
module.fail_json(msg=missing_required_lib("immutable_views"),
exception=IMP_IMMUTABLE_VIEWS_ERR)
if IMP_ZHMCCLIENT_ERR is not None:
module.fail_json(msg=missing_required_lib("zhmcclient"),
exception=IMP_ZHMCCLIENT_ERR)
Expand Down Expand Up @@ -1241,3 +1251,59 @@ def parse_hmc_host(hmc_host):
"Module parameter 'hmc_host' must be a string or list type, but "
f"is of type {type(hmc_host)}")
return hmc_host


def _underscore_properties_dict(prop_dict):
"""
Return a copy of the input dict with property names converted from
hyphens (as used by HMC) to underscores (as used by Ansible).
The input object must be:
* a dict of properties (key: property name, value: property value)
"""
under_prop_dict = {}
for pname_hyphen, pvalue in prop_dict.items():
pname_under = pname_hyphen.replace('-', '_')
under_prop_dict[pname_under] = pvalue
return under_prop_dict


def underscore_properties(obj):
"""
Return a copy of the input object with property names converted from
hyphens (as used by HMC) to underscores (as used by Ansible).
The input object may be one of:
* a mapping/dict of properties for a single resource
* an immutable_views.DictView object for a single resource
(as returned by zhmcclient.BaseResource.properties)
* a single zhmcclient.BaseResource object
* an iterable of zhmcclient.BaseResource objects
(as returned by zhmcclient.BaseResourceManager.list/findall())
"""
if isinstance(obj, (Mapping, DictView)):
return _underscore_properties_dict(obj)

if isinstance(obj, BaseResource):
return _underscore_properties_dict(obj.properties)

under_prop_list = []
for res_obj in obj:
res_under_props = _underscore_properties_dict(res_obj.properties)
under_prop_list.append(res_under_props)
return under_prop_list


def hyphen_properties(obj):
"""
Return a copy of the input object with property names converted from
underscores (as used by Ansible) to hyphens (as used by HMC).
The input object must be:
* a mapping/dict of properties (key: property name, value: property value)
"""
hyphen_prop_dict = {}
for pname_under, pvalue in obj.items():
pname_hyphen = pname_under.replace('_', '-')
hyphen_prop_dict[pname_hyphen] = pvalue
return hyphen_prop_dict
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ pytz>=2019.1
zhmcclient @ git+https://github.com/zhmcclient/python-zhmcclient.git@master
# zhmcclient>=1.17.0

immutable-views>=0.6.0


# Indirect dependencies for install that are needed for some reason (must be consistent with minimum-constraints-install.txt)

Expand Down
160 changes: 160 additions & 0 deletions tests/unit/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

import re
import pytest
from immutable_views import DictView

from zhmcclient import BaseResource

from plugins.module_utils import common

Expand Down Expand Up @@ -144,3 +147,160 @@ def test_common_parse_hmc_host(
hmc_host = common.parse_hmc_host(in_hmc_host)

assert hmc_host == exp_hmc_host


class DummyResource(BaseResource):
"""
Dummy zhmcclient.BaseResource object that can be created without any
parent or manager objects, for test purposes.
It is good for accessing its 'properties' property, but not for much more.
"""

def __init__(self, properties):
# pylint: disable=super-init-not-called
self._properties = dict(properties) if properties else {}


COMMON_UNDER_PROPS_TESTCASES = [
# Testcases for test_common_under_props()
# The list items are tuples with the following items:
# - desc (string): description of the testcase.
# - input: Input object
# - exp_result: Expected result object
# - exp_exc_type: Expected exception type, or None for no exc. expected.

(
"Property dict for single resource",
{
'prop-name-1': 'value1',
'prop_name_2': 'value2',
},
{
'prop_name_1': 'value1',
'prop_name_2': 'value2',
},
None,
),
(
"DictView for a single resource",
DictView({
'prop-name-1': 'value1',
'prop_name_2': 'value2',
}),
{
'prop_name_1': 'value1',
'prop_name_2': 'value2',
},
None,
),
(
"zhmcclient.BaseResource for a single resource",
DummyResource({
'prop-name-1': 'value1',
'prop_name_2': 'value2',
}),
{
'prop_name_1': 'value1',
'prop_name_2': 'value2',
},
None,
),
(
"list of one zhmcclient.BaseResource object",
[
DummyResource({
'prop-name-1': 'value1',
'prop_name_2': 'value2',
}),
],
[
{
'prop_name_1': 'value1',
'prop_name_2': 'value2',
},
],
None,
),
(
"list of strings (illegal)",
['prop-name-1', 'prop_name_2'],
None,
AttributeError,
),
]


@pytest.mark.parametrize(
"desc, input, exp_result, exp_exc_type",
COMMON_UNDER_PROPS_TESTCASES)
def test_common_under_props(desc, input, exp_result, exp_exc_type):
# pylint: disable=unused-argument
"""
Test the underscore_properties() function.
"""

if exp_exc_type:
with pytest.raises(exp_exc_type):

# The code to be tested
common.underscore_properties(input)

else:

# The code to be tested
result = common.underscore_properties(input)

assert result == exp_result


COMMON_HYPHEN_PROPS_TESTCASES = [
# Testcases for test_common_hyphen_props()
# The list items are tuples with the following items:
# - desc (string): description of the testcase.
# - input: Input object
# - exp_result: Expected result object
# - exp_exc_type: Expected exception type, or None for no exc. expected.

(
"Property dict for single resource",
{
'prop-name-1': 'value1',
'prop_name_2': 'value2',
},
{
'prop-name-1': 'value1',
'prop-name-2': 'value2',
},
None,
),
(
"list of strings (illegal)",
['prop-name-1', 'prop_name_2'],
None,
AttributeError,
),
]


@pytest.mark.parametrize(
"desc, input, exp_result, exp_exc_type",
COMMON_HYPHEN_PROPS_TESTCASES)
def test_common_hyphen_props(desc, input, exp_result, exp_exc_type):
# pylint: disable=unused-argument
"""
Test the hyphen_properties() function.
"""

if exp_exc_type:
with pytest.raises(exp_exc_type):

# The code to be tested
common.hyphen_properties(input)

else:

# The code to be tested
result = common.hyphen_properties(input)

assert result == exp_result

0 comments on commit 92d98b9

Please sign in to comment.