
    Vh                       d dl mZmZmZ eZ	 d dlmZ dZ	 d dl
mZmZ dZd dlmZ d dlmZ d dlmZmZ 	 d d	lZd d	lZdZd d	lZd d	lZd d	lZd d	lZd d	lZd d	lZd d	lZd d	l Z  G d
 d      Z!d Z"d Z#d Z$d Z%d Z&d Z'd Z(e(fdZ)d Z*d Z+d Z,d Z-d Z.d Z/d!dZ0dZ1dZ2 G d de3      Z4d Z5e6d k(  r e5        y	y	# e	$ r dZY w xY w# e	$ r dZY w xY w# e	$ r dZY w xY w)"    )absolute_importdivisionprint_function)FernetTF)api
exceptions)	to_native)
validation)ABCMetaabstractmethodNc                      e Zd ZdZeZdZd Zd Zd Z	d Z
d Zd Zed	        Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd ZdPdZ d Z!d Z"d Z#d Z$d  Z%d! Z&dQd"Z'd# Z(d$ Z)d% Z*d& Z+d' Z,dRd)Z-dRd*Z.d+ Z/dSd,Z0d- Z1d. Z2d/ Z3d0 Z4d1 Z5d2 Z6d3 Z7d4 Z8d5 Z9d6 Z:d7 Z;d8 Z<d9 Z=d: Z>d; Z?d< Z@dTd=ZAd> ZBd? ZCd@ ZDdA ZEdB ZFdC ZGdD ZHdE ZIdF ZJdRdGZKdH ZLdI ZMdJ ZNdK ZOdL ZPdM ZQdTdNZRdO ZSy()UDnacBasezAClass contains members which can be reused for all intent modulesFc           	         || _         |j                  | _        t        j                  |j                  j	                  d            | _        i | _        i | _        g | _        d| _	        d| _
        | j                  | j                        }t        |      | _        d| j                  j                  i| _        | j                   | j"                  | j$                  | j&                  | j(                  | j*                  | j,                  d| _        | j0                  | j2                  | j4                  | j6                  | j8                  | j:                  | j<                  d| _        |j	                  d      | _         | j                  j	                  d      | _!        |j                  | _"        tG        | jD                  j	                  d	      jI                  d
d            | _%        tG        | jD                  j	                  d	      jI                  d
d            | _&        | jD                  j	                  d	      | _'        ddddddd| _(        | jP                  jS                         D ]%  \  }}tU        | d|jI                  d
d      z   |       ' | j@                  rtV        jX                  s|j	                  d      xs d| _-        | jZ                  j]                         | _-        | j_                          |j	                  d      xs d| _0        | jc                          |j	                  d      sdnd| _2        | jg                  d       ti        jj                  d      | _6        dtV        _,        | jo                  dd       nGti        jj                  d      | _6        | jl                  jq                  ti        jr                                | jo                  d ju                  |      d       g d| _;        d!g g g d"| _<        y )#Nconfig success)paramsexec)mergeddeletedreplaced
overriddengatheredrenderedparseddnac_logdnac_api_task_timeoutdnac_version.i  i  i	  i1	  iH	  iK	  )z2.2.2.32.2.3.3z2.3.3.02.3.5.3z2.3.7.6z2.3.7.9version__dnac_log_levelWARNINGdnac_log_file_pathzdnac.logdnac_log_appendwaloggerTz Logging configured and initiatedDEBUGempty_loggerz%Cisco Catalyst Center parameters: {0}F)changeddiffresponsewarnings)=moduler   copydeepcopygetr   havewantvalidated_configmsgstatusget_dnac_paramsDNACSDKdnac_exec
dnac_applyget_diff_mergedget_diff_deletedget_diff_replacedget_diff_overriddenget_diff_gatheredget_diff_renderedget_diff_parsedget_diff_state_applyverify_diff_mergedverify_diff_deletedverify_diff_replacedverify_diff_overriddenverify_diff_gatheredverify_diff_renderedverify_diff_parsedverify_diff_state_applyr   max_timeoutpayloadintreplacer   dnac_version_in_integerdnac_version_in_stringdnac_versionsitemssetattrr   _DnacBase__is_log_initr$   uppervalidate_dnac_log_levelr&   validate_dnac_log_file_pathdnac_log_modesetup_loggerlogging	getLoggerr*   log
addHandlerNullHandlerformatsupported_statesresult)selfr1   dnac_paramsversion_keyversion_values        h/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/cisco/dnac/plugins/module_utils/dnac.py__init__zDnacBase.__init__0   s>   mmmmFMM$5$5h$?@		 "**4;;7;/	!499??3/3/C/C040E0E151G1G373K3K151G1G151G1G/3/C/C%'! 372I2I373K3K484M4M6:6Q6Q484M4M484M4M262I2I(*$ $
3;;??+BC}} 0 0 @ H Hb QR'*4<<+;+;N+K+S+STWY[+\']$&*ll&6&6~&F#
 
 +/*<*<*B*B*D 	U&KD*{':':3'DDmT	U ==!7!7"-//2B"C"PyD"&"5"5";";"=D((*&1oo6J&K&YzD#,,.,7OO<M,NTWDh'!++H5DK%)H"HH7A "++N;DKKK""7#6#6#898??LgV q"'QST    c                    t        t        t        |j                  d                  }t        t        t        |j                  d                  }t	        ||      D ]  \  }}||kD  r y||k  s y t        |      t        |      kD  r"t        d |t        |      d D              rdS dS t        |      t        |      kD  r"t        d |t        |      d D              rdS dS y)aE  
        Compare two DNAC version strings.

        param version1: str, the first version string to compare (e.g., "2.3.5.3")
        param version2: str, the second version string to compare (e.g., "2.3.7.6")
        return: int, returns 1 if version1 > version2, -1 if version1 < version2, and 0 if they are equal
        r      c              3   &   K   | ]	  }|d kD    ywr   N .0parts     rj   	<genexpr>z1DnacBase.compare_dnac_versions.<locals>.<genexpr>   s     JD1HJ   Nr   c              3   &   K   | ]	  }|d kD    ywrq   rr   rs   s     rj   rv   z1DnacBase.compare_dnac_versions.<locals>.<genexpr>   s     K$TAXKrw   )listmaprQ   splitziplenany)rf   version1version2v1_partsv2_partsv1v2s          rj   compare_dnac_versionszDnacBase.compare_dnac_versionsx   s     C!456C!456 (H- 	FBBwb		 x=3x=(J#h-.1IJJ1QPQQ]S]*K(3x=>2JKK2RQRR rl   c                 8    | j                   j                  d      S )Nr   )rP   r4   rf   s    rj   get_ccc_versionzDnacBase.get_ccc_version   s    ||//rl   c                     | j                   S N)rT   r   s    rj   get_ccc_version_as_stringz"DnacBase.get_ccc_version_as_string   s    ***rl   c                     | j                   S r   )rS   r   s    rj   get_ccc_version_as_integerz#DnacBase.get_ccc_version_as_integer   s    +++rl   c                 8    | j                   j                  |      S r   )rU   r4   )rf   r   s     rj   get_ccc_version_as_int_from_strz(DnacBase.get_ccc_version_as_int_from_str   s    !!%%l33rl   c                 <    | j                   sd| _        d| _        | S y )Nz/config not available in playbook for validationfailed)r   r8   r9   r   s    rj   validate_inputzDnacBase.validate_input   s!    {{HDH"DKK rl   c                     d| _         | S NTr   r   s    rj   r?   zDnacBase.get_diff_merged       rl   c                     d| _         | S r   r   r   s    rj   r@   zDnacBase.get_diff_deleted       rl   c                     d| _         | S r   r   r   s    rj   rA   zDnacBase.get_diff_replaced       rl   c                     d| _         | S r   r   r   s    rj   rB   zDnacBase.get_diff_overridden       rl   c                     d| _         | S r   r   r   s    rj   rC   zDnacBase.get_diff_gathered   r   rl   c                     d| _         | S r   r   r   s    rj   rD   zDnacBase.get_diff_rendered   r   rl   c                     d| _         | S r   r   r   s    rj   rE   zDnacBase.get_diff_parsed   r   rl   c                     d| _         | S r   r   r   s    rj   rG   zDnacBase.verify_diff_merged   r   rl   c                     d| _         | S r   r   r   s    rj   rH   zDnacBase.verify_diff_deleted   r   rl   c                     d| _         | S r   r   r   s    rj   rI   zDnacBase.verify_diff_replaced   r   rl   c                     d| _         | S r   r   r   s    rj   rJ   zDnacBase.verify_diff_overridden   r   rl   c                     d| _         | S r   r   r   s    rj   rK   zDnacBase.verify_diff_gathered   r   rl   c                     d| _         | S r   r   r   s    rj   rL   zDnacBase.verify_diff_rendered   r   rl   c                     d| _         | S r   r   r   s    rj   rM   zDnacBase.verify_diff_parsed   r   rl   c                    t         j                  t         j                  t         j                  t         j                  t         j
                  d}|j                  | j                  t         j                        }t        j                  |      }t        j                  dd      }t        j                  | j                  | j                        }|j                  |       |j                  |       |j                  |       y)zMSet up a logger with specified name and configuration based on dnac_log_levelINFOr+   r%   ERRORCRITICALz%%(asctime)s %(levelname)s %(message)sz%m-%d-%Y %H:%M:%S)datefmt)modeN)r^   r   r+   r%   r   r   r4   r$   r_   	FormatterFileHandlerr&   r\   setFormattersetLevelra   )rf   logger_namelevel_mappinglevelr*   	formatterfile_handlers          rj   r]   zDnacBase.setup_logger   s     LL]]]]((
 !!$"5"5wG"";/%%&MWjk	**4+B+BI[I[\!!),,'rl   c                 h    | j                   dvr$t        dj                  | j                               y)z>Validates if the logging level is string and of expected valuer   z'Invalid log level: 'dnac_log_level:{0}'N)r$   
ValueErrorrc   r   s    rj   rZ   z DnacBase.validate_dnac_log_level  s4    &WWFMMdNaNabcc Xrl   c                    t         j                  j                  | j                        }t         j                  j	                  |      }t         j                  j                  |      st        dj                  |            y)z
        Validates the specified log file path, ensuring it is either absolute or relative,
        the directory exists, and has a .log extension.
        z0The directory for log file '{0}' does not exist.N)ospathabspathr&   dirnameexistsFileNotFoundErrorrc   )rf   r&   log_directorys      rj   r[   z$DnacBase.validate_dnac_log_file_path  s_      WW__T-D-DE (:;ww~~m,#$V$]$]^p$qrr -rl   c           	      P   | j                   r| j                  j                  }t        j                         d|z      }|d   }t        j
                  |      }d|d|j                  d|j                  d|d	}t        | j                  |j                               }	 |	|       yy)a  Logs formatted messages with specified log level and incrementing the call stack frame
        Args:
            self (obj, required): An instance of the DnacBase Class.
            message (str, required): The log message to be recorded.
            level (str, optional): The log level, default is "info".
                                   The log level can be one of 'DEBUG', 'INFO', 'WARNING', 'ERROR', or 'CRITICAL'.
        rn   r    : z 
N)r   	__class____name__inspectstackgetframeinfofunctionlinenogetattrr*   lower)
rf   messager   frameIncrement
class_namecallerframerecordframeinfolog_message
log_methods
             rj   r`   zDnacBase.log  s     == 00J 'N0B C%a(E''.D2<dmmT[[ZabK ekkm<J{# rl   c                 r   t        j                         j                  }|j                  }| j	                  dj                  || j                  | j                        d       d| j                  v rB| j                  j                  | j                  | j                  j                  dg              y	d| j                  v r' | j                  j                  d
i | j                   y	d| j                  v rB| j                  j                  | j                  | j                  j                  dg              y	y	)z=API to check the return status value and exit/fail the modulez/Line No: {line_no} status: {status}, msg: {msg})line_nor9   r8   r+   r   r/   r8   r/   exitedinvalidNrr   )r   currentframef_backf_linenor`   rc   r9   r8   r1   	fail_jsonre   r4   	exit_json)rf   r   r   s      rj   check_return_statuszDnacBase.check_return_status(  s     $$&--..=VGDKKTXXVF	
 t{{"KK!!dhhUW9X!Y$!DKK!!0DKK0$++%KK!!dhhUW9X!Y &rl   c                 6    d}t        j                  ||      duS )ai  
        Check if a password is valid.
        Args:
            self (object): An instance of a class that provides access to Cisco Catalyst Center.
            password (str): The password to be validated.
        Returns:
            bool: True if the password is valid, False otherwise.
        Description:
            The function checks the validity of a password based on the following criteria:
            - Minimum 8 characters.
            - At least one lowercase letter.
            - At least one uppercase letter.
            - At least one digit.
            - At least one special character
        zJ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[-=\\;,./~!@#$%^&*()_+{}[\]|:?]).{8,}$Nrematch)rf   passwordpatterns      rj   is_valid_passwordzDnacBase.is_valid_password9  s     " `xx*$66rl   c                 6    d}t        j                  ||      ryy)a,  
        Validate an email address.
        Args:
            self (object): An instance of a class that provides access to Cisco Catalyst Center.
            email (str): The email address to be validated.
        Returns:
            bool: True if the email is valid, False otherwise.
        Description:
            This function checks if the provided email address is valid based on the following criteria:
            - It contains one or more alphanumeric characters or allowed special characters before the '@'.
            - It contains one or more alphanumeric characters or dashes after the '@' and before the domain.
            - It contains a period followed by at least two alphabetic characters at the end of the string.
        The allowed special characters before the '@' are: ._%+-.
        z0^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$TFr   )rf   emailr   s      rj   is_valid_emailzDnacBase.is_valid_emailN  s    " F88GU#rl   c                 l   |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d	      |j                  d
      |j                  d      d}|S )z<Store the Cisco Catalyst Center parameters from the playbook	dnac_host	dnac_portdnac_usernamednac_passwordr   dnac_verify
dnac_debugr   r$   r&   r'   )r   r   r   r   r   r   r   r   r$   r&   r'   )r4   )rf   r   rg   s      rj   r:   zDnacBase.get_dnac_paramsf  s     %+JJ{$;$*JJ{$;(.

?(C(.

?(C'-zz.'A&,jj&?%+ZZ%=#)::j#9)/4D)E-3ZZ8L-M*0**5F*G rl   c                    d}	 | j                   j                  ddd|id      }| j                  dj                  ||      d       t	        |t
              s#| j                  d	j                  |      d
       |S |j                  d      }| j                  dj                  |      d       |S # t        $ rU}| j                          dj                  t        |            | _
        | j                  | j                         Y d}~|S d}~ww xY w)ah  
        Get the details of a specific task in Cisco Catalyst Center.
        Args:
            self (object): An instance of a class that provides access to Cisco Catalyst Center.
            task_id (str): The unique identifier of the task for which you want to retrieve details.
        Returns:
            dict or None: A dictionary containing detailed information about the specified task,
            or None if the task with the given task_id is not found.
        Description:
            If the task with the specified task ID is not found in Cisco Catalyst Center, this function will return None.
        Ntaskget_task_by_idtask_idT)familyr   r   op_modifieszURetrieving task details by the API 'get_task_by_id' using task ID: {0}, Response: {1}r+   /Failed to retrieve task details for task ID: {}r   r/   z'Successfully retrieved Task status: {0}rAn error occurred while executing API call to Function: 'get_tasks_by_id' due to the the following exception: {0}.r<   r=   r`   rc   
isinstancedictr4   	Exceptionlog_tracebackstrr8   fail_and_exitrf   r   task_statusr/   es        rj   get_task_detailszDnacBase.get_task_detailsw  s    	)yy)!7+ 	 ' H HHlfWh/: h-JQQRYZ\cd""",,z2KHH>EEkRT[\   	) ;;A6#a&> H txx((	)s   A5B. :2B. .	D7A
DDc                 
    d}|S )a-  
        Retrieves the limit for 'get_device_list' API to collect the device details..
        Args:
            self (object): An instance of a class that provides access to Cisco Catalyst Center.
        Returns:
            int: The limit for 'get_device_list' api device details, which is set to 500 by default.
        Description:
            This method returns a predefined limit for the number of device details that can be processed or retrieved
            from 'get_device_list' api. Currently, the limit is set to a fixed value of 500.
          rr   )rf   api_response_limits     rj   get_device_details_limitz!DnacBase.get_device_details_limit  s     !!!rl   c                    |s dj                  |      | _        d| _        | S t        |t              s dj                  |      | _        d| _        | S |j                  d      }|j                  d      .|j                  d      j                  d      | _        d| _        | S |j                  d      }t        j                         }	 t        j                         }||z
  | j                  k\  r[d
j                  | j                  |      dj                  |      z   | _        | j                  | j                  d       d| _        	 | S | j                  |      }| j                  dj                  ||      d       |j                  d      d	u r|j                  d      rt        |j                  d            | _        d}	|	| j                  j                         v rdt        j                  | j                  j                  d             | j                  |      | _        nt        |j                  d            | _        d| _        	 | S ||j                  d      j                         v r3d	| j                  d<   |d	u r|j                  d      | _        d| _        	 | S | j                  dj                  |j                  d      |      d       
)a  
        Get the site id from the site name.
        Args:
            self - The current object details.
            response (dict) - API response.
            validation_string (str) - String used to match the progress status.
            api_name (str) - API name.
            data (bool) - Set to True if the API is returning any information. Else, False.
        Returns:
            self
        0The response from the API '{api_name}' is empty.api_namer   ;The response from the API '{api_name}' is not a dictionary.r/   	errorcodedetailtaskIdTzJMax timeout of {max_timeout} sec has reached for the task id '{task_id}'. )rO   r   ;Exiting the loop due to unexpected API '{api_name}' status.r%   z*Getting task details from task ID {0}: {1}r+   isErrorfailureReasonzcheck task treednac_task_poll_intervalprogressr-   datar    Progress is {0} for task ID: {1})rc   r8   r9   r   r  r4   timerO   r`   r	  r  r   sleepr   check_task_tree_responsere   )
rf   r/   validation_stringr  r  r   
start_timeend_timetask_detailsstring_checks
             rj   check_task_response_statusz#DnacBase.check_task_response_status  s    B* H #DKK(D)M* H #DKK<<
+<<$0||J/33H=DH"DKK,,x(YY[
yy{H:%$*:*::g"Ft/?/?FQX__iq_rs 9-&4 1  009LHHAHHR^_ahi	*d2##O4"<#3#3O#DEDH#4L#txx~~'77

4;;??3L#MN#'#@#@#I"<#3#3J#?@DH&  !L$4$4Z$@$F$F$HH)-I&4<+//7DH'  HH7>>|?O?OPZ?[]degnoA rl   c                 l    | j                   j                          | j                  j                          y)z1Reset all neccessary attributes to default valuesN)r5   clearr6   r   s    rj   reset_valueszDnacBase.reset_values  s      					rl   c                 R   d}	 | j                   j                  ddd|i      }| j                  dj                  ||      d       |S # t        $ rU}| j                          dj                  t        |            | _        | j                  | j                         Y d}~|S d}~ww xY w)	z
        Get the execution details of an API
        Args:
            exec_id (str) - Id for API execution
        Returns:
            response (dict) - Status for API execution
        Nr   "get_business_api_execution_detailsexecution_idr   r   r   z}Successfully retrieved execution details by the API 'get_business_api_execution_details' for execution ID: {0}, Response: {1}r+   zAn error occurred while executing API call to Function: 'get_business_api_execution_details' due to the the following exception: {0}.)	r<   r=   r`   rc   r  r  r  r8   r  )rf   exec_idr/   r  s       rj   get_execution_detailszDnacBase.get_execution_details  s     	)yy=&0 ' H
 HH  UfWh/:   	) ;;A6#a&> H txx((	)s   AA 	B&A
B!!B&c                    |s dj                  |      | _        d| _        | S t        |t              s dj                  |      | _        d| _        | S |j                  d      }t        j                         }	 t        j                         }||z
  | j                  k\  r[dj                  | j                  |      d	j                  |      z   | _        | j                  | j                  d
       d| _        	 | S | j                  |      }|j                  d      dk(  r d| j                  d<   d| _        d| _        	 | S |j                  d      r |j                  d      | _        d| _        	 | S )z
        Checks the response status provided by API in the Cisco Catalyst Center
        Args:
            response (dict) - API response
            api_name (str) - API name
        Returns:
            self
        r  r  r   r  executionIdTzTMax timeout of {max_timeout} sec has reached for the execution id '{execution_id}'. )rO   r+  r  r%   r9   SUCCESSr-   zSuccessfully executedr   	bapiError)rc   r8   r9   r   r  r4   r  rO   r`   r.  re   )rf   r/   r  r+  r!  r"  execution_detailss          rj   check_execution_response_statusz(DnacBase.check_execution_response_status  sm    B* H #DKK(D)M* H #DKK||M2YY[
yy{H:%$*:*::q"Ft/?/?lF[X__iq_rs 9-&  !% : :< H $$X.);)-I&2'  !$$[1,00=&- rl   c                     	 t        j                  |      }t        |t              r|S 	 y# t         j                  $ r Y yw xY w)a  
        Check whether the input is string dictionary or string.
        Args:
            task_details_data (string) - Input either string dictionary or string.
        Returns:
            value (dict) - If the input is string dictionary, else returns None.
        N)jsonloadsr   r  JSONDecodeError)rf   task_details_datavalues      rj   check_string_dictionaryz DnacBase.check_string_dictionaryK  sL    	JJ01E%& '  ## 		s   &+ A Ac                 P   	 | j                  |      }| j                         | j                  d      k  rX|j                  d      }|d   j                  d      }|D ]+  }|d   dk(  s|j                  d      j                  d      }- 	 S | j	                  d	j                  t        |            d
       |j                  d      }|d   j                  d      }	 |S # t        $ r=}dj                  ||      | _        | j                  | j                         Y d}~S d}~ww xY w)a  
        Get the type of a site in Cisco Catalyst Center.
        Args:
            self (object): An instance of a class used for interacting with Cisco Catalyst Center.
            site_name (str): The name of the site for which to retrieve the type.
        Returns:
            site_type (str or None): The type of the specified site, or None if the site is not found.
        Description:
            This function queries Cisco Catalyst Center to retrieve the type of a specified site. It uses the
            get_site API with the provided site name, extracts the site type from the response, and returns it.
            If the specified site is not found, the function returns None, and an appropriate log message is generated.
        r!   r/   r   additionalInfo	nameSpaceLocation
attributestypez+Received API response from 'get_sites': {0}r+   z?An exception occurred while fetching the site '{0}'. Error: {1}N)
get_siter   r   r4   r`   rc   r  r  r8   r  )rf   	site_namer/   sitesite_additional_infoitem	site_typer  s           rj   get_sites_typezDnacBase.get_sites_type\  s    	)}}Y/H..0D4X4XYb4cc||J/'+Aw{{3C'D$0 GDK(J6$(HH\$:$>$>v$F	G  FMMcRZm\^ef||J/ GKK/	 	  	)X__`iklmDHtxx((		)s%   A%C ("C AC 	D%(2D  D%Nc                    g }d}|~| j                  dj                  |      d       | j                  |      \  }}|s%| j                  dj                  |      d       ||fS | j                  dj                  ||      d       | j                  dj                  |      d       | j                  | j                  k  r| j                  dj                  | j                        d       d	|i}| j                  d
d|      }| j                  dd       |rRd|v rN|j                  dg       D ]9  }|j                  dg       D ]"  }|j                  |j                  d             $ ; | j                  dj                  ||      d       n| j                  dj                  | j                        d       d	|i}	| j                  dd|	      }| j                  dd       |r;d|v r7|j                  dg       D ]"  }|j                  |j                  d             $ | j                  dj                  ||      d       |s"| j                  dj                  ||      d       ||fS )a{  
        Retrieve device IDs associated with a specific site in Cisco Catalyst Center.
        Args:
            site_id (str): The unique identifier of the site.
        Returns:
            tuple: A tuple containing the API response and a list of device IDs associated with the site.
                Returns an empty list if no devices are found or if an error occurs.
        N>Site ID not provided. Retrieving Site ID for site name: '{0}'.r+   @Site '{0}' does not exist, cannot proceed with device retrieval.r   ,Retrieved site ID '{0}' for site name '{1}'.6Initiating retrieval of device IDs for site ID: '{0}'.z>Using 'get_membership' API for Catalyst Center version: '{0}'.site_idsitesget_membershipz?Received response from 'get_membership'. Extracting device IDs.devicer/   instanceUuidz8Retrieved device IDs from membership for site '{0}': {1}zFUsing 'get_site_assigned_network_devices' API for DNAC version: '{0}'.site_design!get_site_assigned_network_deviceszRReceived response from 'get_site_assigned_network_devices'. Extracting device IDs.deviceIdz>Retrieved device IDs from assigned devices for site '{0}': {1}z4No devices found for site '{0}' with site ID: '{1}'.r%   )r`   rc   get_site_idr   version_2_3_5_3execute_get_requestr4   append)
rf   rC  rN  
device_idsapi_responsesite_existsget_membership_paramsrQ  rF  (get_site_assigned_network_devices_paramss
             rj   get_device_ids_from_sitez!DnacBase.get_device_ids_from_site}  s{    
 ?HHU\\]fgipq#'#3#3I#> G[[bbclmovw#Z//HHCJJ7T]^`ghIPPQXY[bc  4 44HHU\\]a]n]noqxy%.$8!33G=MOdeLHHVX_`L 8*..x< DF &

:r : D"))$((>*BCDD HHOVVW^`jkmtuHH]ddeievevw  zA  B8A77K433MCf  iQ  RLHHikrs
l :*..z2> >F%%fjj&<=> HHU\\]dfpqsz{HHKRRS\^efhqrZ''rl   c                    g }| j                  dj                  |      d       ||| j                  dj                  |      d       | j                  |      \  }}|s#| j                  dj                  |      d       |S | j                  dj                  ||      d       | j                  ||      \  }}|s1dj                  |      | _        | j                  | j                         | j                  d	j                  |t        |            d       |D ]  }| j                  d
j                  |      d       d|i}| j                  dd|      }	|	s1dj                  |      | _        | j                  | j                         |j                  |	j                  d             | j                  dj                  |      d        |S )ax  
        Retrieves device details for all devices within a specified site.
        Args:
            site_id (str): The ID of the site from which to retrieve device details.
        Returns:
            list: A list of device details retrieved from the specified site.
        Raises:
            SystemExit: If the API call to get device IDs or device details fails.
        rM  r   rJ  r+   rK  r   rL  zNNo response received from API call 'get_device_ids_from_site' for site ID: {0}z)Device IDs retrieved from site '{0}': {1}z<Initiating retrieval of device details for device ID: '{0}'.iddevicesget_device_by_idzHNo response received from API call 'get_device_by_id' for device ID: {0}r/   z.Device details retrieved for device ID: '{0}'.)
r`   rc   rV  r_  r8   r  r  rX  rY  r4   )
rf   rC  rN  device_details_listr\  r[  rZ  	device_idget_device_by_id_paramsdevice_infos
             rj   get_device_details_from_sitez%DnacBase.get_device_details_from_site  s    !IPPQXY[ab ?HHU\\]fgipq#'#3#3I#> G[[bbclmovw**HHCJJ7T]^`gh $(#@#@G#T jgnnovwDHtxx(<CCGSQ[_]_fg $ 	bIHHSZZ[degmn'+Y&7# 229>PRijKellmvw""488,  &&{z'BCHHELLYWY`a	b #"rl   c                 J   i }g }| j                  |      \  }}|s1dj                  |      | _        | j                  | j                         | j	                  dj                  |      d       | j                  ||      }| j	                  dj                  ||      d       |D ]  }|j                  d      }|j                  d      }	|j                  d      }
|j                  d	      }|j                  d
      }|
dk(  rF|dk(  rA|dk7  r|	||<   m|j                  |       dj                  ||      }| j	                  |d       |j                  |       dj                  ||
|      }| j	                  |d        |s!| j	                  dj                  |      d       ||fS )a  
        Retrieves a mapping of management IP addresses to instance IDs for reachable devices from a specified site.
        Args:
            site_id (str): The ID of the site from which to retrieve device details.
        Returns:
            tuple: A tuple containing:
                - dict: A mapping of management IP addresses to instance IDs for reachable devices.
                - list: A list of management IP addresses of skipped devices.
        z`Site '{0}' does not exist in the Cisco Catalyst Center, cannot proceed with device(s) retrieval.z:Initiating retrieval of device details for site ID: '{0}'.r   z0Device details retrieved for site ID: '{0}': {1}r+   managementIpAddressrR  reachabilityStatuscollectionStatusr   	ReachableManagedz
Unified APz*Skipping device {0} as its family is: {1}.r%   zHSkipping device {0} as its status is {1} or its collectionStatus is {2}.z'No reachable devices found at Site: {0})rV  rc   r8   r  r`   rh  r4   rY  )rf   rC  mgmt_ip_to_instance_id_mapskipped_devices_listr\  rN  rd  rg  management_ipinstance_uuidreachability_statuscollection_statusdevice_familyr8   s                 rj   get_reachable_devices_from_sitez(DnacBase.get_reachable_devices_from_site  s    &("!!%!1!1)!<gy  A  A  BK  LDHtxx(MTTU\]_ef #??	7SCJJ7Tghjqr / 	)K'OO,ABM'OON;M"-//2F"G +0B C'OOH5M #k16G96T L0@M.}=(//>FMM%}C HHS),$++M:`gg!#68I i(/	)2 *HH>EEgNPVW)+???rl   c           	      d   | j                  dj                  |      d       g }d}d\  }}}| j                  | j                  k  r2| j                  dj                  | j                        d       d\  }}}n1| j                  dj                  | j                        d       d\  }}}||d	|d
|i}| j                  dj                  |||      d       	 | j	                  |||      }	|	s%| j                  dj                  |d	         d       n| j                  dj                  t        |	j                  d            |d	         d       |j                  |	j                  d             t        |	j                  d            |k  r"| j                  dj                  |      d       n0||z  }||d	<   | j                  dj                  |d	         d       d}
|r1| j                  dj                  t        |      |      d       d|i}
|
S | j                  dj                  |      d       |
S )aS  
        Retrieve site details from Cisco Catalyst Center based on the provided site name.
        Args:
            - site_name (str): The name or hierarchy of the site to be retrieved
            - limit (int): Default value given as 500, it can be updated.
        Returns:
            - response (dict or None): The response from the API call, typically a dictionary containing site details.
                                       Returns None if an error occurs or if the response is empty.
        Criteria:
            - This function uses the Cisco Catalyst Center SDK to execute the 'get_sites'
              function from the 'site_design' family.
            - If the response is empty, a warning is logged.
            - Any exceptions during the API call are caught, logged as errors,
              and the function returns None.
        z:Initiating retrieval of site details for site name: '{0}'.r+   rn   )NNNz8Using 'get_site' API for Catalyst Center version: '{0}'.)rO  rB  namez9Using 'get_sites' API for Catalyst Center version: '{0}'.)rS  	get_sitesname_hierarchyoffsetlimitzESending initial API request: Family='{0}', Function='{1}', Params={2}z;No data received from API (Offset={0}). Exiting pagination.z+Received {0} site(s) from API (Offset={1}).r/   zOReceived less than limit ({0}) results, assuming last page. Exiting pagination.z0Incrementing offset to {0} for next API request.Nz1Total {0} site(s) retrieved for site name: '{1}'.z+No site details found for site name: '{0}'.r%   )r`   rc   r   rW  rX  r}   r4   extend)rf   rC  r|  response_allr{  
api_familyapi_function	param_keyrequest_paramsr/   site_responses              rj   rB  zDnacBase.get_site  s:     	M	"G	-.>+
L) 4 44HHOVD--.92M/JiHHPVD--.92^/Ji#Y&'5QX__n67>	@ //
L.YHVx 897DHHBIIHLL,-~h/GIJQSZ 898<<
+,u4j1eOF'-N8$HHGNNx(*+24% * HHHVC-y97D'6M  HHBII)TV_`rl   c                    	 | j                  |      }|1dj                  |      | _        | j                  | j                         | j	                  dj                  |t        |            d       |j                  d      }|d   j                  d      }d}||fS # t        $ r?}d	j                  ||      | _        | j                  | j                         Y d}~fS d}~ww xY w)
aX  
        Retrieve the site ID and check if the site exists in Cisco Catalyst Center based on the provided site name.
        Args:
            site_name (str): The name or hierarchy of the site to be retrieved.
        Returns:
            tuple (bool, str or None): A tuple containing:
                1. A boolean indicating whether the site exists (True if found, False otherwise).
                2. The site ID (str) if the site exists, or None if the site does not exist or an error occurs.
        Criteria:
            - This function calls `get_site()` to retrieve site details from the Cisco Catalyst Center SDK.
            - If the site exists, its ID is extracted from the response and returned.
            - If the site does not exist or if an error occurs, an error message is logged, and the function returns a status of 'failed'.
        Nz,No site details retrieved for site name: {0}z+Site details retrieved for site '{0}'': {1}r+   r/   r   ra  TzzAn exception occurred while retrieving Site details for Site '{0}' does not exist in the Cisco Catalyst Center. Error: {1})rB  rc   r8   r  r`   r  r4   r  )rf   rC  r/   rD  rN  r\  r  s          rj   rV  zDnacBase.get_site_id_  s    	)}}Y/H IPPQZ[""488,HHBII)UXYaUbcelm<<
+D1gkk$'GK W%%  	)#VIq1 H txx((W%%	)s   BB 	C$%2CC$c           
      p
   | j                  dj                  |      d       | j                  |      }|j                  d      sMdj                  |      | _        | j                  | j                  d       | j                  | j                         |d   d   j                  d      }| j                  dj                  ||      d       |d	vr>d
| _        | j                  | j                  d       | j                  | j                         | j                  dj                  |      d       | j                  |      }|sMdj                  |      | _        | j                  | j                  d       | j                  | j                         t        |j                               d   }dd|igi}| j                         | j                  d      k  rI	  | j                  d   ddd||d      }	| j                  dj                  |	      d       | j                  |	d       | j                  dk(  rud| j                  d<   dj                  t        |      |      | j                  d<   |	j                  d      | j                  d<   | j                  | j                  d   d       yd| j                  d<   dj                  t        |      |      | j                  d<   |	j                  d      | j                  d<   | j                  | j                  d   d       y||d#}| j                  d$j                  |t        |            d       	 | j&                  j)                  d%d&d|      }	| j                  d'j                  |t        |      t        |	d               d       | j+                  |	d()       | j                  d   ryd*j                  |t        |            | _        | j                  | j                  d       d | _        | j"                  j%                  | j                  !       y"# t         $ r}}
dj                  |t        |      t        |
            | _        | j                  | j                  d       d | _        | j"                  j%                  | j                  !       Y d"}
~
y"d"}
~
ww xY w# t         $ rr}
d+j                  |t        |            }| j                  |t        |
      z   d       t        |
      }d | _        | j"                  j%                  ||,       Y d"}
~
y"d"}
~
ww xY w)-a  
        Assign devices to the specified site.
        Args:
            self (object): An instance of a class used for interacting with Cisco Catalyst Center.
            device_ids (list): A list of device IDs to assign to the specified site.
            site_name (str): The complete path of the site location.
            site_id (str): The ID of the specified site location.
        Returns:
            bool: True if the devices are successfully assigned to the site, otherwise False.
        Description:
            Assigns the specified devices to the site. If the assignment is successful, returns True.
            Otherwise, logs an error and returns False along with error details.
        zFetching site details for '{0}'r+   r/   z,Invalid site response received for site: {0}r   r   rA  zSite '{0}' found with type: {1})buildingfloorz0Device(s) can only be assigned to building/floorz*Retrieving IP addresses for device IDs: {}z/No valid IP addresses found for device IDs: {0}rQ  ipr!   r   rO  assign_devices_to_siteT)rN  rP   r   r   r   r   zReceived API response: {0}r   r-   z0Successfully assigned device(s) {0} to site {1}.r8   r0  r   Fz-Unable to assigned device(s) {0} to site {1}.z6Error while assigning device(s) to site: {0}, {1}, {2}r   r8   N)	deviceIdssiteIdzAAssigning device(s) to site '{0}' with the following details: {1}rS   assign_network_devices_to_a_siteziReceived API response from 'assign_network_device_to_site' while assigning devices to site: {0}, {1}, {2}assign_device_to_siter  zEFailed to receive a valid response from site assignment API: {0}, {1}zQException occurred while assigning devices to site '{0}'. Assignment details: {1}r   )r`   rc   rB  r4   r8   r  get_device_ips_from_device_idsry   valuesr   r   r>   r4  r9   re   r  r  r1   r   r<   r=   check_tasks_response_status)rf   rZ  rC  rN  r  rG  	device_ip
ip_addressparamr/   r  assign_network_device_to_siter8   site_assgin_detailss                 rj   r  zDnacBase.assign_device_to_site  s    	299)DgNi0  ,ELLYWDHHHTXXw'txx(!*-a044V<	299)YOQXY11IDHHHTXXw'txx(=DDZPRYZ77
C	HOOPZ[DHHHTXXw'txx()**,-a0
*
 **,0T0TU^0__424??62"5 $#*#(	 5<<XFP44X?WX;;)+-1DKK	*)[)b)bcfgqcrt})~DKK&.6ll=.IDKK
+HHT[[/8-2DKK	*)X)_)_`cdn`oqz){DKK&.6ll=.IDKK
+HHT[[/8  (!-) HHX__`i`c  eB  aCD FLMM99??(? $8	 +    E  L  Ls#@A3xPZG[C\^_eg 00D[0\;;y)biis#@AC7+&%%$((%3?  4SZZ[d[^_i[jlopqlrt7+&%%$((%334B  Mippqzqt  vS  rTUs1vw/&)!f#&%%#8K%LLMsB   CP1 'A4P1 A9R: A)R: 1	R7:A3R22R7:	T5A(T00T5c                 F    t         rdt        j                         iS d}d|iS )z
        Generate a new encryption key using Fernet.
        Returns:
            - key (bytes): A newly generated encryption key.
        Criteria:
            - This function should only be called if HAS_FERNET is True.
        generate_keyz`The 'cryptography' library is not installed. Please install it using 'pip install cryptography'.error_message)
HAS_FERNETr   r  )rf   r  s     rj   r  zDnacBase.generate_key  s+     "F$7$7$9::~M#]33rl   c                     	 t        |      }|j                  |j                               }d|iS # t        $ r}ddj	                  |      icY d}~S d}~ww xY w)a  
        Encrypt a plaintext password using the provided encryption key.
        Args:
            - password (str): The plaintext password to be encrypted.
            - key (bytes): The encryption key used to encrypt the password.
        Returns:
            - encrypted_password (bytes): The encrypted password as bytes.
        Criteria:
            - This function should only be called if HAS_FERNET is True.
            - The password should be encoded to bytes before encryption.
        encrypt_passwordr  z1Exception occurred while encrypting password: {0}N)r   encryptencoder  rc   )rf   r   keyfernetencrypted_passwordr  s         rj   r  zDnacBase.encrypt_password  s]    	dC[F!'0A!B&(:;; 	d#%X%_%_`a%bcc	ds   -0 	AAAAc                     	 t        |      }|j                  |      j                         }d|iS # t        j                  $ r ddicY S t        $ r}ddj                  |      icY d}~S d}~ww xY w)a  
        Decrypt an encrypted password using the provided encryption key.
        Args:
            - encrypted_password (bytes): The encrypted password as bytes to be decrypted.
            - key (bytes): The encryption key used to decrypt the password.
        Returns:
            - decrypted_password (str): The decrypted plaintext password.
        Criteria:
            - This function should only be called if HAS_FERNET is True.
            - The encrypted password should be decoded from bytes after decryption.
        decrypt_passwordr  zInvalid decryption token.z1Exception occurred while decrypting password: {0}N)r   decryptdecoder  InvalidTokenrc   )rf   r  r  r  decrypted_passwordr  s         rj   r  zDnacBase.decrypt_password  s{    	dC[F!'0B!C!J!J!L&(:;;%% 	B#%@AA 	d#%X%_%_`a%bcc	ds!   -0 A.	A.A)#A.)A.c                    t        |t              r~i }|j                         D ]g  \  }}t        j                  dd|      j                         }||k7  r"| j                  dj                  ||      d       | j                  |      }|||<   i |S t        |t              r|D cg c]  }| j                  |       c}S |S c c}w )z
        Convert camel case keys to snake case keys in the config.
        Args:
            config (list) - Playbook details provided by the user.
        Returns:
            new_config (list) - Updated config after eliminating the camel cases.
        ([a-z0-9])([A-Z])\1_\2z,{0} will be deprecated soon. Please use {1}.r+   )
r   r  rV   r   subr   r`   rc   camel_to_snake_casery   rf   r   
new_configr  r:  new_key	new_valuerF  s           rj   r  zDnacBase.camel_to_snake_case+  s     fd#J$lln 0
U&&!5xEKKMc>HHKRRSVX_`bij 44U;	&/
7#0 	 %?EFtD,,T2FFM Gs   #C c                 L   t        |t              r_i }|j                         D ]H  \  }}|dk(  rd}n%t        j                  dd|      j                         }| j                  |      }|||<   J |S t        |t              r|D cg c]  }| j                  |       c}S |S c c}w )z
        Replace 'site_type' key with 'type' in the config.
        Args:
            config (list or dict) - Configuration details.
        Returns:
            updated_config (list or dict) - Updated config after replacing the keys.
        rG  rA  r  r  )r   r  rV   r   r  r   update_site_type_keyry   r  s           rj   r  zDnacBase.update_site_type_keyB  s     fd#J$lln 0
U+%$G ff%98SIOOQG 55e<	&/
7#0  %@FGD--d3GGM Hs   B!c           	      T   | j                  dj                  t        |            d       i }|D ]  }	 | j                  j	                  dddd|i      }|r| j                  dj                  |t        |            d	       |j                  d
      }|rY|d   d   }|r(|||<   | j                  dj                  ||      d       ntd||<   | j                  dj                  |      d       nMd||<   | j                  dj                  |      d       n&d||<   | j                  dj                  |      d        | j                  dj                  |      d       |S # t        $ r=}dj                  |t        |            }| j                  |d       d||<   Y d}~sd}~ww xY w)a  
        Get the list of unique device IPs for list of specified hostnames of devices in Cisco Catalyst Center.
        Parameters:
            self (object): An instance of a class used for interacting with Cisco Catalyst Center.
            hostnames (list): The hostnames of devices for which you want to retrieve the device IPs.
        Returns:
            device_ip_mapping (dict): Provide the dictionary with the mapping of hostname of device to its ip address.
        Description:
            Queries Cisco Catalyst Center to retrieve the unique device IP's associated with a device having the specified
            list of hostnames. If a device is not found in Cisco Catalyst Center, an error log message is printed.
        z@Entering 'get_device_ips_from_hostnames' with hostname_list: {0}r   rb  get_device_listThostnamer  z-Received API response for hostname '{0}': {1}r+   r/   r   rj  z)Added device IP '{0}' for hostname '{1}'.Nz*No management IP found for hostname '{0}'.r%   z(No response received for hostname '{0}'.z?No response received from 'get_device_list' for hostname '{0}'.r   zCException occurred while fetching device IP for hostname '{0}': {1}zCExiting 'get_device_ips_from_hostnames' with device IP mapping: {0}r`   rc   r  r<   r=   r4   r  )rf   	hostnamesdevice_ip_mappingr  r/   r  r  r  s           rj   get_device_ips_from_hostnamesz&DnacBase.get_device_ips_from_hostnames[  s    	SZZ[^_h[ijlrs! 	3H399??$. $&1	 +  HHLSST\^abj^klnuv'||J7H$,QK0E$F	$:C-h7 HH%P%W%WXack%lntu:>-h7 HH%Q%X%XYa%bdmn6:)(3!K!R!RS[!\^gh26%h/HH^eefnoqxy1	3> 	V]]^oprxy    3 e l lmuwz{|w} ~0.2!(+3s   DE!!	F'*2F""F'c           	         | j                  dj                  t        |            d       i }|D ],  }	 | j                  dj                  |      d       | j                  j	                  dddd|i      }|r| j                  d	j                  |t        |            d
       |j                  d      }|rY|d   d   }|r(|||<   | j                  dj                  ||      d       ntd||<   | j                  dj                  |      d       nMd||<   | j                  dj                  |      d       n&d||<   | j                  dj                  |      d       / | j                  dj                  |      d       |S # t        $ r=}dj                  |t        |            }| j                  |d       d||<   Y d}~d}~ww xY w)a  
        Get the list of unique device IPs for a specified list of serial numbers in Cisco Catalyst Center.
        Parameters:
            self (object): An instance of a class used for interacting with Cisco Catalyst Center.
            serial_numbers (list): The list of serial number of devices for which you want to retrieve the device IPs.
        Returns:
            device_ip_mapping (dict): Provide the dictionary with the mapping of serial number of device to its ip address.
        Description:
            Queries Cisco Catalyst Center to retrieve the unique device IPs associated with a device having the specified
            serial numbers.If a device is not found in Cisco Catalyst Center, an error log message is printed.
        zFEntering 'get_device_ips_from_serial_numbers' with serial_numbers: {0}r   z+Fetching device info for serial number: {0}rb  r  TserialNumberr  z2Received API response for serial number '{0}': {1}r+   r/   r   rj  z.Added device IP '{0}' for serial number '{1}'.Nz/No management IP found for serial number '{0}'.r%   z-No response received for serial number '{0}'.zDNo response received from 'get_device_list' for serial number '{0}'.r   zHException occurred while fetching device IP for serial number '{0}': {1}zHExiting 'get_device_ips_from_serial_numbers' with device IP mapping: {0}r  )rf   serial_numbersr  serial_numberr/   r  r  r  s           rj   "get_device_ips_from_serial_numbersz+DnacBase.get_device_ips_from_serial_numbers  s    	Y``adesatuw}~+ 	8M8FMMm\^de99??$. $*M:	 +  HHQXXYfhklthuvx  A'||J7H$,QK0E$F	$?H-m< HH%U%\%\]fhu%vx~?C-m< HH%V%]%]^k%lnwx;?)-8!P!W!WXe!fhqr7;%m4HHcjjkxy  |C  D3	8@ 	[bbctuw}~    8 j q qr  BE  FG  BH  !I037!-08   D(F	G2GGc           	         | j                  dj                  t        |            d       i }|D ],  }	 | j                  dj                  |      d       | j                  j	                  dddd|i      }|r| j                  d	j                  |t        |            d
       |j                  d      }|rY|d   d   }|r(|||<   | j                  dj                  ||      d       ntd||<   | j                  dj                  |      d       nMd||<   | j                  dj                  |      d       n&d||<   | j                  dj                  |      d       / | j                  dj                  |      d       |S # t        $ r=}dj                  |t        |            }| j                  |d       d||<   Y d}~d}~ww xY w)a  
        Get the list of unique device IPs for list of specified mac address of devices in Cisco Catalyst Center.
        Parameters:
            self (object): An instance of a class used for interacting with Cisco Catalyst Center.
            mac_addresses (list): The list of mac address of devices for which you want to retrieve the device IPs.
        Returns:
            device_ip_mapping (dict): Provide the dictionary with the mapping of mac address of device to its ip address.
        Description:
            Queries Cisco Catalyst Center to retrieve the unique device IPs associated with a device having the specified
            mac addresses. If a device is not found in Cisco Catalyst Center, an error log message is printed.
        zDEntering 'get_device_ips_from_mac_addresses' with mac_addresses: {0}r   z)Fetching device info for mac_address: {0}rb  r  T
macAddressr  z0Received API response for mac address '{0}': {1}r+   r/   r   rj  z,Added device IP '{0}' for mac address '{1}'.Nz-No management IP found for mac address '{0}'.r%   z+No response received for mac address '{0}'.zBNo response received from 'get_device_list' for mac address '{0}'.r   zFException occurred while fetching device IP for mac address '{0}': {1}zGExiting 'get_device_ips_from_mac_addresses' with device IP mapping: {0}r  )rf   mac_addressesr  mac_addressr/   r  r  r  s           rj   !get_device_ips_from_mac_addressesz*DnacBase.get_device_ips_from_mac_addresses  s    	W^^_bcp_qrtz{( 	6K6DKKKXZ`a99??$. $(+6	 +  HHOVVWbdghpdqrt{|'||J7H$,QK0E$F	$=F-k: HH%S%Z%Z[dfq%rtz{=A-k: HH%T%[%[\g%hjst9=)+6!N!U!UVa!bdmn59%k2HHahhituw~3	6@ 	Zaabstv|}    6 h o op{  ~A  BC  ~D  !E015!+.6r  c           	         | j                  dj                  t        |            d       i }|D ],  }	 | j                  dj                  |      d       | j                  j	                  dddd|i      }|r| j                  d	j                  |t        |            d
       |j                  d      }|rY|d   d   }|r(|||<   | j                  dj                  ||      d       ntd||<   | j                  dj                  |      d       nMd||<   | j                  dj                  |      d       n&d||<   | j                  dj                  |      d       / | j                  dj                  |      d       |S # t        $ r=}dj                  |t        |            }| j                  |d       d||<   Y d}~d}~ww xY w)a  
        Get the list of unique device IPs for list of specified hostnames of devices in Cisco Catalyst Center.
        Parameters:
            self (object): An instance of a class used for interacting with Cisco Catalyst Center.
            hostname_list (list): The hostnames of devices for which you want to retrieve the device IPs.
        Returns:
            device_id_mapping (dict): Provide the dictionary with the mapping of ip address of device to device id.
        Description:
            Queries Cisco Catalyst Center to retrieve the unique device IP's associated with a device having the specified
            list of hostnames. If a device is not found in Cisco Catalyst Center, an error log message is printed.
        z>Entering 'get_device_ids_from_device_ips' with device ips: {0}r   z%Fetching device id for device ip: {0}rb  r  Fmanagement_ip_addressr  z/Received API response for device ip  '{0}': {1}r+   r/   r   ra  z+Added device ID '{0}' for device ip  '{1}'.Nz(No device ID found for device ip  '{0}'.r%   z*No response received for device ip  '{0}'.zANo response received from 'get_device_list' for device ip  '{0}'.r   zEException occurred while fetching device ID for device ip  '{0}': {1}zKExiting 'get_device_ids_from_device_ips' with unique device ID mapping: {0}r  )rf   
device_ipsdevice_id_mappingr  r/   re  r  r  s           rj   get_device_ids_from_device_ipsz'DnacBase.get_device_ids_from_device_ips  s    	QXXY\]gYhikqr# 	4I4@GG	RTZ[99??$. %3Y?	 +  HHNUUV_ademanoqxy'||J7H$,QK$5	$;D-i8 HH%R%Y%YZcen%oqwx;?-i8 HH%O%V%VW`%aclm7;))4!M!T!TU^!_ajk37%i0HH`gghqrt{|3	4@ 	^eefwx  {A  	B    4 g n noxz}~  {A  !B0/3!),4r  c           	         | j                  dj                  t        |            d       i }|D ],  }	 | j                  dj                  |      d       | j                  j	                  dddd|i      }|r| j                  d	j                  |t        |            d
       |j                  d      }|rY|d   d   }|r(|||<   | j                  dj                  ||      d       ntd||<   | j                  dj                  |      d       nMd||<   | j                  dj                  |      d       n&d||<   | j                  dj                  |      d       / | j                  dj                  |      d       |S # t        $ r=}dj                  |t        |            }| j                  |d       d||<   Y d}~d}~ww xY w)a  
        Retrieves the management IP addresses of devices based on the provided device IDs.

        Args:
            device_ids (list): A list of device IDs for which the management IP addresses need to be fetched.
        Returns:
            device_ip_mapping (dict): Provide the dictionary with the mapping of id of device to its ip address.
        Description:
            This function iterates over a list of device IDs, makes an API call to Cisco Catalyst Center to fetch
            the management IP addresses of the devices, and returns a list of these IPs. If a device is not found
            or an exception occurs, it logs the error or warning and continues to the next device ID.
        z>Entering 'get_device_ips_from_device_ids' with device ips: {0}r   z%Fetching device ip for device id: {0}rb  r  Fra  r  z/Received API response for device id  '{0}': {1}r+   r/   r   rj  z+Added device IP '{0}' for device id  '{1}'.Nz(No device ID found for device id  '{0}'.r%   z*No response received for device id  '{0}'.zANo response received from 'get_device_list' for device id  '{0}'.r   zDException occurred while fetching device ip for device id '{0}': {1}zFExiting 'get_device_ips_from_device_ids' with device IP mapping: '{0}'r  )rf   rZ  r  re  r/   r  r  r  s           rj   r  z'DnacBase.get_device_ips_from_device_ids&  s    	QXXY\]gYhikqr# 	4I4@GG	RTZ[99??$. % ),	 +  HHNUUV_ademanoqxy'||J7H$,QK0E$F	$;D-i8 HH%R%Y%YZcen%oqwx;?-i8 HH%O%V%VW`%aclm7;))4!M!T!TU^!_ajk37%i0HH`gghqrt{|3	4@ 	Y``arsu{|    4 f m mnwy|}~y  !A0/3!),4r  c                     | j                  dj                  |      d       d}	 | j                  j                  dddd|i      }|s#| j                  d	j                  |      d
       |S |j	                  d      }|s#| j                  dj                  |      d
       |S | j                  dj                  t        |            d       |d   d   }|r$| j                  dj                  ||      d       |S | j                  dj                  |      d
       	 |S # t        $ rW}dj                  |t        |            | _        | j                  dd| j                  d      j                          Y d}~|S d}~ww xY w)aI  
        Retrieves the ID of a network device tag from the Cisco Catalyst Center based on the tag name.

        Args:
            self (object): An instance of the class used for interacting with Cisco Catalyst Center.
            tag_name (str): The name of the tag whose ID is to be retrieved.
        Returns:
            str or None: The tag ID if found, or `None` if the tag is not available or an error occurs.
        Description:
            This function queries the Cisco Catalyst Center API to retrieve the ID of a tag by its name.
            It sends a request to the 'get_tag' API endpoint with the specified `tag_name`. If the tag is found,
            the function extracts and returns its `id`. If no tag is found or an error occurs during the API call,
            it logs appropriate messages and returns `None`.
        z9Entering 'get_network_device_tag_id' with tag_name: '{0}'r   Ntagget_tagFrx  r  z2No response received from 'get_tag' for tag '{0}'.r%   r/   z2Unable to fetch the tag details for the tag '{0}'.z)Received API response from 'get_tag': {0}r+   r   ra  z*Received the tag ID '{0}' for the tag: {1}z/Tag ID not found in the response for tag '{0}'.zZException occurred while fetching tag id for the tag '{0} 'from Cisco Catalyst Center: {1}r   )
r`   rc   r<   r=   r4   r  r  r8   set_operation_resultr   )rf   tag_namedevice_tag_idr/   response_datar  s         rj   get_network_device_tag_idz"DnacBase.get_network_device_tag_idZ  s     	LSST\]_ef	_yy"!)	 ' H MTTU]^`ij$$$LL4M MTTU]^`ij$$HH@GGMHZ[]de)!,T2MELL]\degmn  JQQRZ[]fg   	_-fXs1v& H %%htxxH\\^^	_s,   AD +5D !AD 9!D 	E=&AE88E=c                    g }|j                         D ]]  \  }}	 |"| j                  dj                  |      d       +| j                  dj                  ||      d       |j                  |       _ |S # t        $ rV}dj                  |t        |            | _        | j                  dd| j                  d      j                          Y d}~d}~ww xY w)	aQ  
        Extracts values from a dictionary and returns a list of non-None values.

        Args:
            self (object): An instance of the class used for interacting with Cisco Catalyst Center.
            dict_name (dict): The dictionary from which values are extracted. Each key-value pair is
                checked, and non-None values are included in the returned list.
        Returns:
            list: A list containing all non-None values from the dictionary.
        Description:
            This function iterates over a given dictionary, checking each key-value pair. If the value
            is `None`, it logs a debug message and skips that value. Otherwise, it appends the value to
            a list. If an exception occurs during this process, it logs the exception message and handles
            the operation result.
        Nz;Value for the key {0} is None so not including in the list.r+   z'Fetch the value '{0}' for the key '{1}'zYException occurred while fetching value for the key '{0} 'from Cisco Catalyst Center: {1}r   Fr   )	rV   r`   rc   rY  r  r  r8   r  r   )rf   	dict_namevalues_listr  r:  r  s         rj   get_list_from_dict_valuesz"DnacBase.get_list_from_dict_values  s    " #//+ 	cJCc=HHZaabefhopHHFMMeUXY[bc&&u-	c   c1&c!f%  ))(E488VL``bbcs   #A53A55	C>ACCc                     	 t        j                  |       |j                  d      }t        |      dk7  ry|D ]  }dt	        |      cxk  rdk  r y  y y# t         j
                  $ r Y yw xY w)a
  
        Validates an IPv4 address.
        Args:
            ip_address - String denoting the IPv4 address passed.
        Returns:
            bool - Returns true if the passed IP address value is correct or it returns
            false if it is incorrect
        r      Fr      T)socket	inet_atonr{   r}   rQ   error)rf   r  octetsoctets       rj   is_valid_ipv4zDnacBase.is_valid_ipv4  sz    
	Z(%%c*F6{a !CJ-#-  . ! || 		s(   4A A A A A A.-A.c                 b    	 t        j                  |      }y# t         j                  $ r Y yw xY w)z
        Validates an IPv6 address.
        Args:
            ip_address - String denoting the IPv6 address passed.
        Returns:
            bool: True if the IPv6 address is valid, otherwise False
        TF)	ipaddressIPv6AddressAddressValueError)rf   r  r  s      rj   is_valid_ipv6zDnacBase.is_valid_ipv6  s2    	&&z2B** 		s    ..c                    |i }t        |t              r|j                  |       |j                         D ]  \  }}t	        j
                  dd|      j                         }|||<   t        |t              r| j                  ||       St        |t              sd|D ]%  }t        |t              s| j                  ||       '  |S t        |t              r*|D ]%  }t        |t              s| j                  ||       ' |S )aD  
        Converts keys in a dictionary from CamelCase to snake_case and creates a keymap.
        Args:
            keymap (dict): Already existing key map dictionary to add to or empty dict {}.
            data (dict or list): Input data where keys need to be mapped using the key map.
        Returns:
            dict: A dictionary with the original keys as values and the converted snake_case
                    keys as keys.
        Example:
            functions = Accesspoint(module)
            keymap = functions.map_config_key_to_api_param(keymap, device_data)
        r  r  )	r   r  updaterV   r   r  r   map_config_key_to_api_paramry   )rf   keymapr  r  r:  r  rF  s          rj   r  z$DnacBase.map_config_key_to_api_param  s     >FdD!MM&!"jjl 	K
U&&!5xEKKM"%weT*44VUCt, % K%dD1 <<VTJK	K   d# CdD)44VTBC rl   c                     	 t        j                  |dd      S # t        t        f$ r(}t        dj	                  t        |                  d}~ww xY w)z
        Pretty prints JSON/dictionary data in a readable format.
        Args:
            jsondata (dict): Dictionary data to be printed.
        Returns:
            str: Formatted JSON string.
        r  ),r   )indent
separatorsz)Invalid input for JSON serialization: {0}N)r6  dumps	TypeErrorr   rc   r  )rf   jsondatar  s      rj   pprintzDnacBase.pprint  sN    	X::hq[II:& 	XGNNsSTvVWW	Xs    A#AAc                    d}t        j                          }	 t        j                          }||z
  | j                  k\  r/| j                  dj                  | j                  |      d       	 |S | j                  j                  dddd|i      }| j                  d	j                  t        |            d
       |d   dk7  r|}	 |S t        j                  | j                  j                  d             )a3  
        Checks the status of API events in Cisco Catalyst Center until completion or timeout.
        Args:
            self (object): An instance of a class used for interacting with Cisco Catalyst Center.
            status_execution_id (str): The execution ID for the event to check the status.
        Returns:
            dict or None: The response from the API once the status is no longer "IN_PROGRESS",
                        or None if the maximum timeout is reached.
        Description:
            This method repeatedly checks the status of an API event in Cisco Catalyst Center using the provided
            execution ID. The status is checked at intervals specified by the 'dnac_task_poll_interval' parameter
            until the status is no longer "IN_PROGRESS" or the maximum timeout ('dnac_api_task_timeout') is reached.
            If the status becomes anything other than "IN_PROGRESS" before the timeout, the method returns the
            response from the API. If the timeout is reached first, the method logs a warning and returns None.
        NTzMax timeout of {0} sec has reached for the execution id '{1}' for the event and unexpected
                        api status so moving out of the loop.r%   event_managementget_status_api_for_eventsr+  r  z;Received API response from 'get_status_api_for_events': {0}r+   	apiStatusIN_PROGRESSr  )
r  rO   r`   rc   r<   r=   r  r  r   r4   )rf   status_execution_idevents_responser!  r"  r/   s         rj   check_status_api_eventsz DnacBase.check_status_api_events  s    " YY[
yy{H:%$*:*:: AAGHXHXZmAnpy{  yy)4 &(;<	 ' H HHRYYZ]^fZghjqr$5"*  JJt{{'@AB# rl   c                     	 t        j                  |       y# t        $ r Y nw xY wt        j                  d      }|j                  |      ryy)a|  
        Validates the server address to check if it's a valid IPv4, IPv6 address, or a valid hostname.
        Args:
            self (object): An instance of a class used for interacting with Cisco Catalyst Center.
            server_address (str): The server address to validate.
        Returns:
            bool: True if the server address is valid, otherwise False.
        Tzj^(([A-Za-z0-9]+([A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,6}|localhost|(\d{1,3}\.)+\d{1,3}|[A-Fa-f0-9:]+$)$F)r  r  r   r   compiler   )rf   server_addresshostname_regexs      rj   is_valid_server_addressz DnacBase.is_valid_server_address0  sU    	  0 		 
 /s    	$$c                 "   t        j                         }t         j                  j                  ||      }| j	                  t        |             t         j                  j                  |      s"| j	                  dj                  |      d       yy)z
        Check if the file path 'file_path' exists or not.
        Args:
            file_path (string) - Path of the provided file.
        Returns:
            True/False (bool) - True if the file path exists, else False.
        zCThe specified path '{0}' is not valid. Please provide a valid path.r   FT)r   getcwdr   joinr`   r  r   rc   )rf   	file_pathcurrent_working_directoryfinal_file_paths       rj   is_path_existszDnacBase.is_path_existsP  sh     %'IIK!'',,'@)L_%&ww~~o.HHZaabqrt{|rl   c                     	 t        |d      5 }t        j                  |       	 ddd       y# 1 sw Y   yxY w# t        t        f$ r$ | j                  dj                  |      d       Y yw xY w)z
        Check if the file in the file path is JSON or not.
        Args:
            file_path (string) - Path of the provided file.
        Returns:
            True/False (bool) - True if the file is in JSON format, else False.
        rNTz-The provided file '{0}' is not in JSON formatr   F)openr6  loadr   r   r`   rc   )rf   r  files      rj   is_jsonzDnacBase.is_jsonb  sj    	i% 		$   -. 	HHDKKIVXbc	s#   : .: 7: : 0A-,A-c                 z   | j                   j                  ddd|i      }| j                  dj                  ||      d       d}|rtt	        |t
              rd|j                  d      }g }|D ]6  }|j                  d	      d
u s|j                  |j                  d             8 |rdj                  |      dz   }|S )a  
        Returns the task tree response of the task ID.
        Args:
            task_id (string) - The unique identifier of the task for which you want to retrieve details.
        Returns:
            error_msg (str) - Returns the task tree error message of the task ID.
        r   get_task_treer   r,  zYRetrieving task tree details by the API 'get_task_tree' using task ID: {0}, Response: {1}r+   r   r/   r  Tr  z. r   )	r<   r=   r`   rc   r   r  r4   rY  r  )rf   r   r/   	error_msgre   error_messagesrF  s          rj   r  z!DnacBase.check_task_tree_responset  s     99??$w' # 

 	l&(+W	6	
8T2\\*-FN @88I&$."))$((:*>?@  IIn5;	rl   c                    d}	 | j                   j                  ddd|i      }t        |t              s#| j	                  dj                  |      d       |S |j                  d      }| j	                  d	j                  |
      d       |S # t        $ rU}| j                          dj                  t        |            | _
        | j                  | j                         Y d}~|S d}~ww xY w)ar  
        Get the details of a specific task in Cisco Catalyst Center.
        Args:
            self (object): An instance of a class that provides access to Cisco Catalyst Center.
            task_id (str): The unique identifier of the task for which you want to retrieve details.
        Returns:
            dict or None: A dictionary containing detailed information about the specified task,
            or None if the task with the given task_id is not found.
        Description:
            Call the API 'get_task_details_by_id' to get the details along with the
            failure reason. Return the details.
        Nr   get_task_details_by_idra  r,  zDInvalid response received when fetching task details for task ID: {}r   r/   zTask Details: {task_details})r#  r+   zyAn error occurred while executing API call to Function: 'get_task_details_by_id' due to the the following exception: {0}.)r<   r=   r   r  r`   rc   r4   r  r  r  r8   r  )rf   r   r#  r/   r  s        rj   r  zDnacBase.get_task_details_by_id  s     	)yy1g ' H
 h-_ffgnoqxy###<<
3LHH3:::UW^_   	) ;;A6#a&> H txx((	)s   AB 3B 	C*A
C%%C*c                 ^   d}	 | j                   j                  ddd|i      }| j                  dj                  |      d       | j                  dj                  ||      d       t	        |t
              s#| j                  d	j                  |      d
       |S |j                  d      }| j                  dj                  |      d       |S # t        $ rU}| j                          dj                  t        |            | _
        | j                  | j                         Y d}~|S d}~ww xY w)ae  
        Get the tasks of a task ID in Cisco Catalyst Center.
        Args:
            self (object): An instance of a class that provides access to Cisco Catalyst Center.
            task_id (str): The unique identifier of the task for which you want to retrieve details.
        Returns:
            dict or None: A dictionary status information about the specified task,
            or None if the task with the given task_id is not found.
        Description:
            Call the API 'get_tasks_by_id' to get the status of the task.
            Return the details along with the status of the task.
        Nr   get_tasks_by_idra  r,  zTask Details: {0}r+   zVRetrieving task details by the API 'get_tasks_by_id' using task ID: {0}, Response: {1}r   r   r/   zTask Status: {0}r   r   r  s        rj   r  zDnacBase.get_tasks_by_id  s    	)yy*g ' H
 HH(//97CHHmfWh/: h-JQQRYZ\cd""",,z2KHH'..{;WE   	) ;;A6#a&> H txx((	)s   BC 2C 	D,A
D''D,c                    |sd| _         d| _        | S t        |t              sd| _         d| _        | S |j	                  d      }|j	                  d      .|j	                  d      j	                  d      | _         d| _        | S |j	                  d      }t        j
                         }	 t        j
                         |z
  }|| j                  k\  rYd
j                  | j                  |      dj                  |      z   | _         | j                  | j                   d       d| _        	 | S | j                  |      }| j                  dj                  ||      d       |j	                  d      }|dk(  r1| j                  |      }	|	j	                  d      | _         d| _        	 | S |dk(  r3d	| j                  d<   | j                  dj                  |      d       	 | S | j                  dj                  ||      d       U)a  
        Get the task response status from taskId
        Args:
            self: The current object details.
            response (dict): API response.
            api_name (str): API name.
        Returns:
            self (object): The current object with updated desired Fabric Transits information.
        Description:
            Poll the function 'get_tasks_by_id' until it returns either 'SUCCESS' or 'FAILURE'
            state or till it reaches the maximum timeout.
            Log the task details and return self.
        zresponse is emptyr   zresponse is not a dictionaryr/   r  r  r   r  Tz:Max timeout of {0} sec has reached for the task id '{1}'. z4Exiting the loop due to unexpected API '{0}' status.r%   z+Getting tasks details from task ID {0}: {1}r+   r9   FAILUREr  r1  r-   z5The task with task ID '{0}' is executed successfully.r   r  )r8   r9   r   r  r4   r  rO   rc   r`   r  r  re   )
rf   r/   r  	task_infor   r!  elapsed_timer#  r  detailss
             rj   r  z$DnacBase.check_tasks_response_status  s    *DH"DKK(D)5DH"DKKLL,	==%1||J/33H=DH"DKK--)YY[
99;3Lt///W"F4#3#3W=QXXYabc 9-&, )  //8LHHBfWl3W> '**84Ki'55g>";;7&  	))-I&P &/63
  HH7f['2G=7 rl   c                     ||n|}|| _         | j                  j                  |||||dk(  d       | j                  ||       | S )a^  
        Update the result of the operation with the provided status, message, and log level.
        Args:
            - operation_status (str): The status of the operation ("success" or "failed").
            - is_changed (bool): Indicates whether the operation caused changes.
            - status_message (str): The message describing the result of the operation.
            - log_level (str): The log level at which the message should be logged ("INFO", "ERROR", "CRITICAL", etc.).
            - additional_info (dict, optional): Additional data related to the operation result.
        Returns:
            self (object): An instance of the class.
        Note:
            - If the status is "failed", the "failed" key in the result dictionary will be set to True.
            - If data is provided, it will be included in the result dictionary.
        r   )r9   r8   r/   r-   r   )r9   re   r  r`   )rf   operation_status
is_changedstatus_message	log_leveladditional_infor/   s          rj   r  zDnacBase.set_operation_result   sW      '6&A?~&&! !&(2
 	 	+rl   c                 L    | j                  dd|d       | j                          y)z6Helper method to update the result as failed and exit.r   Fr   N)r  r   )rf   r8   s     rj   r  zDnacBase.fail_and_exit@  s"    !!(E3@  "rl   c                 n    t        j                         }| j                  dj                  |      d       y)zC
        Logs the full traceback of the current exception.
        zTraceback: {0}r+   N)	traceback
format_excr`   rc   )rf   full_tracebacks     rj   r  zDnacBase.log_tracebackE  s.    
 #--/ 	!((8'Brl   c                     t        j                          |z
  }|| j                  j                  d      kD  r@dj                  ||t	        |            | _        | j                  dd| j
                  d       yy)a  
        Check if the elapsed time exceeds the specified timeout period and exit the while loop if it does.
        Args:
            - loop_start_time (float): The time when the while loop started.
            - task_id (str): ID of the task being monitored.
            - task_name (str): Name of the task being monitored.
        Returns:
            bool: True if the elapsed time exceeds the timeout period, False otherwise.
        r   zUTask {0} with task id {1} has not completed within the timeout period of {2} seconds.r   Fr   T)r  r   r4   rc   rQ   r8   r  )rf   loop_start_timer   	task_namer  s        rj   check_timeout_and_exitzDnacBase.check_timeout_and_exitO  sh     yy{_4$++//*ABBnuu7C$57DH %%htxxIrl   c           	         | j                  dj                  |||      d       	 | j                  j                  ||d|      }| j                  dj                  ||t	        |            d       ||dk(  st        |t              r%|s#| j                  d	j                  ||      d
       yt        |t              r,d|v r(|d   s#| j                  dj                  ||      d
       y|S # t        $ rW}| j                          dj                  |||t	        |            | _	        | j                  | j                         Y d}~yd}~ww xY w)a  
        Makes a GET API call to the specified function within a given family and returns the response.
        Args:
            api_family (str): The family of the API to call.
            api_function (str): The specific function of the API to call.
            api_parameters (dict): Parameters to pass to the API call.
        Returns:
            dict or None: The response from the API call if successful, otherwise None.
        Logs detailed information about the API call, including responses and errors.
        zPInitiating GET API call for Function: {0} from Family: {1} with Parameters: {2}.r+   Fr  zZResponse received from GET API call to Function: '{0}' from Family: '{1}' is Response: {2}r   Nr   zMNo response received from GET API call to Function: '{0}' from Family: '{1}'.r%   r/   zaEmpty 'response' key in the API response from GET API call to Function: '{0}' from Family: '{1}'.zvAn error occurred while executing GET API call to Function: '{0}' from Family: '{1}'. Parameters: {2}. Exception: {3}.)r`   rc   r<   r=   r  r   r  r  r  r8   r  )rf   r  r  api_parametersr/   r  s         rj   rX  zDnacBase.execute_get_requeste  s]    	^eej. 		
,	)yy!%!%	 ' H HHlss *c(m 	 8r>j46PYacjj$j 
  (D)jH.DXV`Maw~~$j 
 O 	) 3396,
Tbdghidj3k H txx((	)s%   BC* ,;C* (C* *	E
3AEE
c           	         	 | j                  dj                  ||      d       | j                  j                  ||d|      }| j                  dj                  ||t	        |            d       |j                  d      }|s#| j                  dj                  ||      d	       y
| j                  j                  t        |             |j                  d      }| j                  dj                  |||      d       |S # t        $ rW}| j                          dj                  |||t	        |            | _        | j                  | j                         Y d
}~y
d
}~ww xY w)a  
        Retrieve task ID from response after executing POST API call
        Args:
            api_family (str): The API family.
            api_function (str): The API function (e.g., "add_port_assignments").
            api_parameters (dict): The parameters for the API call.
        z7Requested payload for the the function: '{0}' is: '{1}'r   Tr  zVResponse received from API call to Function: '{0}' from Family: '{1}' is Response: {2}r+   r/   zINo response received from API call to Function: '{0}' from Family: '{1}'.r%   N)r/   r  zRTask ID received from API call to Function: '{0}' from Family: '{1}', Task ID: {2}zrAn error occurred while executing API call to Function: '{0}' from Family: '{1}'. Parameters: {2}. Exception: {3}.)r`   rc   r<   r=   r  r4   re   r  r  r  r  r8   r  )rf   r  r  r)  r/   r  r   r  s           rj   get_taskid_post_api_callz!DnacBase.get_taskid_post_api_call  s\   .	)HHNUUVbdrsu{| yy!% %	 ' H HHhoo *c(m 	 %LL4M _ff$j 
  KKt];<#''1GHHdkk *g
 N 	) 3396,
Tbdghidj3k H txx((	)s    B"D  %AD   	E 	AEE c                    t        j                          }| j                  dj                  ||      d       	 | j                  |      }|s7dj                  ||      | _        | j                  dd| j                  d       n| j                  dj                  |      d	       |j                  d
      }|j                  d      }t        j                          |z
  }| j                  |||      r%| j                  dj                  |||      d       n|r|dk(  r| j                  |      }	|	j                  d      }
|
rdj                  |||
      | _        n'dj                  ||      j                  ||      | _        | j                  dd| j                  d       n~|dk(  r&|| _        | j                  dd| j                  d	       nS| j                  j                  d      }| j                  dj                  |      d       t        j                  |       t        j                          |z
  }| j                  dj                  |||      d       | S )a  
        Retrieves and monitors the status of a task by its task ID.
        This function continuously checks the status of a specified task using its task ID.
        If the task completes successfully, it updates the message and status accordingly.
        If the task fails or times out, it handles the error and updates the status and message.
        Args:
            task_id (str): The unique identifier of the task to monitor.
            task_name (str): The name of the task being monitored.
            success_msg (str): The success message to set if the task completes successfully.
        Returns:
            self: The instance of the class with updated status and message.
        6Starting task monitoring for '{0}' with task ID '{1}'.r+   T9Error retrieving task status for '{0}' with task ID '{1}'r   Fr   (Successfully retrieved task details: {0}r   r9   endTimeVTimeout exceeded after {0:.2f} seconds while monitoring task '{1}' with task ID '{2}'.r  r  zDFailed to execute the task {0} with Task ID: {1}.Failure reason: {2}z1Failed to execute the task {0} with Task ID: {1}.r1  r   r  TWaiting for the next poll interval of {0} seconds before checking task status again.ICompleted monitoring task '{0}' with task ID '{1}' after {2:.2f} seconds.)r  r`   rc   r  r8   r  r4   r'  r  r   r  )rf   r   r&  success_msgr%  r/   r9   r"  r  get_task_details_responsefailure_reasonpoll_intervaltotal_elapsed_times                rj    get_task_status_from_tasks_by_idz)DnacBase.get_task_status_from_tasks_by_id  sO    ))+IPPQZ\cdfmn++G4H V]]^gipq))(E488WMHH?FFxPRXY\\(+F||I.H99;8L**?GYOlss$i 	  Y&040K0KG0T-%>%B%B?%SN%228&G^2\  PVVW`bij &G4  --htxxQy(*DH--itxxP !KKOO,EFMHHkrr  tA  B  DK  LJJ}%c f "YY[?:\ccdmov  yK  L  NU  	Vrl   c                    t        j                          }| j                  dj                  ||      d       	 | j                  |      }|s7dj                  ||      | _        | j                  dd| j                  d       n|j                  d      r^|j                  d	      }	|	| _        |	r$| xj                  d
j                  |	      z  c_        | j                  dd| j                  d       n| j                  dj                  |      d       t        j                          |z
  }
| j                  |||      r%| j                  dj                  |
||      d       n;|j                  d      }|j                  d      }|j                  d      }| j                  dj                  |||      d       |r|rF||v rB|| _        | j                  dd| j                  d       | j                  | j                  d       n|rF||v rB|| _        | j                  dd| j                  d       | j                  | j                  d       nS| j                  j                  d      }| j                  dj                  |      d       t        j                  |       dt        j                          |z
  }| j                  dj                  |||      d       | S )a  
        Retrieves and monitors the status of a task by its ID and validates the task's data or progress.
        Args:
            task_id (str): The ID of the task to check.
            task_name (str): The name of the task.
            data_validation (str, optional): A key to validate the task's data. Defaults to None.
            progress_validation (str, optional): A key to validate the task's progress. Defaults to None.
            failure_msg (str, optional): A custom message to log if the task fails. Defaults to None.
            success_msg (str, optional): A custom message to log if the task succeeds. Defaults to None.
        Returns:
            self: The instance of the class.
        r-  r+   Tr.  r   Fr   r  r  zFailure reason: {0}r/  r   r1  r  r  r0  z/Current task progress for '{0}': {1}, Data: {2}r   r  r2  r3  )
r  r`   rc   r	  r8   r  r4   r'  r   r  )rf   r   r&  failure_msgr4  progress_validationdata_validationr%  r/   r6  r  r  r  r"  r7  r8  s                   rj   get_task_status_from_task_by_idz(DnacBase.get_task_status_from_task_by_id#  s    ))+IPPQZ\cdfmn,,W5H V]]^gipq))(E488WM ||I&!)o!>)!HH 5 < <^ LLH))(E488WMHH?FFxPRXY  99;8L**?GYOlss$i 	  <<'D||J/H||I.HHHFMMiYacghjpq "$'>*DH--itxxPHHTXXv.&+>(+J*DH--itxxPHHTXXv. !KKOO,EFMHHkrr  tA  B  DK  LJJ}%o r "YY[?:\ccdmov  yK  L  NU  	Vrl   c                     ||| j                  dj                        d       | j                  dj                        d       t        fd|D              S )aA  
        Check if the config given requires update by comparing
        current information with the requested information.

        This method compares the current fabric devices information from
        Cisco Catalyst Center with the user-provided details from the playbook,
        using a specified schema for comparison.

        Args:
            have (dict): Current information from the Cisco Catalyst Center
                          of SDA fabric devices.
            want (dict): Users provided information from the playbook
            obj_params (list of tuples) - A list of parameter mappings specifying which
                                          Cisco Catalyst Center parameters (dnac_param) correspond to
                                          the user-provided parameters (ansible_param).
        Returns:
            bool - True if any parameter specified in obj_params differs between
            current_obj and requested_obj, indicating that an update is required.
            False if all specified parameters are equal.
        Description:
            This function retrieves the object parameters needed for the requires_update function.
            The obj_params will contain patterns to be compared based on the specified object.
            This function checks both the information provided by the user and
            the information available in the Cisco Catalyst Center.
            Based on the object_params the comparison will be taken place.
            If there is a difference in those information, it will return True.
            Else False.
        z#Current State (have): {current_obj})current_objr+   z%Desired State (want): {requested_obj})requested_objc              3   z   K   | ]2  \  }}t        j                  |      j                  |              4 y wr   dnac_compare_equalityr4   )rt   
dnac_paramansible_paramr@  rA  s      rj   rv   z+DnacBase.requires_update.<locals>.<genexpr>  sD      B2
M -[__Z-H-:->->}-MO O Bs   8;)r`   rc   r~   )rf   r5   r6   
obj_paramsr@  rA  s       @@rj   requires_updatezDnacBase.requires_updatep  si    < 6==+=VX_`8??m?\^ef B6@B B 	Brl   )r%   r   )Fr   )r  NN)Tr   
__module____qualname____doc__r   __metaclass__rX   rk   r   r   r   r   r   r   r   r?   r@   rA   rB   rC   rD   rE   rG   rH   rI   rJ   rK   rL   rM   r]   rZ   r[   r`   r   r   r   r:   r	  r  r%  r(  r.  r4  r;  rH  r_  rh  rv  rB  rV  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r'  rX  r+  r9  r>  rH  rr   rl   rj   r   r   )   s   KMMFUP80+,4  













(*d
s$(Z"7*0"&P"GR61f"B5(n/#b5@nAF"&HpMd4d&d*.20!d1!f1!f1!f2!h1f!F,$LX'R@$$:#J'R?B@#
C,=)~6)pENKZ%Brl   r   c                 R    t        | d   t              xs t        | d   t              S )Nr   )r   r  ry   )xs    rj   is_list_complexrP    s#    adD!;Z!d%;;rl   c                 ,     t         fd|D              S )Nc              3   &   K   | ]  }|v 
 y wr   rr   )rt   elemls1s     rj   rv   z has_diff_elem.<locals>.<genexpr>  s     0DC0s   )r~   )rT  ls2s   ` rj   has_diff_elemrV    s    0C011rl   c                     t        |       }t        |      }||k7  ry|dk(  ry| |k(  }|ryt        |       s"t        |      st        |       t        |      k(  S d}||kD  r|S t        | |       xs t        ||        S )NFr   Td   )r}   rP  setrV  )list1list2	len_list1	len_list2attempt_std_cmpMAX_SIZE_CMPs         rj   compare_listr`    s    E
IE
IIA~unO5!/%*@5zSZ'' L< "%/0Uue9T4UUrl   c                 V    t        |j                  |       |j                  |             S r   rC  )kdict1dict2s      rj   fn_comp_keyre    s     1uyy|<<rl   c                     t        | t              st        d      	 t        t        j                  |             }|S # t
        $ r | cY S w xY w)z>
    Normalize an IPv6 address for consistent comparison.
    z4Input must be a string representing an IPv6 address.)r   r  r  r  r  r   )ipv6normalized_addresss     rj   normalize_ipv6_addressri    sO     dC NOO !6!6t!<=!! s   = A
Ac                     y yt         t              r3t        t              r#d v rdv rt                t               k(  S t         t              rYt        t              rIt	         j                               t	        j                               z   }t         fd|D               S t         t              rt        t              rt               S  k(  S )NT:c              3   :   K   | ]  }t        |         y wr   )re  )rt   r  current_valuerequested_values     rj   rv   z(dnac_compare_equality.<locals>.<genexpr>  s     lSXK}oNNls   )r   r  ri  r  ry   keysr~   r`  )rm  rn  all_dict_paramss   `` rj   rD  rD    s    -%*_c*J-C?$:2=AM4_EO//-&:ot+L}1134tO<P<P<R7SSl\klmmm	M4	(Z-NM?;;//rl   c                     | |k(  S r   rr   )obj1obj2s     rj   
simple_cmprt    s    4<rl   c                    t        | t              rt        |       dk(  rEt        | d   t              r.| d   } | j	                  |      a| j	                  |      |k7  rMd } | S d } | S | D ]>  }t        |t              s|j	                  |      |j	                  |      |k(  s:|} | c S  d } | S t        | t              sd } | S | j	                  |      | j	                  |      |k7  rd } | S )Nrn   r   )r   ry   r}   r  r4   )re   r  r:  cmp_fnrF  s        rj   get_dict_resultrw    s    &$v;!&)T*::c?.6::c?e3K!F M  M  "dD)txx}/DQTY^H^!F!M" F
 M	 % M 
C	$CE)AMrl   c                      t        t        dd      t        ddd      t        ddd	g
      t        dd      t        dd      t        dd      t        dd      t        dd            } | S )Nr  T)rA  requiredrQ   Fi  )rA  ry  defaultadminuser)rA  rz  aliases)rA  no_logbool)rA  rz  r    )r   r   r   r   r   r   r   validate_response_schema)r  )argument_specs    rj   dnac_argument_specr    sj    ED1EE3?wId3fd3ui8VU3!%64!@	M rl   c           	          t        j                  |       } |j                  d      rWdt        |       cxk  r|j                  d      k  r| S  |j	                  dj                  || |j                  d                   | S )a  
    This function checks that the input `item` is a valid string and confirms to
    the constraints specified in `param_spec`. If the string is not valid or does
    not meet the constraints, an error message is added to `invalid_params`.

    Args:
        item (str): The input string to be validated.
        param_spec (dict): The parameter's specification, including validation constraints.
        param_name (str): The name of the parameter being validated.
        invalid_params (list): A list to collect validation error messages.

    Returns:
        str: The validated and possibly normalized string.

    Example `param_spec`:
        {
            "type": "str",
            "length_max": 255 # Optional: maximum allowed length
        }
    
length_maxrn   z>{0}:{1} : The string exceeds the allowed range of max {2} char)r
   check_type_strr4   r}   rY  rc   rF  
param_spec
param_nameinvalid_paramss       rj   validate_strr  	  sx    , $$T*D~~l#D	9Z^^L99K : !!((.z4P\A](^ Krl   c           
         	 t        j                  |       } |j                  dd      }|j                  d      rQ|| cxk  r|d   k  sCn |j                  dj	                  || |j                  d      |j                  d                   | S # t        $ r7}|j                  dj	                  || t        |                   | cY d}~S d}~ww xY w)a&  
    This function checks that the input `item` is a valid integer and conforms to
    the constraints specified in `param_spec`. If the integer is not valid or does
    not meet the constraints, an error message is added to `invalid_params`.

    Args:
        item (int): The input integer to be validated.
        param_spec (dict): The parameter's specification, including validation constraints.
        param_name (str): The name of the parameter being validated.
        invalid_params (list): A list to collect validation error messages.

    Returns:
        int: The validated integer.

    Example `param_spec`:
        {
            "type": "int",
            "range_min": 1,     # Optional: minimum allowed value
            "range_max": 100    # Optional: maximum allowed value
        }
    z{0}: value: {1} {2}N	range_minrn   	range_maxzF{0}: {1} : The item exceeds the allowed range of min: {2} and max: {3})r
   check_type_intr  rY  rc   r  r4   )rF  r  r  r  r  	min_values         rj   validate_integer_within_ranger  /	  s    ,((.
 {A.I~~k"I,XKAX,XT[[D*.."=z~~k?Z\	

 K  3:::tSQRVTUs   B 	C,CCCc                 ,    t        j                  |       S )a   
    This function checks that the input `item` is a valid boolean value. If it does
    not represent a valid boolean value, an error message is added to `invalid_params`.

    Args:
        item (bool): The input boolean value to be validated.
        param_spec (dict): The parameter's specification, including validation constraints.
        param_name (str): The name of the parameter being validated.
        invalid_params (list): A list to collect validation error messages.

    Returns:
        bool: The validated boolean value.
    )r
   check_type_boolr  s       rj   validate_boolr  U	  s     %%d++rl   c                 "   	 |j                  d      t        |       j                  k(  rg }|D ]  }|j                  |        t	        |      dk(  rt        j                  |       S |d   ||d      i}	 |d   r|d   }|d   }t        |       j                  |k(  rB| D ]<  }	t        |	      j                  |k7  s|j                  dj                  |	|             > n!|j                  dj                  | |             | S |j                  dj                  |              	 | S # t        $ r+}
t        | |      \  } }|j                  |       Y d}
~
| S d}
~
ww xY w# t        $ r+}
|j                  dj                  |
             Y d}
~
| S d}
~
ww xY w)a  
    This function checks if the input `item` is a valid list based on the specified `param_spec`.
    It also verifies that the elements of the list match the expected data type specified in the
    `param_spec`. If any validation errors occur, they are appended to the `invalid_params` list.

    Args:
        item (list): The input list to be validated.
        param_spec (dict): The parameter's specification, including validation constraints.
        param_name (str): The name of the parameter being validated.
        invalid_params (list): A list to collect validation error messages.

    Returns:
        list: The validated list, potentially normalized based on the specification.
    rA  rn   elementsz8{0} is not of the same datatype as expected which is {1}Nz{0} : is not a valid listz{0} : comes into the exception)r4   rA  r   rY  r}   r
   check_type_listrc   r  validate_list_of_dictsr}  )rF  r  r  r  	keys_listdict_key	temp_dictget_spec_typeget_spec_elementelementr  list_invalid_paramss               rj   validate_listr  g	  s    J>>&!T$Z%8%88I& +  *+9~"!11$77"1z)A,'?@I;j)$.v$6M'1*'=$Dz**m;'+ "G#G}559II . 5 5$^$e$efmo  %A!"" '--V]]^bdqr K	 !!"="D"DT"JK K  ;,B4,S))%%&9:: K;
  J>EEaHIIKJsP   A#E &E 4AD# 9AD#   E #	E, EE EE 	F# F		Fc                     |j                  d      t        |       j                  k7  r |j                  dj	                  |              t        j                  |       S )ar  
    This function checks if the input `item` is a valid dictionary based on the specified `param_spec`.
    If the dictionary does not match the expected data type specified in the `param_spec`,
    a validation error is appended to the `invalid_params` list.

    Args:
        item (dict): The input dictionary to be validated.
        param_spec (dict): The parameter's specification, including validation constraints.
        param_name (str): The name of the parameter being validated.
        invalid_params (list): A list to collect validation error messages.

    Returns:
        dict: The validated dictionary.
    rA  z{0} : is not a valid dictionary)r4   rA  r   rY  rc   r
   check_type_dictr  s       rj   validate_dictr  	  sJ      ~~fd!4!44?FFtLM%%d++rl   c           	      v   t         }g }g }| D ]  }i }|s|j                  d        ||fS |D ]r  }|j                  |      }	|	O||   j                  d      r!|j                  dj                  |             n||   j                  d      }	|	||<   f||   j                  d      }
t        t
        t        t        t        d}|j                  |
      }|r ||	||   ||      }	n"|j                  dj                  ||	|
             ||   j                  d      }|r$|	|vr |j                  d	j                  |	             ||   j                  d
      }|rD||j                  j                  |	       n&dj                  |      }|dz  }|dz  }t        |      |	||<   u |j                  |        ||fS )aO  Validate/Normalize playbook params. Will raise when invalid parameters found.
    param_list: a playbook parameter list of dicts
    spec: an argument spec dict
          e.g. spec = dict(ip=dict(required=True, type='bool'),
                           foo=dict(type='str', default='bar'))
    return: list of normalized input data
    z/No more spec to validate, but parameters remainry  z"{0} : Required parameter not foundrz  rA  )r  rQ   r  ry   r  z${0}:{1} : Unsupported data type {2}.choicesz{0} : Invalid choice providedr~  z

'{0}' is a no_log parameterz.
Ansible module object must be passed to this z&
function to ensure it is not logged

)r
   rY  r4   rc   r  r  r  r  r  no_log_valuesaddr  )
param_listspecr1   v
normalizedr  
list_entryvalid_params_dictr  rF  	data_typeswitch	validatorchoicer~  r8   s                   rj   r  r  	  s    	AJN  4-
!!"STb ~%%a  -	,E>>%(D|;??:."))<CCEJ  ;??95D/3%e,U/I#4%%%F 

9-I tE{E>J%%:AA%yY %[__Y/Fv%"))7>>tD %[__X.F%((,,T2;BB5ICLLCFFC#C.('+e$[-	,\ 	+,i4-l ~%%rl   zRate Limit exceeded   c                   d    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d Z
dd
Zd Zd ZddZd Zd Zy	)r;   c           
      z   t        dd      | _        |j                  d      | _        t	        j
                  d      | _        t        rt        j                  |j                  d      |j                  d      dj                  |j                  d	      |j                  d
            |j                  d      |j                  d      |j                  d            | _        |j                  d      r5t        r.| j                  j                  t	        j                                y y y | j                  d       y )NFr   r-   re   r  dnacentersdkr   r   zhttps://{dnac_host}:{dnac_port}r   r   )r   r   r   r   r   )usernamer   base_urlversionverifydebugzJDNA Center Python SDK is not installed. Execute 'pip install dnacentersdk'r  )r  re   r4   r  r^   r_   r*   DNAC_SDK_IS_INSTALLEDr   DNACenterAPIrc   LOGGING_IN_STANDARDra   StreamHandlerr   )rf   r   s     rj   rk   zDNACSDK.__init__	  s    54(.

3M(N%''7 ''O4O4:AA$jj5KAX B  

>2zz-0jj.	DH zz,',?&&w'<'<'>? -@' NNkNlrl   c                 "    d| j                   d<   y )NTr-   re   r   s    rj   r-   zDNACSDK.changed
  s    !%Irl   c                 B    | j                          d| j                  d<   y )NzObject createdre   r  r   s    rj   object_createdzDNACSDK.object_created
       0Hrl   c                 B    | j                          d| j                  d<   y )NzObject updatedre   r  r   s    rj   object_updatedzDNACSDK.object_updated
  r  rl   c                 B    | j                          d| j                  d<   y )NzObject deletedre   r  r   s    rj   object_deletedzDNACSDK.object_deleted
  r  rl   c                 "    d| j                   d<   y )NzObject already absentre   r  r   s    rj   object_already_absentzDNACSDK.object_already_absent
  s     7Hrl   c                 "    d| j                   d<   y )NzObject already presentre   r  r   s    rj   object_already_presentzDNACSDK.object_already_present
  s     8Hrl   c                 "    d| j                   d<   y )NzDObject already present, but it has different values to the requestedre   r  r   s    rj   object_present_and_differentz$DNACSDK.object_present_and_different"
  s     fHrl   Nc                 L    ||| j                   d<   |r| j                          y y )Nre   )re   r-   )rf   r-   re   s      rj   object_modify_resultzDNACSDK.object_modify_result%
  s'    $*DKK!LLN rl   c                 @    t         j                  j                  |      S r   )r   r   isfilerf   r  s     rj   is_filezDNACSDK.is_file+
  s    ww~~i((rl   c                 @    t         j                  j                  |      S r   )r   r   basenamer  s     rj   extract_file_namezDNACSDK.extract_file_name.
  s    ww	**rl   c           	         |}|}	 t        | j                  |      }t        ||      }	 |r|j	                  dg       }
|
rt        |
t              ri }|
D ]d  \  }}t        |j	                  |      t              s&| j                  ||         s;| j                  ||         }||   }|t        |d      f||<   f |j                  d|       |j                  dd        | j                  s|rd|d<    di |}n        }|rt        |t              r|j	                  d      r|j	                  d      }d	|i}t        t        | j                  d
      d      }	  |di |}|j	                  d      dk(  r	 |S |j	                  d      }|rut        |v rO| j                  j!                  dt               t#        j$                  t&                | j(                  ||||fi |S | j                  j+                  |       	 |S |S # t        $ r}	| j                  |	       Y d }	~	d }	~	ww xY w# t,        j.                  $ rj}	| j                  dj1                  t3        |	j4                  j6                        t3        |	j4                  j8                        ||             Y d }	~	S d }	~	wt,        j:                  $ r8}	| j                  dj1                  t3        |	      ||             Y d }	~	S d }	~	ww xY w)Nr  
file_pathsrbmultipart_fieldsmultipart_monitor_callbackFactive_validationr0  r+  r   r*  r9   r1  r2  z!!!!! %s !!!!!zAn error occured when executing operation for the family '{family}' having the function '{function}'. The error was: status_code: {error_status},  {error})error_statusr  r   r   z|An error occured when executing operation for the family '{family}' having the function '{function}'. The error was: {error})r  r   r   rr   )r   r   r  r   r4   r   ry   r  r  r  r  
setdefaultr  r  RATE_LIMIT_MESSAGEr*   warningr  r  RATE_LIMIT_RETRY_AFTERr=   r  r   ApiErrorrc   r	   r/   status_codetextdnacentersdkException)rf   r   r   r   r   kwargsfamily_namefunction_namefuncr  file_paths_paramsr  r  r:  	file_namer  r/   r+  exec_details_paramsexec_details_funcr3  
bapi_errors                         rj   r=   zDNACSDK._exec1
  s    	"TXXv.F68,D@	$*JJ|R$@!$4Et)L')$(9 Ye%fjjos;VTW[@Y(,(>(>vc{(KI(.sI7@$yRVBW6X,U3	Y %%&8:JK%%&BDI4427F./>&>  6Jx68<<;V'||M:'5|&D#$+DHHf-/S%! (9(P<O(P%(,,X6)C@ = "3!6!6{!CJ!-; KK//0@BTU JJ'=>#-4:: +]FK$SY$  ))*5( G F I  	"NNqN!!	"d "" 	NNL &i

0F0F&GyYZYcYcYhYhOi +m  E   "  // 	NN. &y|K-&X    	s\   "H AH- 9H- C(H- 9A)H- #H- H- 	H*H%%H*-K3 AJ%%K3;-K..K3c                 P     | j                   j                  di | t        |      )Nrr   )re   r  r  )rf   r8   r  s      rj   r   zDNACSDK.fail_json}
  s#    $V$nrl   c                     | j                   S r   r  r   s    rj   r   zDNACSDK.exit_json
  s    {{rl   rI  )NF)r   rJ  rK  rk   r-   r  r  r  r  r  r  r  r  r  r=   r   r   rr   rl   rj   r;   r;   	  sL    m(&11189g)+JXrl   r;   c                       y r   rr   rr   rl   rj   mainr  
  s    rl   __main__r   )7
__future__r   r   r   rA  rM  cryptography.fernetr   r  ImportErrorr  r   r   r  ansible.module_utils._textr	   ansible.module_utils.commonr
   abcr   r   r^   r  r  os.pathr   r2   r6  r   r   r  r  r!  r   rP  rV  r`  re  ri  rD  rt  rw  r  r  r  r  r  r  r  r  r  objectr;   r  r   rr   rl   rj   <module>r     sD   C B*J!, ! 0 2 '      	   l!B l!B^C<2V4=00 0: ,D#L,$/d,*C&L +  Jf JZ	 zF {S  J  "!"    s3   B: C C :CCCCCC