3 \me"T @s*UdZddlZddlZddlZddlZddlZddlZddlZddlZddl m Z m Z ddl m Z ddlmZddlmZddlmZddlmZmZmZdd lmZdd lmZdd lmZmZdd lm Z m!Z!dd l"m#Z#dZ$e#Z%ddde$e ge%ededggdZ&e&ee&ZddgddZ'edZ(GdddZ)ej*e+Z,e-e!e.dddZ/Gdd d e0Z1Gd!d"d"e Z2Gd#d$d$e2Z3Gd%d&d&e2Z4Gd'd(d(e2Z5d)d*Z6d+d,Z7d-d.Z8d/d0Z9e:d1d2d3Z;d4d5Ze-eee.dd:d;d<Z?d=e3fd>e4fd?e5ffZ@dS)@zGrowpart: Grow partitionsN)ABCabstractmethod)suppress)Path)dedent)Tuple)subp temp_utilsutil)Cloud)Config) MetaSchema get_meta_doc) ALL_DISTROSDistro) PER_ALWAYSa6Growpart resizes partitions to fill the available disk space. This is useful for cloud instances with a larger amount of disk space available than the pristine image uses, as it allows the instance to automatically make use of the extra space. Note that this only works if the partition to be resized is the last one on a disk with classic partitioning scheme (MBR, BSD, GPT). LVM, Btrfs and ZFS have no such restrictions. The devices on which to run growpart are specified as a list under the ``devices`` key. There is some functionality overlap between this module and the ``growroot`` functionality of ``cloud-initramfs-tools``. However, there are some situations where one tool is able to function and the other is not. The default configuration for both should work for most cloud instances. To explicitly prevent ``cloud-initramfs-tools`` from running ``growroot``, the file ``/etc/growroot-disabled`` can be created. By default, both ``growroot`` and ``cc_growpart`` will check for the existence of this file and will not run if it is present. However, this file can be ignored for ``cc_growpart`` by setting ``ignore_growroot_disabled`` to ``true``. For more information on ``cloud-initramfs-tools`` see: https://launchpad.net/cloud-initramfs-tools On FreeBSD, there is also the ``growfs`` service, which has a lot of overlap with ``cc_growpart`` and ``cc_resizefs``, but only works on the root partition. In that configuration, we use it, otherwise, we fall back to ``gpart``. Note however, that ``growfs`` may insert a swap partition, if none is present, unless instructed not to via ``growfs_swap_size=0`` in either ``kenv(1)``, or ``rc.conf(5)``. Growpart is enabled by default on the root partition. The default config for growpart is:: growpart: mode: auto devices: ["/"] ignore_growroot_disabled: false Z cc_growpartZGrowpartzGrow partitionsz growpart: mode: auto devices: ["/"] ignore_growroot_disabled: false z growpart: mode: growpart devices: - "/" - "/dev/vdb1" ignore_growroot_disabled: true )idnametitle descriptionZdistros frequencyZexamplesZactivate_by_schema_keysauto/F)modedevicesignore_growroot_disabledz/cc_growpart_keydatac@seZdZdZdZdZdZdS)RESIZESKIPPEDCHANGEDNOCHANGEFAILEDN)__name__ __module__ __qualname__rrrr r$r$!/usr/lib/python3.6/cc_growpart.pyrosr)rdistrorc Csd}|dkrFx*tD]"\}}||}|j|dr|}PqW|stdn^i}xtD]\}} | ||<qPW||krxtd||||} | j|dr| }|std||S)Nr)rzNo resizers availablezunknown resize mode %szmode %s not available)RESIZERS available ValueError TypeError) rr&rZ resize_class_nameresizerZcurZmmapkvZmclassr$r$r%resizer_factoryys(       r/c@s eZdZdS)ResizeFailedExceptionN)r!r"r#r$r$r$r%r0sr0c@s:eZdZedddZeeedddZeddZ d S) Resizer)r&cCs ||_dS)N)_distro)selfr&r$r$r%__init__szResizer.__init__)rreturncCsdS)Nr$)r3rr$r$r%r(szResizer.availablecCsdS)Nr$)r3diskdevpartnumpartdevr$r$r%resizeszResizer.resizeN) r!r"r#rr4rlistboolr(r9r$r$r$r%r1sr1c@s"eZdZedddZddZdS)ResizeGrowPart)rc CsXtjj}d|d<y*tjddg|d\}}tjd|r:dSWntjk rRYnXdS) NCLANGgrowpartz--help)envz --update\s+TF)osenvironcopyrresearchProcessExecutionError)r3rmyenvoutZ_errr$r$r%r(s  zResizeGrowPart.availablec &Cs8tjj}d|d<t|}|jj}tj|dd}tjj |d}tjj |sZtj |d||d<yt j dd||g|d WnJt j k r} z,| jd krtjtd ||t| | ||fSd} ~ XnXyt j d||g|d Wn@t j k r } z tjtd ||t| | WYdd} ~ XnXWdQRX|t|fS) Nr=r>T)dirZ needs_exer?iZTMPDIRz --dry-run)r@z&Failed growpart --dry-run for (%s, %s)zFailed: growpart %s %s)rArBrCget_sizer2Zget_tmp_exec_pathr ZtempdirpathjoinexistsmkdirrrF exit_coder logexcLOGr0) r3r6r7r8rGbeforeZtmp_dirZtmpdZ growpart_tmper$r$r%r9s6      &zResizeGrowPart.resizeN)r!r"r#r:r(r9r$r$r$r%r<s r<c@s&eZdZdZedddZddZdS) 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``. )rcCstjjdo|dgkS)z'growfs only works on the root partitionz/etc/rc.d/growfsr)rArLisfile)r3rr$r$r%r(szResizeGrowFS.availablecCsdt|}y|jjdddWn:tjk rV}ztjtdt||WYdd}~XnX|t|fS)NZonestartgrowfs)actionZservicezFailed: service growfs onestart) rKr2Zmanage_servicerrFr rQrRr0)r3r6r7r8rSrTr$r$r%r9s zResizeGrowFS.resizeN)r!r"r#__doc__r:r(r9r$r$r$r%rUs rUc@s"eZdZedddZddZdS) ResizeGpart)rc Cs^tjj}d|d<y0tjddg|ddgd\}}tjd|r@d SWntjk rXYnXd S) Nr=r>gparthelprrJ)r@rcszgpart recover TF)rArBrCrrDrErF)r3rrGZ_outerrr$r$r%r(s  zResizeGpart.availablecCsytjdd|gWnFtjk rZ}z(|jdkrJtjtd|t||WYdd}~XnXt|}ytjddd||gWn>tjk r}z tjtd||t||WYdd}~XnX|t|fS) 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. r[ZrecoverrzFailed: gpart recover %sNr9z-izFailed: gpart resize -i %s %s)rrFrPr rQrRr0rK)r3r6r7r8rTrSr$r$r%r9s zResizeGpart.resizeN)r!r"r#r:r(r9r$r$r$r%rZs rZc Cs0tj|tj}ztj|dtjStj|XdS)Nr)rAopenO_RDONLYlseekSEEK_ENDclose)filenamefdr$r$r%rKsrKc Cstjj|}tjj|}d|}tjrVdtj|}tjd|}|rV|d|dfStjj |srt d||ftjj |d}tjj |st d|tj |j}tjj|}tjj|} tj tjj | dj} tjjd | } | |fS) Nz/sys/class/block/%sz/dev/z/^(?P/dev/.+)[sp](?P\d+[a-z]*)$devZ part_slicez%s had no syspath (%s) partitionz%s not a partitionz /dev/block/%s)rArLrealpathbasenamer Zis_BSDZfind_freebsd_partrDrErNr)rMr*Z load_filerstripdirname) ZdevpathZrpathZbnameZsyspathZfpartmZptpathptnumZrsyspathZ disksyspathZ diskmajminZ diskdevpathr$r$r%device_part_info&s(       rncCst|jdr|Stj|}|s$td|d}tj}|dkrp| rptjtj}|dkrptjj |rh|Std|S)Nz/dev/z,Could not determine device of '%s' % dev_entrz /dev/rootz!Unable to find device '/dev/root') startswithr Zget_mount_infor)Z is_containerZrootdev_from_cmdlineZ get_cmdlinerArLrN)deventresultrfZ containerr$r$r% devent2devQs   rrcCs,tjj|}|jdr(tjd|||SdS)aReturns underlying block device for a mapped device. If it is mapped, blockdev will usually take the form of /dev/mapper/some_name If blockdev is a symlink pointing to a /dev/dm-* device, return the device pointed to. Otherwise, return None. z/dev/dm-z$%s is a mapped device pointing to %sN)rArLrhrorRdebug)blockdevrhr$r$r%get_mapped_devicehs  ru)r5cCstjdstjddSytjdd|gWnHtjk rt}z*|jdkrXtjd|ntjd|jdSd}~XnXttj"tjdd |gtjd |d SQRXdS) 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 partitionsFstatusz#Determined that %s is not encryptedzZReceived unexpected exit code %s from cryptsetup status. Assuming no encrypted partitions.NZisLukszDetermined that %s is encryptedT)rZwhichrRrsrFrPwarningr)rtrgrTr$r$r% is_encryptedxs"      rzcCsddd|g}tj|d}|jds2td|yd|jdd jd dStk r}ztd |d |d |WYdd}~XnXdS)NZdmsetupZdepsz--options=devnamerz1 dependz5Expecting '1 dependencies' from 'dmsetup'. Received: z/dev/z: (rJ)zRan `z$`, but received unexpected stdout: ``)rro RuntimeErrorsplit IndexError)rtZcommandZdeprTr$r$r%get_underlying_partitions   rc0Cs,tjstjdfSyt k r}z"|j|tjd||ffwWYdd}~XnXtj |j  rtj |j  r|j|tjd|fqt |}|ryt|}t||r^|dd|Dkr>|jd||jd|wt||\} } |j|| | fn|j|tjd|dfWqtk r}z$|j|tjd |d |fWYdd}~XqXqyt|\} } WnDttfk r}z"|j|tjd ||ffwWYdd}~XnXyZ|j| | |\} }| |krP|j|tjd | | ffn|j|tjd | | | |ffWqtk r}z"|j|tjd| | |ffWYdd}~XqXqW|S)Nrzunable to convert to device: %szstat of '%s' failed: %szdevice '%s' not a block devicecSsg|] }|dqS)rr$).0xr$r$r% sz"resize_devices..zResizing mapped device (z!) skipped as it is not encrypted.zResizing encrypted device (z ) failed: zdevice_part_info(%s) failed: %szno change necessary (%s, %s)zchanged (%s, %s) from %s to %sz'failed to resize: disk=%s, ptnum=%s: %s)rCpoprrr)appendrrrAstatOSErrorS_ISBLKst_modeS_ISCHRrurrzinsertrrr rnr*r9rrr0)r,rinforprtrTZstatretZunderlying_blockdevrgrwmessageZdiskrmoldnewr$r$r%resize_devicess           & &r)rcfgcloudargsr5c Csd|krtjdtt|d<|jd}t|ts>tjddS|jdd}tj|r||dkrltj ddd d tjd |dStj|jd d rt j j drtjdtjddStj |ddg}t|stjddSyt||j|d}WnBttfk r.}z tjd|||dkr |dSd}~XnXtjtjdt||fd} x@| D]8\} } } | tjkrttjd| | ntjd| | | qNWdS)Nr?z.No 'growpart' entry in cfg. Using default: %sz#'growpart' in config was not a dictrrZoffz)Growpart's 'mode' key with value '{mode}'z22.2zUse 'off' instead.)Z deprecatedZdeprecated_versionZ extra_messagezgrowpart disabled: mode=%srFz/etc/growroot-disabledz0growpart disabled: /etc/growroot-disabled existsz&use ignore_growroot_disabled to ignorerrzgrowpart: empty device list)r&rz,growpart unable to find resizer for '%s': %sr)Zlogfuncmsgfuncrz'%s' resized: %sz '%s' %s: %s)rRrsDEFAULT_CONFIGget isinstancedictryr Zis_falseZ deprecaterArLrVZget_cfg_option_listlenr/r&r)r*Zlog_timerrrr) rrrrZmycfgrrr,rTZresizedentryrXrr$r$r%handle^sT             rr?rWr[)ArYrrCrZloggingrAZos.pathrDrabcrr contextlibrZpathlibrtextwraprZtypingrZ cloudinitrr r Zcloudinit.cloudr Zcloudinit.configr Zcloudinit.config.schemar rZcloudinit.distrosrrZcloudinit.settingsrZMODULE_DESCRIPTIONrmetarrrZ getLoggerr!rRrr:r/rr0r1r<rUrZrKrnrrrur;rzrrrrr'r$r$r$r%st       '   3'+78