
    Vh]                    H   d Z ddlm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mZmZ ddlmZmZmZmZ ddlmZmZmZmZmZmZmZmZmZmZmZmZm Z m!Z!m"Z" ddl#m$Z$m%Z%m&Z& dd	l'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2 dd
l3m4Z4m5Z5 ddl6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z<m=Z=m>Z>m?Z?m@Z@mAZAmBZBmCZC ddlDmEZEmFZF ddlGmHZH ddlImJZJ ddlKmLZLmMZMmNZN ddlOmPZPmQZQmRZR ddlSmTZTmUZUmVZVmWZW ddlXmYZYmZZZm[Z[ ddl\m]Z]m^Z^ ddl_m`Z`maZambZbmcZcmdZd  e
j                  de      Zf e
j                  de      Zg e
j                  de      Zh e
j                  de      Zi G d de(      Zj ej                  d       G d d             Zl G d  d!e
j                  eg   ej                  "      Zo G d# d$eoeh   ej                  "      Zp G d% d&epef   ej                  "      Zq G d' d(eoeg   ej                  "      Zr G d) d*erei   ej                  "      Zs G d+ d,ere   epe         Zt G d- d.eqe   ere         Zu G d/ d0eoe         Zv G d1 d2ese         Zw G d3 d4eqe         Zx G d5 d6eqe   ese         Zy G d7 d8ere   epe         Zz G d9 d:ere!         Z{ G d; d<ese"         Z|e*d?d=       Z}	 	 	 	 	 	 	 	 d@d>Z~y)AzNProfiles to represent individual test hosts or a user-provided inventory file.    )annotationsN   )read_text_filewrite_text_file)CommonConfigEnvironmentConfigIntegrationConfigTerminateMode)ControllerConfigControllerHostConfigDockerConfig
HostConfigNetworkInventoryConfigNetworkRemoteConfigOriginConfigPosixConfigPosixRemoteConfigPosixSshConfigPythonConfigRemoteConfigVirtualPythonConfigWindowsInventoryConfigWindowsRemoteConfig)AnsibleCoreCISshKey
VmResource)ApplicationErrorSubprocessErrorcachedisplayget_type_mapsanitize_host_namesorted_versionsInternalErrorHostConnectionErrorANSIBLE_TEST_TARGET_ROOTWINDOWS_CONNECTION_VARIABLES)get_docs_urlintercept_python)docker_execdocker_image_inspectdocker_logsdocker_pull	docker_rmget_docker_hostnamerequire_dockerget_docker_infodetect_host_propertiesrun_utility_containerSystemdControlGroupV1StatusLOGINUID_NOT_SETUTILITY_IMAGE)BootstrapDockerBootstrapRemote)get_virtual_python)SshConnectionDetail)ansible_environment	get_hostsparse_inventory)HostTypeget_container_databaserun_support_container)
ConnectionDockerConnectionLocalConnectionSshConnection)BecomeSUPPORTED_BECOME_METHODSSudo)	AuditModeCGroupVersion)CGroupMount
CGroupPathCGroupState	MountTypecheck_container_cgroup_statusTControllerHostConfig)boundTHostConfigTPosixConfigTRemoteConfigc                  $     e Zd ZdZd fdZ xZS )ControlGroupErrorz]Raised when the container host does not have the necessary cgroup support to run a container.c                   t               j                  }t        |      j                  }d| dj	                         }d}dt        d       d}|dk(  r||z  }n|r||z  }|j	                         }t        |   |       y )N
aS  

Run the following commands as root on the container host to resolve this issue:

  mkdir /sys/fs/cgroup/systemd
  mount cgroup -t cgroup /sys/fs/cgroup/systemd -o none,name=systemd,xattr
  chown -R {user}:{group} /sys/fs/cgroup/systemd  # only when rootless

NOTE: These changes must be applied each time the container host is rebooted.
z
      If rootless Podman is already running [1], you may need to stop it before
      containers are able to use the new mount point.

[1] Check for 'podman' and 'catatonit' processes.
z[
      When using Docker Desktop with WSL2, additional configuration [1] is required.

[1] zkhttps://docs.ansible.com/ansible-core/devel/dev_guide/testing_running_locally.html#docker-desktop-with-wsl2podman)r0   commandr1   docker_desktop_wsl2stripr(   super__init__)	selfargsreasonenginedd_wsl2messagepodman_messagedd_wsl_message	__class__s	           T/home/dcms/DCMS/lib/python3.12/site-packages/ansible_test/_internal/host_profiles.pyr]   zControlGroupError.__init__   s    !))!$';; 		
 
EG 	   A  B B X~%G~%G--/!    )r_   r   r`   strreturnNone)__name__
__module____qualname____doc__r]   __classcell__)rf   s   @rg   rU   rU      s    g$" $"rh   rU   Tfrozenc                  D    e Zd ZU dZded<   dZded<   ed	d       Zd
dZy)	Inventoryz.Simple representation of an Ansible inventory.z2dict[str, dict[str, dict[str, t.Union[str, int]]]]host_groupsNz t.Optional[dict[str, list[str]]]extra_groupsc                2    t        t        | |i            S )zKReturn an inventory instance created from the given hostname and variables.)all)ru   )rt   dict)name	variabless     rg   create_single_hostzInventory.create_single_host   s     TtY.?%@AArh   c                
   d}| j                   j                         D ]\  \  }}|d| dz  }|j                         D ]6  \  }}dj                  d |j                         D              }|| d| dz  }8 |dz  }^ | j                  xs i j                         D ]"  \  }}	|d| dz  }|	D ]
  }
||
 dz  } |dz  }$ |j	                         }|j
                  st        ||dz          t        j                  d| d	       y
)z8Write the given inventory to the specified path on disk. [z]
 c              3  2   K   | ]  \  }}| d | d  yw)z=""N ).0keyvalues      rg   	<genexpr>z"Inventory.write.<locals>.<genexpr>   s!     Vjc5#bq1Vs   rW   z>>> Inventory
   	verbosityN)	ru   itemsjoinrv   r[   explainr   r    info)r^   r_   pathinventory_textgrouphostshostr{   kvpchildrenchilds              rg   writezInventory.write   s6     ,,224 	#LE5%n,N#(;;= 4ihhVIOODUVVTF!C5"334 d"N	# !% 1 1 7R>>@ 	#OE8%n,N! /UG2,./ d"N	# (--/||D.4"78~&671Erh   )rz   ri   r{   zdict[str, t.Union[str, int]]rj   rt   )r_   r   r   ri   rj   rk   )	rl   rm   rn   ro   __annotations__rv   staticmethodr|   r   r   rh   rg   rt   rt      s0    8CC59L29B B Frh   rt   c                  d    e Zd ZdZ	 	 	 	 	 	 	 	 ddZddZddZddZddZddZ	ddZ
d	 Zd
 Zy)HostProfilezBase class for host profiles.c               t    || _         || _        t        |      | _        |xs g | _        i | _        	 i | _        y N)r_   configbool
controllertargetsstater   )r^   r_   r   r   s       rg   r]   zHostProfile.__init__   s<     	w-}"')
=')
Arh   c                     y)%Provision the host before delegation.Nr   r^   s    rg   	provisionzHostProfile.provision       rh   c                     y),Perform out-of-band setup before delegation.Nr   r   s    rg   setupzHostProfile.setup   r   rh   c                     y)=Executed during failure handling if this profile is a target.Nr   r   s    rg   on_target_failurezHostProfile.on_target_failure   r   rh   c                     y4Deprovision the host after delegation has completed.Nr   r   s    rg   deprovisionzHostProfile.deprovision  r   rh   c                     yrWait for the instance to be ready. Executed before delegation for the controller and after delegation for targets.Nr   r   s    rg   waitzHostProfile.wait  r   rh   c                     y)nPerform in-band configuration. Executed before delegation for the controller and after delegation for targets.Nr   r   s    rg   	configurezHostProfile.configure  r   rh   c                t    | j                   j                         D ci c]  \  }}|dvs|| c}}S c c}}w )N)r_   r   )__dict__r   )r^   r   r   s      rg   __getstate__zHostProfile.__getstate__  s3    -1]]-@-@-BczsEcQbFbU
cccs   44c                H    | j                   j                  |       i | _        y r   )r   updater   )r^   r   s     rg   __setstate__zHostProfile.__setstate__  s    U# 
rh   N)r_   r   r   rQ   r   zt.Optional[list[HostConfig]]rj   rk   rj   rk   )rl   rm   rn   ro   r]   r   r   r   r   r   r   r   r   r   rh   rg   r   r      sd    'B  B 	B
 .B 
B"4;LCA}drh   r   )	metaclassc                  "    e Zd ZdZedd       Zy)PosixProfilez#Base class for POSIX host profiles.c                    | j                   j                  d      }|sK| j                  j                  }t	        |t
              rt        | j                  |      }|| j                   d<   |S )z
        The Python to use for this profile.
        If it is a virtual python, it will be created the first time it is requested.
        python)r   getr   r   
isinstancer   r9   r_   )r^   r   s     rg   r   zPosixProfile.python  sV     )[[''F&"56+DIIv>#)DJJx rh   N)rj   r   )rl   rm   rn   ro   propertyr   r   rh   rg   r   r     s    - rh   r   c                  \    e Zd ZdZej
                  dd       Zej
                  dd       Zy)ControllerHostProfilez/Base class for profiles usable as a controller.c                     y)KReturn a connection for accessing the host as a controller from the origin.Nr   r   s    rg    get_origin_controller_connectionz6ControllerHostProfile.get_origin_controller_connection.  r   rh   c                     y)*Return the working directory for the host.Nr   r   s    rg   get_working_directoryz+ControllerHostProfile.get_working_directory2  r   rh   N)rj   rA   rj   ri   )rl   rm   rn   ro   abcabstractmethodr   r   r   rh   rg   r   r   +  s9    9Z Z 	9 9rh   r   c                  6    e Zd ZdZej
                  dd       Zy)SshTargetHostProfilez2Base class for profiles offering SSH connectivity.c                     y)PReturn SSH connection(s) for accessing the host as a target from the controller.Nr   r   s    rg   !get_controller_target_connectionsz6SshTargetHostProfile.get_controller_target_connections:  r   rh   Nrj   zlist[SshConnection])rl   rm   rn   ro   r   r   r   r   rh   rg   r   r   7  s    <_ _rh   r   c                      e Zd ZdZedd       Zej                  dd       ZddZddZedd       Z	e	j                  dd       Z	ddZ
dd	Zdd
ZddZy)RemoteProfilez(Base class for remote instance profiles.c                8    | j                   j                  d      S ) The saved Ansible Core CI state.core_cir   r   r   s    rg   core_ci_statezRemoteProfile.core_ci_stateB       zz~~i((rh   c                "    || j                   d<   y)r   r   Nr   r^   r   s     rg   r   zRemoteProfile.core_ci_stateG       !&

9rh   c                    | j                  d      | _        | j                  j                          | j                  j                         | _        y)r   TloadN)create_core_cir   startsaver   r   s    rg   r   zRemoteProfile.provisionL  s<    ***5!\\..0rh   c                    | j                   j                  t        j                  k(  s=| j                   j                  t        j                  k(  r(| j                   j
                  r| j                          yyyr   )r_   remote_terminater
   ALWAYSSUCCESSsuccessdelete_instancer   s    rg   r   zRemoteProfile.deprovisionS  sg    99%%)=)==$))B\B\`m`u`uBuz~  {D  {D  {L  {L  " {LBurh   c                8    | j                   j                  d      S )zAReturn the cached AnsibleCoreCI instance, if any, otherwise None.r   r   r   r   s    rg   r   zRemoteProfile.core_ciX  r   rh   c                "    || j                   d<   y)z'Cache the given AnsibleCoreCI instance.r   Nr   r   s     rg   r   zRemoteProfile.core_ci]  r   rh   c                    | j                   sH| j                  r<| j                  d      | _         | j                   j                  | j                         | j                   S )zLReturn the current AnsibleCoreCI instance, loading it if not already loaded.Fr   )r   r   r   r   r   s    rg   get_instancezRemoteProfile.get_instanceb  sH    || 2 2..E.:DLLLd001||rh   c                J    | j                         }|sy|j                          y)z%Delete the AnsibleCoreCI VM instance.N)r   stopr^   r   s     rg   r   zRemoteProfile.delete_instancej  s    ##%rh   c                F    | j                         }|j                          |S )z6Wait for an AnsibleCoreCI VM instance to become ready.)r   r   r   s     rg   wait_for_instancezRemoteProfile.wait_for_instances  s    ##%rh   c           
     f   | j                   j                  st        d| j                          t        | j                  t        | j                   j                  | j                   j                  | j                   j                  | j                   j                  | j                  rdnd      |      S )z,Create and return an AnsibleCoreCI instance.zNo arch specified for config: r   target)platformversionarchitectureprovidertag)r_   resourcer   )
r   archr$   r   r_   r   r   r   r  r   )r^   r   s     rg   r   zRemoteProfile.create_core_ciz  s    {{"@ NOO--++![[----$(OOL 

 
	
rh   N)rj   zt.Optional[dict[str, str]])r   zdict[str, str]rj   rk   r   )rj   zt.Optional[AnsibleCoreCI])r   r   rj   rk   )rj   r   )r   r   rj   r   )rl   rm   rn   ro   r   r   setterr   r   r   r   r   r   r   r   rh   rg   r   r   ?  s{    2) ) & &1#
 ) ) ^^& &
rh   r   c                      e Zd ZdZddZy)ControllerProfilez,Host profile for the controller as a target.c           	         t        ddddt        | j                        j                  | j                  j                  j
                        }t        | j                  |      gS )r   	localhostNroot)rz   r   portuseridentity_filepython_interpreter)r:   r   r_   r   controller_pythonr   rD   r^   settingss     rg   r   z3ControllerProfile.get_controller_target_connections  sS    & +//#yy::??
 dii233rh   Nr   rl   rm   rn   ro   r   r   rh   rg   r  r    s
    64rh   r  c                     e Zd ZdZdZ ej                  d       G d d             Zed!d       Z	e	j                  d"d       Z	ed!d	       Zej                  d"d
       Zed#d       Zd$dZd%dZd%dZd%dZd&dZed'd       Zd(dZd#dZed'd       Zd$dZd)dZd$dZd$dZd$dZd$dZd*dZd+dZd#dZd$dZd'dZ y ),DockerProfilez#Host profile for a docker instance.zansible-test-markerTrq   c                  :    e Zd ZU dZded<   ded<   ded<   ded	<   y
)DockerProfile.InitConfigz9Configuration details required to run the container init.	list[str]optionsri   rY   r   command_privilegedztuple[CGroupMount, ...]expected_mountsN)rl   rm   rn   ro   r   r   rh   rg   
InitConfigr    s    G  00rh   r  c                8    | j                   j                  d      S )z9Return the stored container name, if any, otherwise None.container_namer   r   s    rg   r  zDockerProfile.container_name  s     zz~~.//rh   c                "    || j                   d<   y)zStore the given container name.r  Nr   r   s     rg   r  zDockerProfile.container_name  s     (-

#$rh   c                8    | j                   j                  d      S )zKReturn the path to the cgroup v1 systemd hierarchy, if any, otherwise None.cgroup_pathr   r   s    rg   r   zDockerProfile.cgroup_path  s     zz~~m,,rh   c                "    || j                   d<   y)z2Store the path to the cgroup v1 systemd hierarchy.r   Nr   r   s     rg   r   zDockerProfile.cgroup_path  s     %*

=!rh   c                &    | j                   rd S d S )z4Label to apply to resources related to this profile.r   r   )r   r   s    rg   labelzDockerProfile.label  s     #'//,@Ax@Arh   c                   | j                   j                  du}| j                         }t        | j                   d| j                  j
                  d| j                   dg| j                   |j                  d| j                  ||      	      }|s?| j                   j                  r(|j                  s|rt        | j                   t               y|j                  | _        	 g d}|j                   r|j                  r|j                   }|s%|dt#        j$                  | j&                         z  }d	d
t)        |j*                  j,                  j.                        dddd|g}t1        | j                   d| j                   ||       |rt3        | j                   | j                  | j                  |j4                         d	d
t)        |j*                  j,                  j.                        ddg| j&                  z   }t1        | j                   d| j                   ||       yy# t6        $ rE t9        j:                  d| j                   d       t=        | j                   | j                          w xY w)r   N__test_hosts__zansible-test-   F)	r_   contextimagerz   portspublish_portsr  cleanupcmd)z--pidr   --privileged && nsenterz-t-mz-psh-czansible-test-init-zansible-test-wake-Checking container "	" logs...)r_   dev_probe_cgroupsget_init_configr@   r   r(  r#  r   r  build_init_commandprime_containersr  r-   r6   rz   r  rY   shlexr   wake_commandri   details	containerpidr3   rN   r  r   r    r   r,   )r^   
init_probeinit_configr<  r  init_commandr,  s          rg   r   zDockerProfile.provision  s   YY00<
**,)$++## -$"oo-''''Z@

	 yy))11Z		=9'nn	7G""{'E'E*22! d5::d6G6G+H*I$JJL $I,=,=,G,G,K,K(LdTXZ^`dfrs%dii3Edjj\1RTWY`a-diidFYFY[f[v[vw $I,=,=,G,G,K,K(LdTXY\`\m\mm%dii3Edjj\1RTWY`a	 
  	LL/0C0C/DINO		4#6#67		s   !D=H   AI.c                x    | j                          t               j                  } t        | d| d             }|S )zBReturn init config for running under the current container engine.get__init_config)check_cgroup_requirementsr0   rY   getattr)r^   ra   r?  s      rg   r6  zDockerProfile.get_init_config  s<    &&(!))@gdd6(,$?@Brh   c                   | j                         }d}d}t        | j                        j                  }|j	                  d       | j
                  j                  t        j                  k(  r3t        | j                        j                  dk(  r|j	                  d       t        | j                        j                  x}dt        dfvr,t        j                  d| dd	
       |j	                  d       | j
                  j                  t         j"                  k(  r?|j	                  d       t%        t&        j(                  t*        j,                  d	d      f}n| j
                  j                  t         j.                  t         j0                  fv r|dk(  r|j	                  d       | j3                  |       t%        t&        j(                  t*        j,                  d	d      t%        t&        j4                  t*        j6                  dt8        j:                        t%        t&        j<                  ddd      f}n| j
                  j                  t         j.                  t         j>                  fv rR|dk(  rM|j	                  d       t%        t&        j(                  t*        j@                  d	t8        jB                        f}n$| j
                  j                  t         j0                  k(  r|dk(  r| jE                         }d| d}|j	                  ddddddd| d| df       t%        t&        j(                  t*        j@                  d	t8        jB                        t%        t&        j4                  t*        j6                  dt8        jF                        t%        |t*        j6                  d	t8        j:                        f}n&tI        d| j
                  j                   d| d      | jK                  ||||      S ) z,Return init config for running under Podman.NF)	--cap-add
SYS_CHROOTEPERM)rG  AUDIT_WRITEr   zORunning containers with capability AUDIT_CONTROL since the container loginuid (zh) is incorrect. This is most likely due to use of sudo to run ansible-test when loginuid is already set.Tunique)rG  AUDIT_CONTROL)	--systemdfalse
--cgroupnsprivate--tmpfs/sys/fs/cgroupr   typewritabler   r   )rN  alwaysrP  r   rR  rS     )rN  rW  rP  rQ  	echo 1 > /cgroup.procsrN  rW  rP  rQ  --volumez0/sys/fs/cgroup/systemd:/sys/fs/cgroup/systemd:ro::rw Unhandled cgroup configuration:  on cgroup v.r  rY   r  r  )&get_common_run_optionsr1   r_   cgroup_versionextendr   auditrH   REQUIREDr2   
audit_codeloginuidr5   r    warningcgrouprI   NONErJ   rK   ROOTrM   TMPFSV1_V2V1_ONLYcheck_systemd_cgroup_v1SYSTEMD	CGROUP_V1rL   HOSTSYSTEMD_RELEASE_AGENTV2_ONLY	CGROUP_V2PRIVATEcreate_systemd_cgroup_v1SHADOWEDr$   r  )r^   r  rY   r  rc  rh  r  r   s           rg   get_podman_init_configz$DockerProfile.get_podman_init_config  sn   --/#'" )3BB 	23* ;;	 2 227Mdii7X7c7cgn7nNN78& /tyy9BBBHAO_aeKffOOmnvmw xw w @DE NN9:;;!3!33 NN   yQU]abO [[M$7$79N9N#OOTbfgTg NN   ((1 yQU]ab !3!3):M:MX\dodtdtu !A!AW\dhiO [[M$7$79N9N#OOTbfgTg NN   y7J7JUYalatatuO [[=#8#88^q=P 779K!+m<GNNX i N{m1[M=- 4 y7J7JUYalatatu!3!3):M:MX]epeyeyz93F3FQU]h]m]mnO  "B4;;CUCUBVVbcqbrrs tuu1+	  
 	
rh   c                   | j                         }d}d}t        | j                        j                  }| j                  j
                  t        j                  k(  rot        | j                        j                  r|j                  d       |j                  d       t        t        j                  t        j                  dd      f}n| j                  j
                  t        j                  t        j                   fv r|dk(  rt        | j                        j                  r|j                  d       |j                  d	       | j#                  |       t        t        j                  t        j                  dd      t        t        j$                  t        j&                  dt(        j*                        f}n| j                  j
                  t        j                  t        j,                  fv rV|d
k(  rQd}d}|j                  d       t        t        j                  t        j.                  dt(        j0                        f}n| j                  j
                  t        j                   k(  r|d
k(  r| j3                         }d| d}|j                  ddddddd| d| df       t        t        j                  t        j                  dd      t        t        j$                  t        j                  dd      t        |t        j&                  dt(        j*                        f}n&t5        d| j                  j
                   d| d      | j7                  ||||      S )z,Return init config for running under Docker.NF)rP  rQ  )rR  rS  TrT  r   )rP  r   )rR  rS  r[  0/sys/fs/cgroup/systemd:/sys/fs/cgroup/systemd:rwrX  z#mount -o remount,rw /sys/fs/cgroup/rY  rZ  rP  rQ  rR  rS  z/sys/fs/cgroup/systemdr[  r\  r]  r^  r_  r`  ra  )rb  r1   r_   rc  r   rj  rI   rk  cgroupns_option_supportedrd  rJ   rK   rl  rM   rm  rn  ro  rp  rq  rr  rL   rs  ru  rv  rw  rx  r$   r  )r^   r  rY   r  rc  r  r   s          rg   get_docker_init_configz$DockerProfile.get_docker_init_config  s   --/#'" )3BB;;!3!33 tyy)CC    
 NN   yQU]abO [[M$7$79N9N#OOTbfgTg
 tyy)CC
     NN 
 
 ((1 yQU]ab!3!3):M:MX\dodtdtuO [[M$7$79N9N#OOTbfgTg <G!%NN   y7J7JUYalatatuO [[=#8#88^q=P 779K!+m<GNN i + 3{m1[M=  yQU]ab!3!3)//TX`de93F3FQU]h]m]mnO  "B4;;CUCUBVVbcqbrrs tuu1+	  
 	
rh   c                   d}|j                   r|j                  s||j                    dz  }|s|j                  r|dz  }|syt        | j                  | j                  j
                         t        | j                  | j                  j
                        }|dt        j                  |j                         z  }dd|gS )a  
        Build and return the command to start in the container.
        Returns None if the default command for the container should be used.

        The sleep duration below was selected to:

          - Allow enough time to perform necessary operations in the container before waking it.
          - Make the delay obvious if the wake command doesn't run or succeed.
          - Avoid hanging indefinitely or for an unreasonably long time.

        NOTE: The container must have a POSIX-compliant default shell "sh" with a non-builtin "sleep" command.
              The "sleep" command is invoked through "env" to avoid using a shell builtin "sleep" (if present).
        r~   r.  zenv sleep 60 ; Nzexec r1  r2  )
rY   r  r-   r_   r   r(  r+   r9  r   r,  )r^   r?  sleeprY   inspects        rg   r7  z DockerProfile.build_init_command  s     {'E'E+--.d33GK22((GDIIt{{001&tyy$++2C2CDU5::gkk2344dG$$rh   c                
    ddgS )z
        The command used to wake the container from sleep.
        This will be run inside our utility container, so the command used does not need to be present in the container being woken up.
        pkillr  r   r   s    rg   r:  zDockerProfile.wake_command>  s     !!rh   c                   t        t        j                  j                  t        dd            j                  d| j                        j                  d| j                   d| j                  j                         }dg}	 t        | j                  d| j                   |||       y
# t        $ r=}| j                  |j                        x}rt        | j                  d	|       | d
}~ww xY w)zRCheck the cgroup v1 systemd hierarchy to verify it is writeable for our container.r   zcheck_systemd_cgroup_v1.shz@MARKER@z@LABEL@-r1  zansible-test-cgroup-check-)dataCUnable to create a v1 cgroup within the systemd hierarchy.
Reason: N)r   osr   r   r&   replaceMARKERr#  r_   session_namer3   r   extract_errorstderrrU   )r^   r  probe_scriptr,  exerrors         rg   rp  z%DockerProfile.check_systemd_cgroup_v1F  s    &rww||4LgWs'tu T[[9 tzzl!DII<R<R;S,TU 	 f	!$))/I$**-VX[]dkwx 	**29955u5'		 4>>CW4F GLNO 	s    'B( (	C.18C))C.c                   d| j                    d| j                  j                   | _        g d}dddt	        j
                  | j                         dt	        j
                  | j                         g}	 t        | j                  d| j                    ||       | j                  S # t        $ r=}| j                  |j                        x}rt        | j                  d	|       | d
}~ww xY w)zTCreate a unique ansible-test cgroup in the v1 systemd hierarchy and return its path.z$/sys/fs/cgroup/systemd/ansible-test-r  r[  r|  r-  r1  r2  	>&2 echo z
 && mkdir zansible-test-cgroup-create-r  N)r#  r_   r  r   r9  quoter  r3   r   r  r  rU   r^   r  r,  r  r  s        rg   rx  z&DockerProfile.create_systemd_cgroup_v1W  s    A$**QtyyOeOeNfg cTYu{{4;;'?&@
5;;W[WgWgKhJijk	!$))/J4::,-WY\^ef   	**29955u5'		 6>>CW4F GLNO 	s   6%B' '	C-08C((C-c                $    d| j                   dddgS )zbThe command used to remove the previously created ansible-test cgroup in the v1 systemd hierarchy.findz-typedz-delete)r   r   s    rg    delete_systemd_cgroup_v1_commandz.DockerProfile.delete_systemd_cgroup_v1_commandk  s     (('3	BBrh   c                   g d}dddt        j                  | j                         dt        j                  | j                         g}	 t        | j                  d| j                   ||       y# t        $ r[}| j                  |j                        x}r|j                  d      rY d}~yt        j                  t        |             Y d}~yd}~ww xY w)	zLDelete a previously created ansible-test cgroup in the v1 systemd hierarchy.r  r1  r2  r  r.  zansible-test-cgroup-delete-z: No such file or directoryN)r9  r  r  r   r  r3   r_   r#  r   r  r  endswithr    r  ri   r  s        rg   delete_systemd_cgroup_v1z&DockerProfile.delete_systemd_cgroup_v1p  s     cTYu{{4;;'?&@UZZPTPuPuEvDwxy	#!$))/J4::,-WY\^ef 	#**29955u5>>"?@MM#b'""	#s   %A1 1	C:.C-CCc                    |j                         j                         }	 |j                  | j                        }||dz   d }dj                  |      }|S # t        $ r Y yw xY w)z
        Extract the ansible-test portion of the error message from the given value and return it.
        Returns None if no ansible-test marker was found.
        Nr   rW   )r[   
splitlinesindexr  
ValueErrorr   )r^   r   linesidxrc   s        rg   r  zDockerProfile.extract_error  sg    
 ((*	++dkk*C cAgh))E"  		s   A 	A"!A"c                   t        | j                        j                  }|dvrt        d| d      | j                  j
                  t        j                  k(  r+|dk7  r&t        d| j                  j                   d| d      | j                  j
                  t        j                  k(  sI| j                  j
                  t        j                  k7  rt        | j                        j                  dk(  rt        | j                        j                  x}t        j                  k7  r| j                  j
                  t        j                  k(  rXt        | j                        j                  dk(  rd| j                  j                   d	}nd| j                  j                   d
}nd}|d|j                   z  }t!        | j                  |      yyy)z,Check cgroup requirements for the container.)r   rX  z$The container host provides cgroup vz+, but only version v1 and v2 are supported.rX  z
Container z< requires cgroup v2 but the container host provides cgroup vr`  r   zD requires cgroup v1, but the container host only provides cgroup v2.zR requires cgroup v1, but the container host does not appear to be running systemd.zQThe container host provides cgroup v1, but does not appear to be running systemd.rW   N)r1   r_   rc  r   r   rj  rI   ru  rz   ro  rk  r2   	cgroup_v1r4   VALIDr   rU   )r^   rc  r  r`   s       rg   rD  z'DockerProfile.check_cgroup_requirements  s   (3BB'"%I.IY  ZE  $F  G  G ;;!6!66>Q;N"Z0@0@/AA}  M  ~N  NO  $P  Q  Q& ;;!6!664;;;M;MQ^QcQc;chwx|  yB  yB  iC  iR  iR  VW  iW3DII>HHH	MhMnMnn;;%%)>)>>&tyy1@@AE#-dkk.>.>-?  @D  "E#-dkk.>.>-?  @R  "SpFBy/00'		6:: o iW;crh   c                   t        | j                  | j                  j                  | j                  j                  it        | j                              }|j                         }|j                         d   dd }	 t        | j                  | j                  |g|d       y# t        $ rE t        j                  d| j                   d       t        | j                  | j                          w xY w)	r   )r   python_interpretersssh_keyr   rX  NFr  capturer3  r4  )r7   r   r   r   r   r   r_   
get_scriptr  r*   r  r   r    r   r,   )r^   bootstrappersetup_shshells       rg   r   zDockerProfile.setup  s    &!%!4!4dkk6F6F G499%
  **,##%a(,			4#6#6hX]^ 	LL/0C0C/DINO		4#6#67	s   =%B# #AC1c           
        d}| j                   r| j                  j                  t        j                  k(  s=| j                  j                  t        j
                  k(  r7| j                  j                  r!t        | j                  | j                          nd}| j                  rl|rYt        j                  dt               j                   d| j                    dt        j                  | j                         d       y| j!                          y|r9t        j                  dt               j                   d| j                    d       yy)	r   FTzRemember to run `z rm -f z#` when finished testing. Then run `z` on the container host.z` when finished testing.N)r  r_   docker_terminater
   r   r   r   r.   r   r    noticer0   rY   r9  r   r  r  )r^   container_existss     rg   r   zDockerProfile.deprovision  s    yy))]-A-AAdiiF`F`dqdydyFy  C  H  H  P  P$))T%8%89#' !2>3C3K3K2LGTXTgTgSh i,,1JJt7\7\,]+^^v x y --/NN.~/?/G/G.HPTPcPcOdd|}~ rh   c                @     j                   s̉ j                         d   }d}t        dd      D ]  }	 |j                  dgd        y t        j                  d
       t        j                  |        j                  j                  s j                  j                  sd fd}nd}t        d j                  j                    d j"                   d|      y# t        $ r9}d	|j
                  v r t        |      }t        j                  d       Y d}~d}~ww xY w)r   r   r~   r   
   idTr  NzPermission deniedzChecking SSH debug output...c                 &     j                          y)z%Callback to run during error display.N)r   r   s   rg   callbackz$DockerProfile.wait.<locals>.callback  s    **,rh   Timeout waiting for z container r`  r   )r   r   rangerunr   rc   ri   timer  r    r   r_   delegate	host_pathr%   r   rz   r  )r^   con
last_errordummyr  r  s   `     rg   r   zDockerProfile.wait  s   88:1=CJq" 
	GGTFDG1 
 LL78LL$99%%dii.A.A-
  %(<T[[=M=M<NkZ^ZmZmYnno&prz{{9  ' "*bjj8!$RJJJqMM"s   C	D$/DDc           
        t        | j                        }|j                  t        j                     d   | j
                     }|j                  }t        |j                               d   }t        | j                  j                  d||t        | j                        j                  | j                  j                  d| j                  j                   v       }t#        | j                  |      gS )r   r%  r&  r
  centos6)rz   r  r   r  r  r  enable_rsa_sha1)r?   r_   r  r>   controlr  host_ipry   port_mapr:   r   rz   r   r   r   r   r(  rD   )r^   
containersaccessr   r  r  s         rg   r   z/DockerProfile.get_controller_target_connections  s    +DII6
!1!123CDTEXEXY~~FOO%&r*&!! +//#{{// &):)::
 dii233rh   c                B    t        | j                  | j                        S r   )rB   r_   r  r   s    rg   r   z.DockerProfile.get_origin_controller_connection  s    		4+>+>??rh   c                     y)r   z/rootr   r   s    rg   r   z#DockerProfile.get_working_directory  s    rh   c                   t        j                  d| j                   d       	 t        | j                  | j                         | j                  j                  t        j                  k7  rHt        j                  d| j                   d       	 t        | j                  | j                  dgd       t        j                  d| j                   d	       y# t
        $ r(}t        j                  t        |             Y d}~d}~ww xY w# t
        $ r(}t        j                  t        |             Y d}~d}~ww xY w)
r   r3  r4  Nz" systemd logs...
journalctlFr  zConnection to container "z," failed. See logs and original error above.)r    r   r  r,   r_   r   r  ri   r   rj  rI   rk  r*   )r^   r  s     rg   r   zDockerProfile.on_target_failure"  s    +D,?,?+@	JK	#		4#6#67 ;;!3!33LL/0C0C/DDUVW'DIIt':':\NTYZ 	1$2E2E1FFrst  	#MM#b'""	# # 'c"g&&'s/    C $D 	D	!DD		D=D88D=c                   g d}| j                   j                  r|j                  d       | j                   j                  rA|j	                  d| j                   j                   d| j                   j                   g       | j                   j
                  dk7  r*|j	                  dd| j                   j
                   g       d}t               d	k7  st        j                  j                  |      r|j	                  d
| d| g       |S )z5Return a list of options needed to run the container.)rR  z	/tmp:execrR  z	/run:execrR  z	/run/lockr-  z	--memory=z--memory-swap=defaultz--security-optzseccomp=z/var/run/docker.sockr	  r[  r\  )
r   
privilegedappendmemoryrd  seccompr/   r  r   exists)r^   r  docker_sockets      rg   rb  z$DockerProfile.get_common_run_options6  s    
 ;;!!NN>*;;NNDKK../0 !3!3 45 
 ;;)+NN,9L9L8M.NOP. K/277>>-3PNNJ=/=/(JKLrh   Nrj   t.Optional[str]r   ri   rj   rk   r   r   )rj   r  )r?  r  r  r   rj   zt.Optional[list[str]])rj   r  )r  r  rj   rk   )r   ri   rj   r  r   )rj   rB   )!rl   rm   rn   ro   r  dataclasses	dataclassr  r   r  r  r   r#  r   r6  rz  r~  r7  r:  rp  rx  r  r  r  rD  r   r   r   r   r   r   r   rb  r   rh   rg   r  r    s0   -"F[$'1 1 (1 0 0 - - - - * * B B/be
Nw
r%@ " "" ( C C# "+;Z$&|@4.@u("rh   r  c                      e Zd ZdZy)NetworkInventoryProfilez%Host profile for a network inventory.N)rl   rm   rn   ro   r   rh   rg   r  r  [  s    /rh   r  c                  0    e Zd ZdZddZddZddZd	dZy)
NetworkRemoteProfilez+Host profile for a network remote instance.c                $    | j                          yr   wait_until_readyr   s    rg   r   zNetworkRemoteProfile.waitb      rh   c                   | j                         }|j                  }t        | j                  j                  d|j                  |j
                  |j                  |j                  j                  d| j                  j                  r/| j                  j                   d| j                  j                   n| j                  j                        }|S )3Return inventory variables for accessing this host.yesnor`  )ansible_connectionansible_pipeliningansible_hostansible_portansible_useransible_ssh_private_key_file(ansible_paramiko_use_rsa_sha2_algorithmsansible_network_os)r   
connectionry   r   hostnamer  usernamer  r   
collectionr   r^   r   r  r{   s       rg   get_inventory_variablesz,NetworkRemoteProfile.get_inventory_variablesf  s    ((*''
>B#{{55$#,,##,,)0)<)< 6:UYU`U`UkUk$++"8"8!94;;;O;O:PQquq|q|  rF  rF?
	" rh   c           	        | j                         }t        | j                  t              syt        j                  t        | j                  j                        | j                               }t        | j                        }| j                  j                  r| j                  j                  dz   nd | j                  j                   d}t        j                         5 }|j                  | j                  |j                         dd|ddd	|j                  d
g}t!        dd      D ]:  }	 t#        | j                  | j                  j$                  ||d        ddd       y t3        d| j                  j                   d|j4                   d      # t&        $ r=}t)        j*                  t-        |             t/        j0                  d       Y d}~d}~ww xY w# 1 sw Y   yxY w):Wait for the host to respond to an Ansible module request.Nr`  r~   _commandansibler0  z-az
commands=?-irx   r   Z   Tr  r  r  
 instance )r   r   r_   r	   rt   r|   r"   r   rz   r  r;   r  r   tempfileNamedTemporaryFiler   r  r)   r  r   r    ri  ri   r  r  r%   instance_id	r^   r   	inventoryenvmodule_nameinventory_filer,  r  r  s	            rg   r  z%NetworkRemoteProfile.wait_until_ready~  s   ((*$))%67001CDKKDTDT1UW[WsWsWuv	!$)),9=9O9O//#5UWXY]YdYdYmYmXnnvw((* 	qnOODII~':':;dK|T>K^K^`efCq" $TYY		0K0KSRU_cd
 	q 	q
 &(<T[[=M=M<NjY`YlYlXmmn&opp ' #OOCG,JJrNN#	q 	qs=    A
G+.FG#1G	G3GGGGG&c                &   | j                         }t        |j                  |j                  j                  |j                  j
                  |j                  j                  |j                  j                  d      }t        | j                  |      gS )r   T)rz   r   r  r  r  r  )r   r:   rz   r  r  r  r  r  r   rD   r_   r^   r   r  s      rg   r   z6NetworkRemoteProfile.get_controller_target_connections  sv    ((*&##,,##((##,,!//-- !

 dii233rh   Nr   rj   z(dict[str, t.Optional[t.Union[str, int]]]r   rl   rm   rn   ro   r   r  r  r   r   rh   rg   r  r  _  s    5 0q64rh   r  c                       e Zd ZdZddZddZy)OriginProfilezHost profile for origin.c                ,    t        | j                        S r  )rC   r_   r   s    rg   r   z.OriginProfile.get_origin_controller_connection  s    tyy))rh   c                *    t        j                         S )r   )r  getcwdr   s    rg   r   z#OriginProfile.get_working_directory  s    yy{rh   N)rj   rC   r   )rl   rm   rn   ro   r   r   r   rh   rg   r
  r
    s    "*rh   r
  c                      e Zd ZdZddZddZddZddZddZddZ	ddZ
edd	       Zej                  dd
       Zy)PosixRemoteProfilez)Host profile for a POSIX remote instance.c                $    | j                          yr   r  r   s    rg   r   zPosixRemoteProfile.wait  r  rh   c           	     >   | j                   j                  | j                   j                  i}|j                  | j                  D ci c]>  }t        |t              s|j                   j                  |j                   j                  @ c}       t        t        |j                                     D ci c]  }|||   
 }}| j                         }| j                         }t        j                  d| d       t        | j                  | j                   j"                  | j                   j                  ||j$                        }|j'                         }|j)                         d   dd }| j+                         }	|	j-                  |g|d	       yc c}w c c}w )
r   zRemote working directory: r   r   )r   r   platform_versionr  r  r   rX  NFr  )r   r   r   r   r   r   r   r#   listkeysr   r  r    r   r8   r   r   r   r  r  r  r   r  )
r^   r  r   r   r   pwdr  r  r  sshs
             rg   r   zPosixRemoteProfile.configure  s     ${{22DKK4D4DE""\`\h\h  $RRXlvw}  @P  mQFMM$9$96==;M;M$M  $R  	STcdhi|  jB  jB  jD  eE  UF  Gw(;G(DD  G  G((*##%1#71E&[[))![[00 3OO
  **,##%a(,335h6) $R Gs   F-F4Fc                T   | j                         }t        |j                  |j                  j                  |j                  j
                  |j                  j                  |j                  j                  | j                  j                        }|j                  dk(  rd}nq| j                  j                  r"t        | j                  j                            }n9t        j                   d| j                  j"                   dd       t%               }t'        | j(                  ||      S )z0Return an SSH connection for accessing the host.rz   r  r   r  r  r  r
  Nz#Defaulting to "sudo" for platform "z" become support.TrK  )r   r:   rz   r  r  r  r  r  r   r   r   r  r   becomerF   r    ri  r   rG   rD   r_   )r^   r   r  r  s       rg   get_ssh_connectionz%PosixRemoteProfile.get_ssh_connection  s    ((*&##,,##,,##((!//--#{{//
 ==F")-F[[-dkk.@.@ACFOOA$++BVBVAWWhirvwVFTYY&99rh   c                Z   | j                         }t        dd      D ]  }	 | j                         c S  t        d| j                  j                   d|j                   d      # t        $ r=}t	        j
                  t        |             t        j                  d       Y d}~d}~ww xY w)z\Wait for instance to respond to SSH, returning the current working directory once connected.r   r  r  Nr  r  r`  )r   r  r   r   r    ri  ri   r  r  r%   r   rz   r  )r^   r   r  r  s       rg   r  z#PosixRemoteProfile.wait_until_ready  s    ((*1b\ 	E1133	 "$89I9I8J*U\UhUhTiij"kll #  B(

2	s   A$$	B*-3B%%B*c                $    | j                         gS )r   r  r   s    rg   r   z4PosixRemoteProfile.get_controller_target_connections  s    '')**rh   c                "    | j                         S r  r  r   s    rg   r   z3PosixRemoteProfile.get_origin_controller_connection  s    &&((rh   c                ^   | j                   s| j                         }|j                  dgd      d   }| j                  j                  ry|j                         j                         d   }|j                  d      st        d| d	|j                                || _         | j                   S )
r   r  Tr  r   z/pwd/z&Unexpected current working directory "z" from "pwd" command output:
)	r  r   r  r_   r   r[   r  
startswith	Exception)r^   r  stdoutr  s       rg   r   z(PosixRemoteProfile.get_working_directory  s    xx779CWWeWdW3A6Fyy  ,,.++-b1C>>#&"HMklrlxlxlzk{ |}}DHxxrh   c                8    | j                   j                  d      S )z.Return the cached pwd, if any, otherwise None.r  r   r   s    rg   r  zPosixRemoteProfile.pwd  s     zz~~e$$rh   c                "    || j                   d<   y)zCache the given pwd.r  Nr   r   s     rg   r  zPosixRemoteProfile.pwd  s     "

5rh   Nr   )rj   rD   r   r   r  r  )rl   rm   rn   ro   r   r   r  r  r   r   r   r   r  r  r   rh   rg   r  r    sU    3 74:.m+)$ % % 	ZZ" "rh   r  c                      e Zd ZdZddZy)PosixSshProfilez&Host profile for a POSIX SSH instance.c           	     ,   t        d| j                  j                  | j                  j                  | j                  j                  t        | j                        j                  | j                  j                        }t        | j                  |      gS )r   r   r  )r:   r   r  r   r  r   r_   r   r   r   rD   r  s     rg   r   z1PosixSshProfile.get_controller_target_connections'  sk    &!!!!!! +//#{{//
 dii233rh   Nr   r  r   rh   rg   r(  r(  $  s
    04rh   r(  c                      e Zd ZdZddZy)WindowsInventoryProfilez%Host profile for a Windows inventory.c                   t        | j                  | j                  j                        }t	        |d      }t        | j                        j                  }|j                         D cg c]  \  }}t        ||d   d|d   |d       }}}|r2dj                  d |D              }t        j                  d	| d
       |D cg c]  }t        | j                  |       c}S c c}}w c c}w )r   windowsr  r&  r  
powershellrz   r   r  r  r  
shell_typerW   c              3     K   | ]9  }|j                    d |j                   d|j                   d|j                    ; yw)r   @r\  N)rz   r  r   r  )r   r  s     rg   r   zLWindowsInventoryProfile.get_controller_target_connections.<locals>.<genexpr>H  s8     cSV388*AchhZq
!CHH: Ncs   ?Az1Generated SSH connection details from inventory:
r   r   )r=   r_   r   r   r<   r   r   r   r:   r   r    r   rD   )	r^   r  r   r  rz   r   r  r;  settings	            rg   r   z9WindowsInventoryProfile.get_controller_target_connections8  s    #DIIt{{/?/?@	)Y/tyy)-- $kkm- dF ('''#
 - - iicZbccGLLMgYWcdeAIJgdii1JJ- Ks   )!C%C+Nr   r  r   rh   rg   r+  r+  5  s    /Krh   r+  c                  0    e Zd ZdZddZddZddZd	dZy)
WindowsRemoteProfilez+Host profile for a Windows remote instance.c                $    | j                          yr   r  r   s    rg   r   zWindowsRemoteProfile.waitQ  r  rh   c                   | j                         }|j                  }t        |j                  |j                  |j
                  j                  |j                        }|j                  | j                  j                  j                  d      d          |j                  t        | j                  j                            |j                  d      r|j                  |j                         |S )r  )r  r  r   ansible_test_connection_password+r   )r  use_password)ansible_password)r   r  ry   r  r  r  r   passwordr   r   splitr'   popr  s       rg   r  z,WindowsRemoteProfile.get_inventory_variablesU  s    ((*''
>B#,,#,,)0)<)<-7-@-@?
	 	DKK,B,B,H,H,Ma,PQ5dkk6L6LMN==(j.A.ABrh   c           	     P   | j                         }t        | j                  t              syt        j                  t        | j                  j                        | j                               }t        | j                        }d}t        j                         5 }|j                  | j                  |j                         dd|d|j                  dg}t        dd      D ]:  }	 t        | j                  | j                  j                   ||d	
        ddd       y 	 ddd       t/        d| j                  j                   d|j0                   d      # t"        $ r=}t%        j&                  t)        |             t+        j,                  d       Y d}~d}~ww xY w# 1 sw Y   xY w)r  Nzansible.windows.win_pingr  r0  r  rx   r   x   Tr  r  r  r  r`  )r   r   r_   r	   rt   r|   r"   r   rz   r  r;   r  r  r   r  r)   r  r   r    ri  ri   r  r  r%   r  r   s	            rg   r  z%WindowsRemoteProfile.wait_until_readyj  s]   ((*$))%67001CDKKDTDT1UW[WsWsWuv	!$)),0((* 	nOODII~':':;dK~7J7JERCq# $TYY		0K0KSRU_cd
 	 	
	 "$89I9I8J*U\UhUhTiij"kll ' #OOCG,JJrNN#	 	s=   AF!.EFF	F3FFFFF%c                    | j                         }t        |j                  |j                  j                  d|j                  j
                  |j                  j                  d      }t        | j                  |      gS )r   r&  r.  r/  )
r   r:   rz   r  r  r  r  r   rD   r_   r  s      rg   r   z6WindowsRemoteProfile.get_controller_target_connections  sh    ((*&##,,##,,!//--#
 dii233rh   Nr   r  r   r  r   rh   rg   r5  r5  N  s    5 *m64rh   r5  c                 *    t        t        t              S )zECreate and return a mapping of HostConfig types to HostProfile types.)r!   r   r   r   rh   rg   get_config_profile_type_maprC    s     Z00rh   c                d    t               t        |         } || ||r| j                  nd      }|S )zCCreate and return a host profile from the given host configuration.N)r_   r   r   )rC  rU  r   )r_   r   r   profile_typeprofiles        rg   create_host_profilerG    s1     /0f>LVZT\\]abGNrh   )rj   z-dict[t.Type[HostConfig], t.Type[HostProfile]])r_   r   r   r   r   r   rj   r   )ro   
__future__r   r   r  r  r9  r  r  typingtior   r   r   r   r   r	   r
   host_configsr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   utilr   r   r   r    r!   r"   r#   r$   r%   r&   r'   util_commonr(   r)   docker_utilr*   r+   r,   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   	bootstrapr7   r8   venvr9   r  r:   ansible_utilr;   r<   r=   r  r>   r?   r@   connectionsrA   rB   rC   rD   r  rE   rF   rG   
completionrH   rI   dev.container_proberJ   rK   rL   rM   rN   TypeVarrO   rQ   rR   rS   rU   r  rt   GenericABCMetar   r   r   r   r   r  r  r  r  r
  r  r(  r+  r5  rC  rG  r   rh   rg   <module>rY     s   T " 
  	    
     $    
    
    
  "		"9AUV aiiZ8qyy{;		/>'"( '"T d#+F +F $+F\-!))K(CKK -`;|, ,	9L)>?3;; 	9_;{3s{{ _J
(73;; J
Z4,-=>M]@^ 4"{
),79Ml9[ {
|0k*@A 0J4=)<= J4Z	),7 	i"./@A=QbCc i"X4*>:L<X 4"K23IJ K2D4=)<= D4N 1 1

  	rh   