o
    fI                  
   @   s8  U 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ddl	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mZ ddlmZ dd	lmZ dd
lmZ ddlmZm Z  ddl!m"Z" dZ#e"Z$dege$g dZ%ee&d< ddgddZ'edZ(G dd dZ)e*e+Z,G dd de-Z.G dd de
Z/G dd de/Z0G dd de/Z1G d d! d!e/Z2d"e3d#e d$e4d%e/fd&d'Z5d%ee6 fd(d)Z7d%ee6 fd*d+Z8d,d- Z9d%e:fd.d/Z;d0d1 Z<d%ee3e3f fd2d3Z=d4d5 Z>d6e/d#e fd7d8Z?d9e3d:ed;ed<e4d%df
d=d>Z@d?e0fd@e1fdAe2ffZAdS )BzGrowpart: Grow partitions    N)ABCabstractmethod)suppress)Path)OptionalTuple)	lifecyclesubp
temp_utilsutil)Cloud)Config)
MetaSchema)ALL_DISTROSDistro)
PER_ALWAYS cc_growpart)iddistros	frequencyactivate_by_schema_keysmetaauto/F)modedevicesignore_growroot_disabledz/cc_growpart_keydatac                   @   s   e Zd ZdZdZdZdZdS )RESIZESKIPPEDCHANGEDNOCHANGEFAILEDN)__name__
__module____qualname__r   r    r!   r"    r&   r&   >/usr/lib/python3/dist-packages/cloudinit/config/cc_growpart.pyr   1   s
    r   c                   @   s   e Zd ZdS )ResizeFailedExceptionN)r#   r$   r%   r&   r&   r&   r'   r(   ;   s    r(   c                   @   s<   e Zd ZdefddZededefddZedd	 Z	d
S )Resizerdistroc                 C   s
   || _ d S N)_distro)selfr*   r&   r&   r'   __init__@   s   
zResizer.__init__r   returnc                 C      d S r+   r&   r-   r   r&   r&   r'   	availableC      zResizer.availablec                 C   r0   r+   r&   )r-   diskdevpartnumpartdevfsr&   r&   r'   resizeF   r3   zResizer.resizeN)
r#   r$   r%   r   r.   r   listboolr2   r8   r&   r&   r&   r'   r)   ?   s    r)   c                   @   "   e Zd ZdefddZdd ZdS )ResizeGrowPartr   c                 C   sH   zt j ddgddidj}td|rW dS W dS  t jy#   Y dS w )	Ngrowpartz--helpLANGC
update_envz--update\s+TF)r	   stdoutresearchProcessExecutionError)r-   r   outr&   r&   r'   r2   K   s   zResizeGrowPart.availablec                 C   sJ  t ||}| j }tj|dd}tj|d}d|d}	tj|s*t	|d zt
j
dd||g|	d W n0 t
jyg }
 z#|
jd	krRttd
|| t|
|
||fW  Y d }
~
W  d    S d }
~
ww zt
j
d||g|	d W n t
jy }
 zttd|| t|
|
d }
~
ww W d    n1 sw   Y  |t ||fS )NT)dir	needs_exer=   r?   )r>   TMPDIRi  z	--dry-runr@      z&Failed growpart --dry-run for (%s, %s)zFailed: growpart %s %s)get_sizer,   get_tmp_exec_pathr
   tempdirospathjoinexistsmkdirr	   rE   	exit_coder   logexcLOGr(   )r-   r4   r5   r6   r7   beforetmp_dirtmpdgrowpart_tmpmy_enver&   r&   r'   r8   W   sF   








zResizeGrowPart.resizeNr#   r$   r%   r9   r2   r8   r&   r&   r&   r'   r<   J       r<   c                   @   s&   e Zd ZdZdefddZdd ZdS )ResizeGrowFSa  
    Use FreeBSD ``growfs`` service to grow root partition to fill available
    space, optionally adding a swap partition at the end.

    Note that the service file warns us that it uses ``awk(1)``, and as
    such requires ``/usr`` to be present. However, cloud-init is installed
    into ``/usr/local``, so we should be fine.

    We invoke the ``growfs`` with ``service growfs onestart``, so it
    doesn't need to be enabled in ``rc.conf``.
    r   c                 C   s   t jdo
|dgkS )z'growfs only works on the root partitionz/etc/rc.d/growfsr   )rN   rO   isfiler1   r&   r&   r'   r2      s   zResizeGrowFS.availablec              
   C   s^   t ||}z
| jjddd W n tjy' } zttd t||d }~ww |t ||fS )Nonestartgrowfs)actionservicezFailed: service growfs onestart)	rK   r,   manage_servicer	   rE   r   rT   rU   r(   )r-   r4   r5   r6   r7   rV   r[   r&   r&   r'   r8      s   

zResizeGrowFS.resizeN)r#   r$   r%   __doc__r9   r2   r8   r&   r&   r&   r'   r^   {   s    r^   c                   @   r;   )ResizeGpartr   c                 C   sN   zt j ddgddiddgdj}td|rW d	S W d
S  t jy&   Y d
S w )Ngparthelpr>   r?   r   rJ   )rA   rcszgpart recover TF)r	   stderrrC   rD   rE   )r-   r   errr&   r&   r'   r2      s   zResizeGpart.availablec              
   C   s   z
t  dd|g W n$ t jy. } z|jdkr$ttd| t||W Y d}~nd}~ww t||}zt  ddd||g W n t jyZ } zttd|| t||d}~ww |t||fS )	a9  
        GPT disks store metadata at the beginning (primary) and at the
        end (secondary) of the disk. When launching an image with a
        larger disk compared to the original image, the secondary copy
        is lost. Thus, the metadata will be marked CORRUPT, and need to
        be recovered.
        rg   recoverr   zFailed: gpart recover %sNr8   z-izFailed: gpart resize -i %s %s)r	   rE   rS   r   rT   rU   r(   rK   )r-   r4   r5   r6   r7   r[   rV   r&   r&   r'   r8      s$   



zResizeGpart.resizeNr\   r&   r&   r&   r'   rf      r]   rf   r   r*   r   r/   c                 C   s   d }| dkr#t D ]\}}||}|j|dr|} nq|s!td|S i }t D ]\}}	|	||< q'| |vr:td|  ||  |}
|
j|drH|
}|sPtd|  |S )Nr   )r   zNo resizers availablezunknown resize mode %szmode %s not available)RESIZERSr2   
ValueError	TypeError)r   r*   r   resize_class_nameresizercurmmapkvmclassr&   r&   r'   resizer_factory   s,   
rx   c              	   C   s   d }zAzt | t j}t |dt jW W |rt | S S  tyC   |dkr6t|  Y W |r5t | S S Y W |rAt | d S d S w |rLt | w w )Nr   zfs)rN   openO_RDONLYlseekSEEK_ENDcloseFileNotFoundErrorget_zfs_size)filenamer7   fdr&   r&   r'   rK      s*   rK   c              
   C   sn   |  dd }ztdddd|g\}}W n tjy0 } ztd|| W Y d }~d S d }~ww t| S )Nr   r   zpoolgetz	-HpovaluesizezFailed: zpool get size %s: %s)splitr	   rE   rU   debugintstrip)datasetr   r   _r[   r&   r&   r'   r      s   r   c                 C   s   |  dr	| d fS t| }|std|d }|d }t }|dkr?|s?tt }|d u r?tj	|r;|d fS td||fS )N/dev/z,Could not determine device of '%s' % dev_entr   rJ   z	/dev/rootz!Unable to find device '/dev/root')

startswithr   get_mount_inforn   is_containerrootdev_from_cmdlineget_cmdlinerN   rO   rQ   )deventresultdevr7   	containerr&   r&   r'   
devent2dev   s   

r   c              
   C   s   t dstd dS z
t  dd| g W n' t jy= } z|jdkr+td|  ntd|j W Y d}~dS d}~ww tt j t  dd	|g td
|  	 W d   dS 1 s^w   Y  dS )z
    Check if a device is an encrypted device. blockdev should have
    a /dev/dm-* path whereas partition is something like /dev/sda1.
    
cryptsetupz6cryptsetup not found. Assuming no encrypted partitionsFstatus   z#Determined that %s is not encryptedzZReceived unexpected exit code %s from cryptsetup status. Assuming no encrypted partitions.NisLukszDetermined that %s is encryptedT)r	   whichrU   r   rE   rS   warningr   )blockdev	partitionr[   r&   r&   r'   is_encrypted  s*   



 r   c              
   C   s   ddd| g}t  |d }|dstd| zd|dd	 d
d  W S  tyA } ztd| d| d|d }~ww )Ndmsetupdepsz--options=devnamer   z1 dependz5Expecting '1 dependencies' from 'dmsetup'. Received: r   z: (rJ   )zRan `z$`, but received unexpected stdout: ``)r	   r   RuntimeErrorr   
IndexError)r   commanddepr[   r&   r&   r'   get_underlying_partition&  s    
 r   c                 C   s  t  s	tjdfS z(t  }t|}W d   n1 sw   Y  |d }t|}|d }W n t	yB } zt
d|d}~ww zKtjdddd	| g|d
 W ztddd|t|g W n tjyx } ztd| W Y d}~nd}~ww zt   W nM t	y   ttd Y n?w ztddd|t|g W n tjy } ztd| W Y d}~nd}~ww zt   W w  t	y   ttd Y w w tjd|  dfS )zUse 'cryptsetup resize' to resize LUKS volume.

    The loaded keyfile is json formatted with 'key' and 'slot' keys.
    key is base64 encoded. Example:
    {"key":"XFmCwX2FHIQp0LBWaLEMiHIyfxt1SGm16VvUAVledlY=","slot":5}
    zNo encryption keyfile foundNkeyslotzZCould not load encryption key. This is expected if the volume has been previously resized.r   z
--key-file-r8   )dataluksKillSlotz--batch-modez<Failed to kill luks slot after resizing encrypted volume: %sz8Failed to remove keyfile after resizing encrypted volumez'Successfully resized encrypted volume '')KEYDATA_PATHrQ   r   r   rz   jsonloadbase64	b64decode	Exceptionr   r	   strrE   rU   r   unlinkr   rT   r    )r   r   fkeydatar   decoded_keyr   r[   r&   r&   r'   resize_encrypted7  s   


		
r   c                 C   s"  g }zm|  ||||\}}||kr!||tjd||f f W |S |d u s)|d u rJd}	|d ur:|d u r:d||f }	nd| }	||tj|	f W |S d}	|d ur]|d u r]d||||f }	nd|||f }	||tj|	f W |S  ty }
 z||tjd|||
f f W Y d }
~
|S d }
~
ww )Nzno change necessary (%s, %s)r   z*changed (%s, %s) size, new size is unknownz&changed (%s) size, new size is unknownzchanged (%s, %s) from %s to %szchanged (%s) from %s to %sz'failed to resize: disk=%s, ptnum=%s: %s)r8   appendr   r!   r    r(   r"   )rr   r   diskptnumr   r7   infooldnewmsgr[   r&   r&   r'   _call_resizern  sT   
&
r   rr   c                 C   sn  t  |}g }|r5|d}d }d }zt|\}}W n ty8 }	 z||tjd|	 f W Y d }	~	qd }	~	ww td| |dkrTt	| t
rT|t| |||||7 }qzt|}
W n tyz }	 z||tjd||	f f W Y d }	~	qd }	~	ww t|
jst|
js||tjd| f q||}|rz?t|}t||r|dd |D vr|d| |d| W qt||\}}||||f n||tjd	| d
f W n! ty }	 z||tjd| d|	 f W Y d }	~	nd }	~	ww qz	||\}}W n" ttfy' }	 z||tjd||	f f W Y d }	~	qd }	~	ww |t| |||||7 }|s
|S )Nr   zunable to convert to device: %szgrowpart found fs=%sry   zstat of '%s' failed: %szdevice '%s' not a block devicec                 S   s   g | ]}|d  qS )r   r&   ).0xr&   r&   r'   
<listcomp>  s    z"resize_devices.<locals>.<listcomp>zResizing mapped device (z!) skipped as it is not encrypted.zResizing encrypted device (z
) failed: zdevice_part_info(%s) failed: %s)copypopr   rn   r   r   r   rU   r   
isinstancer^   r   rN   statOSErrorS_ISBLKst_modeS_ISCHRget_mapped_devicer   r   insertr   r   r"   device_part_inforo   )rr   r   r*   r   r   r   r   r   r7   r[   statretunderlying_blockdevr   r   messager&   r&   r'   resize_devices  s   










lr   namecfgcloudargsc              
   C   s  d|vrt dt t|d< |d}t|tst d d S |dd}t|rB|dkr:t	j
d| dd	d
d t d| d S t|ddr]tjdr]t d t d d S t|ddg}t|spt d d S z
t||j|d}W n" ttfy } zt d|| |dkr|W Y d }~d S d }~ww tjt jdt|||jfd}	|	D ]\}
}}|tjkrt d|
| qt d|
|| qd S )Nr=   z.No 'growpart' entry in cfg.  Using default: %sz#'growpart' in config was not a dictr   r   offz"Growpart's 'mode' key with value 'r   z22.2zUse 'off' instead.)
deprecateddeprecated_versionextra_messagezgrowpart disabled: mode=%sr   Fz/etc/growroot-disabledz0growpart disabled: /etc/growroot-disabled existsz&use ignore_growroot_disabled to ignorer   r   zgrowpart: empty device list)r*   r   z,growpart unable to find resizer for '%s': %sr   )logfuncr   funcr   z'%s' resized: %sz'%s' %s: %s)rU   r   DEFAULT_CONFIGr   r   dictr   r   is_falser   	deprecaterN   rO   r_   get_cfg_option_listlenrx   r*   rn   ro   log_timer   r   r    r   )r   r   r   r   mycfgr   r   rr   r[   resizedentryrb   r   r&   r&   r'   handle  s`   









r   r=   ra   rg   )Bre   r   r   r   loggingrN   os.pathrC   r   abcr   r   
contextlibr   pathlibr   typingr   r   	cloudinitr   r	   r
   r   cloudinit.cloudr   cloudinit.configr   cloudinit.config.schemar   cloudinit.distrosr   r   cloudinit.settingsr   MODULE_DESCRIPTIONr   r   __annotations__r   r   r   	getLoggerr#   rU   r   r(   r)   r<   r^   rf   r   r9   rx   r   rK   r   r   r:   r   r   r   r   r   r   rm   r&   r&   r&   r'   <module>   sh   
1&
7.t8