@ -26,10 +26,9 @@
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import os
from functools import partial
from ansible . module_utils . _text import to_native
from ansible . module_utils . six import iteritems
from ansible . module_utils . _text import to_text
@ -40,7 +39,15 @@ try:
except ImportError :
HAS_INFOBLOX_CLIENT = False
nios_provider_spec = {
# defining nios constants
NIOS_DNS_VIEW = ' view '
NIOS_NETWORK_VIEW = ' networkview '
NIOS_HOST_RECORD = ' record:host '
NIOS_IPV4_NETWORK = ' network '
NIOS_IPV6_NETWORK = ' ipv6network '
NIOS_ZONE = ' zone_auth '
NIOS_PROVIDER_SPEC = {
' host ' : dict ( ) ,
' username ' : dict ( ) ,
' password ' : dict ( no_log = True ) ,
@ -57,10 +64,8 @@ nios_provider_spec = {
def get_connector ( * args , * * kwargs ) :
''' Returns an instance of infoblox_client.connector.Connector
: params args : positional arguments are silently ignored
: params kwargs : dict that is passed to Connector init
: returns : Connector
'''
if not HAS_INFOBLOX_CLIENT :
@ -68,12 +73,11 @@ def get_connector(*args, **kwargs):
' to be installed. It can be installed using the '
' command `pip install infoblox-client` ' )
if not set ( kwargs . keys ( ) ) . issubset ( nios_provider_spec . keys ( ) ) :
if not set ( kwargs . keys ( ) ) . issubset ( NIOS_PROVIDER_SPEC . keys ( ) ) :
raise Exception ( ' invalid or unsupported keyword argument for connector ' )
for key , value in iteritems ( nios_provider_spec ) :
for key , value in iteritems ( NIOS_PROVIDER_SPEC ) :
if key not in kwargs :
# apply default values from nios_provider_spec since we cannot just
# apply default values from NIOS_PROVIDER_SPEC since we cannot just
# assume the provider values are coming from AnsibleModule
if ' default ' in value :
kwargs [ key ] = value [ ' default ' ]
@ -89,11 +93,9 @@ def get_connector(*args, **kwargs):
def normalize_extattrs ( value ) :
''' Normalize extattrs field to expected format
The module accepts extattrs as key / value pairs . This method will
transform the key / value pairs into a structure suitable for
sending across WAPI in the format of :
extattrs : {
key : {
value : < value >
@ -105,29 +107,23 @@ def normalize_extattrs(value):
def flatten_extattrs ( value ) :
''' Flatten the key/value struct for extattrs
WAPI returns extattrs field as a dict in form of :
extattrs : {
key : {
value : < value >
}
}
This method will flatten the structure to :
extattrs : {
key : value
}
'''
return dict ( [ ( k , v [ ' value ' ] ) for k , v in iteritems ( value ) ] )
class WapiBase ( object ) :
''' Base class for implementing Infoblox WAPI API '''
provider_spec = { ' provider ' : dict ( type = ' dict ' , options = nios_provider_spec ) }
provider_spec = { ' provider ' : dict ( type = ' dict ' , options = NIOS_PROVIDER_SPEC ) }
def __init__ ( self , provider ) :
self . connector = get_connector ( * * provider )
@ -163,7 +159,6 @@ class WapiInventory(WapiBase):
class WapiModule ( WapiBase ) :
''' Implements WapiBase for executing a NIOS module '''
def __init__ ( self , module ) :
self . module = module
provider = module . params [ ' provider ' ]
@ -174,29 +169,29 @@ class WapiModule(WapiBase):
def handle_exception ( self , method_name , exc ) :
''' Handles any exceptions raised
This method will be called if an InfobloxException is raised for
any call to the instance of Connector . This method will then
gracefully fail the module .
any call to the instance of Connector and also , in case of generic
exception . This method will then gracefully fail the module .
: args exc : instance of InfobloxException
'''
self . module . fail_json (
msg = exc . response [ ' text ' ] ,
type = exc . response [ ' Error ' ] . split ( ' : ' ) [ 0 ] ,
code = exc . response . get ( ' code ' ) ,
operation = method_name
)
if ( ' text ' in exc . response ) :
self . module . fail_json (
msg = exc . response [ ' text ' ] ,
type = exc . response [ ' Error ' ] . split ( ' : ' ) [ 0 ] ,
code = exc . response . get ( ' code ' ) ,
operation = method_name
)
else :
self . module . fail_json ( msg = to_native ( exc ) )
def run ( self , ib_obj_type , ib_spec ) :
''' Runs the module and performans configuration tasks
: args ib_obj_type : the WAPI object type to operate against
: args ib_spec : the specification for the WAPI object as a dict
: returns : a results dict
'''
update = new_name = None
state = self . module . params [ ' state ' ]
if state not in ( ' present ' , ' absent ' ) :
self . module . fail_json ( msg = ' state must be one of `present`, `absent`, got ` %s ` ' % state )
@ -204,10 +199,14 @@ class WapiModule(WapiBase):
result = { ' changed ' : False }
obj_filter = dict ( [ ( k , self . module . params [ k ] ) for k , v in iteritems ( ib_spec ) if v . get ( ' ib_req ' ) ] )
ib_obj = self . get_object ( ib_obj_type , obj_filter . copy ( ) , return_fields = ib_spec . keys ( ) )
if ib_obj :
current_object = ib_obj [ 0 ]
if ( ' name ' in obj_filter ) :
ib_obj_ref , update , new_name = self . get_object_ref ( ib_obj_type , obj_filter , ib_spec )
else :
ib_obj_ref = self . get_object ( ib_obj_type , obj_filter . copy ( ) , return_fields = ib_spec . keys ( ) )
if ib_obj_ref :
current_object = ib_obj_ref [ 0 ]
if ' extattrs ' in current_object :
current_object [ ' extattrs ' ] = flatten_extattrs ( current_object [ ' extattrs ' ] )
ref = current_object . pop ( ' _ref ' )
@ -223,25 +222,28 @@ class WapiModule(WapiBase):
else :
proposed_object [ key ] = self . module . params [ key ]
modified = not self . compare_objects ( current_object , proposed_object )
# checks if the name's field has been updated
if update and new_name :
proposed_object [ ' name ' ] = new_name
res = None
modified = not self . compare_objects ( current_object , proposed_object )
if ' extattrs ' in proposed_object :
proposed_object [ ' extattrs ' ] = normalize_extattrs ( proposed_object [ ' extattrs ' ] )
if state == ' present ' :
if ref is None :
if not self . module . check_mode :
self . create_object ( ib_obj_type , proposed_object )
result [ ' changed ' ] = True
elif modified :
if ' network_view ' in proposed_object :
self . check_if_network_view_exists ( proposed_object [ ' network_view ' ] )
proposed_object . pop ( ' network_view ' )
elif ' view ' in proposed_object :
self . check_if_dns_view_exists ( proposed_object [ ' view ' ] )
if not self . module . check_mode :
if ( ib_obj_type in ( NIOS_HOST_RECORD , NIOS_NETWORK_VIEW , NIOS_DNS_VIEW ) ) :
proposed_object = self . on_update ( proposed_object , ib_spec )
res = self . update_object ( ref , proposed_object )
elif ' network_view ' in proposed_object :
proposed_object . pop ( ' network_view ' )
if not self . module . check_mode and res is None :
proposed_object = self . on_update ( proposed_object , ib_spec )
self . update_object ( ref , proposed_object )
result [ ' changed ' ] = True
elif state == ' absent ' :
@ -252,42 +254,10 @@ class WapiModule(WapiBase):
return result
def check_if_dns_view_exists ( self , name , fail_on_missing = True ) :
''' Checks if the specified DNS view is already configured
: args name : the name of the DNS view to check
: args fail_on_missing : fail the module if the DNS view does not exist
: returns : True if the network_view exists and False if the DNS view
does not exist and fail_on_missing is False
'''
res = self . get_object ( ' view ' , { ' name ' : name } ) is not None
if not res and fail_on_missing :
self . module . fail_json ( msg = ' DNS view %s does not exist, please create '
' it using nios_dns_view first ' % name )
return res
def check_if_network_view_exists ( self , name , fail_on_missing = True ) :
''' Checks if the specified network_view is already configured
: args name : the name of the network view to check
: args fail_on_missing : fail the module if the network_view does not exist
: returns : True if the network_view exists and False if the network_view
does not exist and fail_on_missing is False
'''
res = self . get_object ( ' networkview ' , { ' name ' : name } ) is not None
if not res and fail_on_missing :
self . module . fail_json ( msg = ' Network view %s does not exist, please create '
' it using nios_network_view first ' % name )
return res
def issubset ( self , item , objects ) :
''' Checks if item is a subset of objects
: args item : the subset item to validate
: args objects : superset list of objects to validate against
: returns : True if item is a subset of one entry in objects otherwise
this method will return None
'''
@ -322,16 +292,45 @@ class WapiModule(WapiBase):
return True
def get_object_ref ( self , ib_obj_type , obj_filter , ib_spec ) :
''' this function gets and returns the current object based on name/old_name passed '''
update = False
old_name = new_name = None
try :
name_obj = self . module . _check_type_dict ( obj_filter [ ' name ' ] )
old_name = name_obj [ ' old_name ' ]
new_name = name_obj [ ' new_name ' ]
except TypeError :
name = obj_filter [ ' name ' ]
if old_name and new_name :
if ( ib_obj_type == NIOS_HOST_RECORD ) :
test_obj_filter = dict ( [ ( ' name ' , old_name ) , ( ' view ' , obj_filter [ ' view ' ] ) ] )
else :
test_obj_filter = dict ( [ ( ' name ' , old_name ) ] )
# get the object reference
ib_obj = self . get_object ( ib_obj_type , test_obj_filter , return_fields = ib_spec . keys ( ) )
if ib_obj :
obj_filter [ ' name ' ] = new_name
else :
test_obj_filter [ ' name ' ] = new_name
ib_obj = self . get_object ( ib_obj_type , test_obj_filter , return_fields = ib_spec . keys ( ) )
update = True
return ib_obj , update , new_name
if ( ib_obj_type == NIOS_HOST_RECORD ) :
test_obj_filter = dict ( [ ( ' name ' , name ) , ( ' view ' , obj_filter [ ' view ' ] ) ] )
else :
test_obj_filter = dict ( [ ( ' name ' , name ) ] )
ib_obj = self . get_object ( ib_obj_type , test_obj_filter . copy ( ) , return_fields = ib_spec . keys ( ) )
return ib_obj , update , new_name
def on_update ( self , proposed_object , ib_spec ) :
''' Event called before the update is sent to the API endpoing
This method will allow the final proposed object to be changed
and / or keys filtered before it is sent to the API endpoint to
be processed .
: args proposed_object : A dict item that will be encoded and sent
the the API endpoint with the updated data structure
: returns : updated object to be sent to API endpoint
'''
keys = set ( )
@ -339,5 +338,4 @@ class WapiModule(WapiBase):
update = ib_spec [ key ] . get ( ' update ' , True )
if not update :
keys . add ( key )
return dict ( [ ( k , v ) for k , v in iteritems ( proposed_object ) if k not in keys ] )