Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rekey on member #33836

Merged
merged 4 commits into from
Dec 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 0 additions & 63 deletions lib/ansible/plugins/filter/cast_type.py

This file was deleted.

57 changes: 54 additions & 3 deletions lib/ansible/plugins/filter/mathstuff.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# (c) 2014, Brian Coca <bcoca@ansible.com>
# Copyright 2014, Brian Coca <bcoca@ansible.com>
# Copyright 2017, Ken Celenza <ken@networktocode.com>
# Copyright 2017, Jason Edelman <jason@networktocode.com>
# Copyright 2017, Ansible Project
#
# This file is part of Ansible
#
Expand Down Expand Up @@ -26,7 +29,9 @@

from ansible import errors
from ansible.module_utils import basic
from ansible.module_utils.six import binary_type, text_type
from ansible.module_utils.six.moves import zip, zip_longest
from ansible.module_utils._text import to_native


def unique(a):
Expand Down Expand Up @@ -113,18 +118,63 @@ def human_readable(size, isbits=False, unit=None):
''' Return a human readable string '''
try:
return basic.bytes_to_human(size, isbits, unit)
except:
except Exception:
raise errors.AnsibleFilterError("human_readable() can't interpret following string: %s" % size)


def human_to_bytes(size, default_unit=None, isbits=False):
''' Return bytes count from a human readable string '''
try:
return basic.human_to_bytes(size, default_unit, isbits)
except:
except Exception:
raise errors.AnsibleFilterError("human_to_bytes() can't interpret following string: %s" % size)


def rekey_on_member(data, key, duplicates='error'):
"""
Rekey a dict of dicts on another member

May also create a dict from a list of dicts.

duplicates can be one of ``error`` or ``overwrite`` to specify whether to error out if the key
value would be duplicated or to overwrite previous entries if that's the case.
"""
if duplicates not in ('error', 'overwrite'):
raise errors.AnsibleFilterError("duplicates parameter to rekey_on_member has unknown value: {0}".format(duplicates))

new_obj = {}

if isinstance(data, collections.Mapping):
iterate_over = data.values()
elif isinstance(data, collections.Iterable) and not isinstance(data, (text_type, binary_type)):
iterate_over = data
else:
raise errors.AnsibleFilterError("Type is not a valid list, set, or dict")

for item in iterate_over:
if not isinstance(item, collections.Mapping):
raise errors.AnsibleFilterError("List item is not a valid dict")

try:
key_elem = item[key]
except KeyError:
raise errors.AnsibleFilterError("Key {0} was not found".format(key))
except Exception as e:
raise errors.AnsibleFilterError(to_native(e))

# Note: if new_obj[key_elem] exists it will always be a non-empty dict (it will at
# minimun contain {key: key_elem}
if new_obj.get(key_elem, None):
if duplicates == 'error':
raise errors.AnsibleFilterError("Key {0} is not unique, cannot correctly turn into dict".format(key_elem))
elif duplicates == 'overwrite':
new_obj[key_elem] = item
else:
new_obj[key_elem] = item

return new_obj


class FilterModule(object):
''' Ansible math jinja2 filters '''

Expand Down Expand Up @@ -154,6 +204,7 @@ def filters(self):
# computer theory
'human_readable': human_readable,
'human_to_bytes': human_to_bytes,
'rekey_on_member': rekey_on_member,

# zip
'zip': zip,
Expand Down
63 changes: 0 additions & 63 deletions test/units/plugins/filter/test_cast_types.py

This file was deleted.

45 changes: 45 additions & 0 deletions test/units/plugins/filter/test_mathstuff.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,48 @@ def test_square_root(self):

def test_cube_root(self):
assert ms.inversepower(27, 3) == 3


class TestRekeyOnMember():
# (Input data structure, member to rekey on, expected return)
VALID_ENTRIES = (
([{"proto": "eigrp", "state": "enabled"}, {"proto": "ospf", "state": "enabled"}],
'proto',
{'eigrp': {'state': 'enabled', 'proto': 'eigrp'}, 'ospf': {'state': 'enabled', 'proto': 'ospf'}}),
({'eigrp': {"proto": "eigrp", "state": "enabled"}, 'ospf': {"proto": "ospf", "state": "enabled"}},
'proto',
{'eigrp': {'state': 'enabled', 'proto': 'eigrp'}, 'ospf': {'state': 'enabled', 'proto': 'ospf'}}),
)

# (Input data structure, member to rekey on, expected error message)
INVALID_ENTRIES = (
# Fail when key is not found
([{"proto": "eigrp", "state": "enabled"}], 'invalid_key', "Key invalid_key was not found"),
({"eigrp": {"proto": "eigrp", "state": "enabled"}}, 'invalid_key', "Key invalid_key was not found"),
# Fail when key is duplicated
([{"proto": "eigrp"}, {"proto": "ospf"}, {"proto": "ospf"}],
'proto', 'Key ospf is not unique, cannot correctly turn into dict'),
# Fail when value is not a dict
(["string"], 'proto', "List item is not a valid dict"),
([123], 'proto', "List item is not a valid dict"),
([[{'proto': 1}]], 'proto', "List item is not a valid dict"),
# Fail when we do not send a dict or list
("string", 'proto', "Type is not a valid list, set, or dict"),
(123, 'proto', "Type is not a valid list, set, or dict"),
)

@pytest.mark.parametrize("list_original, key, expected", VALID_ENTRIES)
def test_rekey_on_member_success(self, list_original, key, expected):
assert ms.rekey_on_member(list_original, key) == expected

@pytest.mark.parametrize("list_original, key, expected", INVALID_ENTRIES)
def test_fail_rekey_on_member(self, list_original, key, expected):
with pytest.raises(AnsibleFilterError) as err:
ms.rekey_on_member(list_original, key)

assert err.value.message == expected

def test_duplicate_strategy_overwrite(self):
list_original = ({'proto': 'eigrp', 'id': 1}, {'proto': 'ospf', 'id': 2}, {'proto': 'eigrp', 'id': 3})
expected = {'eigrp': {'proto': 'eigrp', 'id': 3}, 'ospf': {'proto': 'ospf', 'id': 2}}
assert ms.rekey_on_member(list_original, 'proto', duplicates='overwrite') == expected