@@ -497,7 +497,9 @@ def _afm_convert(value):
497497 return value
498498
499499
500- def module_params_get (module , name , allow_empty_list_item = False ):
500+ def module_params_get (
501+ module , name , allow_empty_list_item = False , ignore_list_check = False
502+ ):
501503 value = _afm_convert (module .params .get (name ))
502504
503505 # Fail on empty strings in the list or if allow_empty_list_item is True
@@ -506,7 +508,7 @@ def module_params_get(module, name, allow_empty_list_item=False):
506508 # "" for lists with choices, even if the empty list is not part of
507509 # the choices.
508510 # Ansible issue https://github.com/ansible/ansible/issues/77108
509- if isinstance (value , list ):
511+ if not ignore_list_check and isinstance (value , list ):
510512 for val in value :
511513 if (
512514 isinstance (val , (str , unicode )) # pylint: disable=W0012,E0606
@@ -537,6 +539,14 @@ def convert_param_value_to_lowercase(value):
537539 return value
538540
539541
542+ def convert_param_value_to_uppercase (value ):
543+ if isinstance (value , list ):
544+ value = [v .upper () for v in value ]
545+ if isinstance (value , (str , unicode )):
546+ value = value .upper ()
547+ return value
548+
549+
540550def module_params_get_with_type_cast (
541551 module , name , datatype , allow_empty = False
542552):
@@ -1189,7 +1199,9 @@ def ipa_connect(self, context=None):
11891199 finally :
11901200 temp_kdestroy (ccache_dir , ccache_name )
11911201
1192- def params_get (self , name , allow_empty_list_item = False ):
1202+ def params_get (
1203+ self , name , allow_empty_list_item = False , ignore_list_check = False
1204+ ):
11931205 """
11941206 Retrieve value set for module parameter.
11951207
@@ -1201,7 +1213,8 @@ def params_get(self, name, allow_empty_list_item=False):
12011213 The parameter allowes to have empty strings in a list
12021214
12031215 """
1204- return module_params_get (self , name , allow_empty_list_item )
1216+ return module_params_get (
1217+ self , name , allow_empty_list_item , ignore_list_check )
12051218
12061219 def params_get_lowercase (self , name , allow_empty_list_item = False ):
12071220 """
@@ -1656,6 +1669,10 @@ class EntryFactory:
16561669 not valid, 'fail_json' should be called. The function must return
16571670 the entry, modified or not. The funcion signature is
16581671 'def fn(module:IPAAnsibleModule, entry: Entry) -> Entry:'
1672+ unique_key: A string or list of strings representing parameter names
1673+ that uniquely identify an object. When set, the objects are checked
1674+ for duplicates and a EntryFactory.DuplicateKeyError is raised, when
1675+ instantiating the EntryFactory object if any duplicate is found.
16591676 **user_vars: any other keyword argument is passed to the
16601677 validate_entry callback as user data.
16611678
@@ -1693,13 +1710,21 @@ def main():
16931710
16941711 """
16951712
1713+ class DuplicateKeyError (Exception ):
1714+ """An exeption for duplicate keys in EntryFactory."""
1715+
1716+ def __init__ (self , dups ):
1717+ dups = [k if len (k ) > 1 else k [0 ] for k in dups ]
1718+ super ().__init__ (", " .join (repr (k ) for k in dups ))
1719+
16961720 def __init__ (
16971721 self ,
16981722 ansible_module ,
16991723 invalid_params ,
17001724 multiname ,
17011725 params ,
17021726 validate_entry = None ,
1727+ unique_key = None ,
17031728 ** user_vars
17041729 ):
17051730 """Initialize the Entry Factory."""
@@ -1711,9 +1736,37 @@ def __init__(
17111736 param : (config or {}).get ("convert" , [])
17121737 for param , config in params .items ()
17131738 }
1739+ self .attrs = {
1740+ param : {
1741+ _attr : _value
1742+ for _attr , _value in config .items ()
1743+ if _attr not in {"convert" }
1744+ }
1745+ for param , config in params .items ()
1746+ }
17141747 self .validate_entry = validate_entry
17151748 self .user_vars = user_vars
17161749 self .__entries = self ._get_entries ()
1750+ _dups = self .get_duplicate_keys (unique_key )
1751+ if _dups :
1752+ raise EntryFactory .DuplicateKeyError (_dups )
1753+
1754+ def get_duplicate_keys (self , unique_key ):
1755+ """Get a set of duplicate keys based on the given parameter name(s)."""
1756+ _dups = set ()
1757+ if unique_key is not None :
1758+ if isinstance (unique_key , (str , unicode )): # pylint: disable=all
1759+ unique_key = [unique_key ]
1760+ _keys = set ()
1761+ for _entry in self .__entries :
1762+ _key_set = tuple (set (_entry [k ] for k in unique_key ))
1763+ if _key_set in _keys :
1764+ _dups .add (_key_set )
1765+ _keys .add (_key_set )
1766+ return _dups
1767+
1768+ def __len__ (self ):
1769+ return len (self .__entries )
17171770
17181771 def __iter__ (self ):
17191772 """Initialize factory iterator."""
@@ -1811,7 +1864,9 @@ def copy_entry_and_set_name(entry, name):
18111864 # list of names.
18121865 _entry = self ._extract_entry (
18131866 self .ansible_module ,
1814- IPAAnsibleModule .params_get
1867+ lambda s , p : (
1868+ IPAAnsibleModule .params_get (s , p , ignore_list_check = True )
1869+ )
18151870 )
18161871 # copy attribute values if 'name' returns a list
18171872 _entries = [
@@ -1828,16 +1883,44 @@ def copy_entry_and_set_name(entry, name):
18281883
18291884 def _extract_entry (self , data , param_get ):
18301885 """Extract an entry from the given data, using the given method."""
1831- def get_entry_param_value (parameter , conversions ):
1832- _value = param_get (data , parameter )
1886+ def check_empty_list_item (parameter , value , attrs ):
1887+ allow_empty_list_item = (attrs if attrs is not None else {}).get (
1888+ "allow_empty_list_item" ,
1889+ False
1890+ )
1891+ if isinstance (value , list ):
1892+ empty = [v for v in value if v == "" ]
1893+ if empty :
1894+ if not allow_empty_list_item :
1895+ self .ansible_module .fail_json (
1896+ msg = "Parameter '%s' contains an empty string" &
1897+ parameter
1898+ )
1899+ elif len (value ) > 1 :
1900+ self .ansible_module .fail_json (
1901+ msg = "Parameter '%s' may not contain another "
1902+ "entry together with an empty string" % parameter
1903+ )
1904+ return value
1905+
1906+ def get_entry_param_value (parameter , conversions , attrs = None ):
1907+ _value = check_empty_list_item (
1908+ parameter ,
1909+ param_get (data , parameter ),
1910+ attrs
1911+ )
18331912 if _value and conversions :
18341913 for fn in conversions :
18351914 _value = fn (_value )
18361915 return _value
18371916
18381917 # Build 'parameter: value' mapping for all module parameters
18391918 _entry = {
1840- parameter : get_entry_param_value (parameter , conversions )
1919+ parameter : get_entry_param_value (
1920+ parameter ,
1921+ conversions ,
1922+ self .attrs .get (parameter )
1923+ )
18411924 for parameter , conversions in self .convert .items ()
18421925 }
18431926
0 commit comments