
    p
fG                     &   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 d dlm	Z	m
Z
 d dlmZ d dlmZ d dlmZmZmZmZ d dlmZ d dlmZ d d	lmZmZ d d
lmZmZmZ d dlm Z  d dl!m"Z" dZ#ddgZ$dZ% G d de      Z& G d de'      Z( G d de(      Z)y)    N)logger)	CpuCgroupMemoryCgroup)CGroupsTelemetry)get_agent_pid_file_path)CGroupsExceptionExtensionErrorCodesExtensionErrorExtensionOperationError)ustr)systemd)fileutil	shellutil)handle_process_completionread_outputTELEMETRY_MESSAGE_MAX_LEN)FlexibleVersion)
get_distroz/sys/fs/cgroupcpumemoryzazure-vmextensionsc                   $     e Zd ZdZd fd	Z xZS )SystemdRunErrorz'
    Raised when systemd-run fails
    c                 ,    t         t        |   |       y N)superr   __init__)selfmsg	__class__s     B/usr/lib/python3/dist-packages/azurelinuxagent/common/cgroupapi.pyr   zSystemdRunError.__init__1   s    ot-c2    r   )__name__
__module____qualname____doc__r   __classcell__)r   s   @r    r   r   ,   s    3 3r!   r   c                   \    e Zd Zed        Zed        Zed        Zed        Zed        Zy)
CGroupsApic                      t               } | d   }	 t        | d         }|j                         dk(  xr |j                  dk\  S # t        $ r Y yw xY w)Nr      Fubuntu   )r   r   
ValueErrorlowermajor)distro_infodistro_namedistro_versions      r    cgroups_supportedzCGroupsApi.cgroups_supported6   s_     l!!n	,[^<N   "h.M>3G3G23MM  		s   A 	AAc           	          	 | D ]  }t        j                  |        y # t        $ rB}t        j                  dj                  j                  t        |                   Y d }~y d }~ww xY w)NzXCannot add cgroup '{0}' to tracking list; resource usage will not be tracked. Error: {1})r   track_cgroup	Exceptionr   warnformatpathr   )extension_cgroupscgroup	exceptions      r    track_cgroupszCGroupsApi.track_cgroups@   sc    	K+ 6 --f56 	KKK %%+VFKKi%IK K	Ks    	A*8A%%A*c                     t        t        j                  j                  | d      d      5 }|j	                         j                         D cg c]  }t        |       c}cd d d        S c c}w # 1 sw Y   y xY w)Ncgroup.procsr)openosr9   joinreadsplitint)cgroup_pathcgroup_procspids      r    get_processes_in_cgroupz"CGroupsApi.get_processes_in_cgroupI   sc    "'',,{N;SA 	E\(4(9(9(;(A(A(CDCHD	E 	ED	E 	Es   !A/A*A/*A//A8c                 "   g }dD ]q  }t         j                  j                  t        |dd      }t         j                  j	                  |      sIt        j                  d|       |j                  ||f       s 	 |D ]  \  }}t         j                  j                  |d      }t         j                  j	                  |      sFt        j                  |      j                         }t        j                         }t        |      |v s | ||        	 |D ]2  \  }}t        j                  d|       t        j                  |d       4 t!        |      S # |D ]2  \  }}t        j                  d|       t        j                  |d       4 w xY w)aE  
        Previous versions of the daemon (2.2.31-2.2.40) wrote their PID to /sys/fs/cgroup/{cpu,memory}/WALinuxAgent/WALinuxAgent;
        starting from version 2.2.41 we track the agent service in walinuxagent.service instead of WALinuxAgent/WALinuxAgent. Also,
        when running under systemd, the PIDs should not be explicitly moved to the cgroup filesystem. The older daemons would
        incorrectly do that under certain conditions.

        This method checks for the existence of the legacy cgroups and, if the daemon's PID has been added to them, executes the
        given operation on the cgroups. After this check, the method attempts to remove the legacy cgroups.

        :param operation:
            The function to execute on each legacy cgroup. It must take 2 arguments: the controller and the daemon's PID
        r   r   WALinuxAgentzFound legacy cgroup {0}r?   zRemoving {0}T)ignore_errors)rB   r9   rC   CGROUPS_FILE_SYSTEM_ROOTexistsr   infoappendr   	read_filestripr(   get_daemon_pidr   shutilrmtreelen)	operationlegacy_cgroups
controllerr;   
procs_fileprocs_file_contents
daemon_pid_s           r    _foreach_legacy_cgroupz!CGroupsApi._foreach_legacy_cgroupN   sR    + 	<JWW\\":JXfgFww~~f%5v>%%z6&:;		<	:&4 :"
FWW\\&.A
77>>*-*2*<*<Z*H*N*N*P'!+!:!:!<JJ'+>>!*j9: , :	6NF3fD9: >"" , :	6NF3fD9:s   :AE AE E 9Fc                  j    t        t        j                  t                     j	                               S r   )rF   r   rS   r   rT    r!   r    rU   zCGroupsApi.get_daemon_pids   s%    8%%&=&?@FFHIIr!   N)	r"   r#   r$   staticmethodr3   r=   rJ   r`   rU   rb   r!   r    r(   r(   5   sm    N N K K E E "# "#H J Jr!   r(   c                       e Zd ZdZd Zd Zd Zed        Zd Z	d Z
ed        Zed	        Zedd
       Zej                  fdZd Zy)SystemdCgroupsApiz'
    Cgroups interface via systemd
    c                 `    d | _         d | _        g | _        t        j                         | _        y r   )_cgroup_mountpoints_agent_unit_name_systemd_run_commands	threadingRLock_systemd_run_commands_lockr   s    r    r   zSystemdCgroupsApi.__init__}   s)    #'  $%'"*3//*;'r!   c                 d    | j                   5  | j                  dd cddd       S # 1 sw Y   yxY w)z^
        Returns a list of the systemd-run commands currently running (given as PIDs)
        N)rl   ri   rm   s    r    get_systemd_run_commandsz*SystemdCgroupsApi.get_systemd_run_commands   s0     ,, 	1--a0	1 	1 	1s   &/c                 ,   | j                   kd}d}t        j                  g d      j                         D ]5  }t	        j
                  d|      }||j                  d      }d|v r|}4|}7 ||d| _         | j                   d   | j                   d   fS )	z
        Returns a tuple with the mount points for the cpu and memory controllers; the values can be None
        if the corresponding controller is not mounted
        N)mount-tr;   z%on\s+(?P<path>/\S+(memory|cpuacct))\sr9   cpuacctrL   r   r   )rg   r   run_command
splitlinesresearchgroup)r   r   r   linematchr9   s         r    get_cgroup_mount_pointsz)SystemdCgroupsApi.get_cgroup_mount_points   s     ##+CF!--.GHSSU &		"JDQ$ ;;v.D D("!%& 03f'ED$''.0H0H0RRRr!   c                    d}d}t        j                  dj                  |             j                         D ]  }t	        j
                  d|      }|R|j                  d      }|j                  d      dk7  r |j                  d      j                  d      nd}|dk(  r|}j|}mt	        j
                  d|      }|s|j                  d      dk7  r |j                  d      j                  d      nd}|}|} ||fS )	a  
        Returns a tuple with the path of the cpu and memory cgroups for the given process (relative to the mount point of the corresponding
        controller).
        The 'process_id' can be a numeric PID or the string "self" for the current process.
        The values returned can be None if the process is not in a cgroup for that controller (e.g. the controller is not mounted).
        Nz/proc/{0}/cgroupz5\d+:(?P<controller>(memory|.*cpuacct.*)):(?P<path>.+)r[   r9   /r   z\d+::(?P<path>.+))r   rS   r8   ru   rv   rz   rx   lstrip)
process_idcpu_pathmemory_pathry   rz   r[   r9   s          r    !get_process_cgroup_relative_pathsz3SystemdCgroupsApi.get_process_cgroup_relative_paths   s    " &&'9'@'@'LMXXZ 	'DHHUW[\E "[[6
:?++f:MQT:Tu{{6*11#6Z^)"&K#H!5t<>Ckk&>QUX>X5;;v.55c:^bD#H"&K	'" $$r!   c                    | j                  |      \  }}| j                         \  }}||pt        j                  d       | j	                         \  }}|Fd}d|vsd|v rt        j                  d       d}d|vsd	|v rt        j                  d
       d}|s|}|}|"| t
        j                  j                  ||      nd}	|"| t
        j                  j                  ||      nd}
|	|
fS )a<  
        Returns a tuple with the path of the cpu and memory cgroups for the given process. The 'process_id' can be a numeric PID or the string "self" for the current process.
        The values returned can be None if the process is not in a cgroup for that controller (e.g. the controller is not mounted).
        Nz Cgroup v1 mount points not foundFr   z-memoryz)Memory controller not active in cgroup v2Tr   z-cpuz&CPU controller not active in cgroup v2)r   r{   r   r7   get_cgroup2_controllersrB   r9   rC   )r   r   cpu_cgroup_relative_pathmemory_cgroup_relative_pathcpu_mount_pointmemory_mount_pointcgroup2_mount_pointcgroup2_controllerscontroller_missingcpu_cgroup_pathmemory_cgroup_paths              r    get_process_cgroup_pathsz*SystemdCgroupsApi.get_process_cgroup_paths   s'   
 AE@f@fgq@r= "=.2.J.J.L++"&8&@KK:;7;7S7S7U4!4".%*"#66)GZ:ZKK KL)-& 33vAT7TKK HI)-&)&9O)<& */G/S '',,8PQY] 	 "-2M2Y  WW\\*<>YZ_c 	  222r!   c                     t        j                  |d      }| j                         \  }}|#t        j                  j                  ||dd       nd}|#t        j                  j                  ||dd       nd}||fS )a'  
        Returns a tuple with the path of the cpu and memory cgroups for the given unit.
        The values returned can be None if the controller is not mounted.
        Ex: ControlGroup=/azure.slice/walinuxagent.service
        controlgroup_path[1:] = azure.slice/walinuxagent.service
        ControlGroupNr*   )r   get_unit_propertyr{   rB   r9   rC   )r   	unit_namecontrolgroup_pathr   r   r   r   s          r    get_unit_cgroup_pathsz'SystemdCgroupsApi.get_unit_cgroup_paths   s     $55iP.2.J.J.L++ * '',,8I!"8MN04 	 "-  WW\\*<>OPQPR>ST37 	  222r!   c                  b   t        j                  g d      j                         D ]  } t        j                  d|       }||j                  d      }d}t        j                  j                  |d      }t        j                  j                  |      rt        j                  |      }||fc S  y)z
        Returns a tuple with the mount point for the cgroups v2 controllers, and the currently mounted controllers;
        either value can be None if cgroups v2 or its controllers are not mounted
        )rq   rr   cgroup2zon\s+(?P<path>/\S+)\sNr9   zcgroup.subtree_control)NN)r   rt   ru   rv   rw   rx   rB   r9   rC   rP   r   rS   )ry   rz   mount_pointcontrollerscontrollers_files        r    r   z)SystemdCgroupsApi.get_cgroup2_controllers   s     ))*DEPPR 		0DII6=E #kk&1"#%77<<=U#V 77>>"23"*"4"45E"FK"K//		0 r!   c                     |j                  d       t        |j                  t              dd      }dj	                  |       }||v xs | |vS )Nr   zutf-8backslashreplace)encodingerrorszUnit {0} not found.)seekr   rD   r   r8   )
scope_namestderrunit_not_founds      r    _is_systemd_failurez%SystemdCgroupsApi._is_systemd_failure  sK    Afkk";<wWij.55jA'C:V+CCr!   c                 n    |s| j                  dd      d   } t        dz   | j                  dd      z   dz   S )N-r*   r   r_   z.slice)rsplitEXTENSION_SLICE_PREFIXreplace)extension_name	old_slices     r    get_extension_slice_namez*SystemdCgroupsApi.get_extension_slice_name  sA    
 +223:1=N%+n.D.DS#.NNQYYYr!   c           
         dj                  |t        j                               }| j                  |      }| j                  5  t        j                  dj                  |||      ||||	|t        j                        }| j                  j                  |j                         d d d        |dz   }t        j                  d|       d }	 t        j                  j                  d|      }| j!                         \  }}|t        j                  d       nAt        j                  j                  ||      }t#        ||      }t%        j&                  |       |t        j                  d       nAt        j                  j                  ||      }t)        ||      }t%        j&                  |       	 t3        ||||	|
|      | j                  5  | j                  j5                  |j                         d d d        S # 1 sw Y   ZxY w# t*        $ rM}|j,                  d	k(  rt        j                  d
       t        j                  dt/        |             Y d }~d }~wt0        $ r)}t        j                  dt/        |             Y d }~d }~ww xY w# 1 sw Y   S xY w# t6        $ r}| j9                  ||	      s t;        ||	      }|j=                  d       |	j=                  d       t?        |t@              rd|jB                  d|}tE        |      d|z  }tE        |      d }~ww xY w# | j                  5  | j                  j5                  j                         d d d        w # 1 sw Y   w xY wxY w)Nz{0}_{1}zisystemd-run --property=CPUAccounting=no --property=MemoryAccounting=no --unit={0} --scope --slice={1} {2})shellcwdstdoutr   env
preexec_fnz.scopezStarted extension in unit '{0}'z$azure.slice/azure-vmextensions.slicez@The CPU controller is not mounted; will not track resource usagezCThe Memory controller is not mounted; will not track resource usage   zFThe extension command already completed; will not track resource usagez>Failed to start tracking resource usage for the extension: {0})processcommandtimeoutr   r   
error_code
cpu_cgroupr   z!Systemd process exited with code z and output zSystemd timed-out, output: %s)#r8   uuiduuid4r   rl   
subprocessPopenrB   setsidri   rR   rI   r   rQ   r9   rC   r{   r   r   r5   r   IOErrorerrnor   r6   r   remover
   r   r   truncate
isinstancer   	exit_coder   )r   r   r   cmd_namer   r   r   r   r   r   r   scopeextension_slice_namer   r   r   cgroup_relative_pathcpu_cgroup_mountpointmemory_cgroup_mountpointr   r   memory_cgroupeprocess_outputerr_msgs                            r    start_extension_commandz)SystemdCgroupsApi.start_extension_command#  s     4::<8#<<^L,, 	; && |  C  C  DI  K_  ah  i99
&G &&--gkk:	;  X%
5zB
	c#%77<<0VXl#m >B>Z>Z>\;!#;$,^_"$'',,/DFZ"[&~G
 --j9'/ab%'WW\\2JL`%a" ,^=O P --m<	?,WgW^gm4:z^hj. 00 ?**11'++>? ?Q	; 	;P  	cww!|deKKXZ^_`Zabb 	cKKXZ^_`Zabb	c8? ?+  	+ ++E6: )8NOOAOOA!45 KK1 "'** :NJ!'**'	+* 00 ?**11'++>? ? ?s|   A!G;	C%H /J &J;H	JAIJ%J		JJ		L)&A>L$$L))L, ,M49&M(	M4(M1-M4c                 .    t         j                  d       S )a  
        Previous versions of the daemon (2.2.31-2.2.40) wrote their PID to /sys/fs/cgroup/{cpu,memory}/WALinuxAgent/WALinuxAgent;
        starting from version 2.2.41 we track the agent service in walinuxagent.service instead of WALinuxAgent/WALinuxAgent. If
        we find that any of the legacy groups include the PID of the daemon then we need to disable data collection for this
        instance (under systemd, moving PIDs across the cgroup file system can produce unpredictable results)
        c                       y r   rb   )r_   s    r    <lambda>z:SystemdCgroupsApi.cleanup_legacy_cgroups.<locals>.<lambda>y  s    r!   )r(   r`   rm   s    r    cleanup_legacy_cgroupsz(SystemdCgroupsApi.cleanup_legacy_cgroupsr  s     00AAr!   N)F)r"   r#   r$   r%   r   ro   r{   rc   r   r   r   r   r   r   r	   PluginUnknownFailurer   r   rb   r!   r    re   re   x   s    <1S6 #% #%J3>3$  * D D Z Z ,?+S+SM?^Br!   re   )*rB   rv   rV   r   rj   r   azurelinuxagent.commonr   azurelinuxagent.common.cgroupr   r   'azurelinuxagent.common.cgroupstelemetryr   azurelinuxagent.common.confr    azurelinuxagent.common.exceptionr   r	   r
   r   azurelinuxagent.common.futurer   azurelinuxagent.common.osutilr   azurelinuxagent.common.utilsr   r   1azurelinuxagent.common.utils.extensionprocessutilr   r   r   -azurelinuxagent.common.utils.flexible_versionr   azurelinuxagent.common.versionr   rO   CGROUP_CONTROLLERSr   r   objectr(   re   rb   r!   r    <module>r      s   $ 
 	     ) A D ?  . 1 <  I 5+ X& - 3& 3@J @JFAB
 ABr!   