i'dWc@sqdZddlZddlZddlZddlmZmZddlmZddlm Z ddl m Z ddl m Z mZddlmZdd lmZdd lmZdd lmZmZmZmZdd lmZmZmZmZdd lm Z e!a"e de j#Z$dZ%e dj&e j'Z(dZ)dZ*e dj&e j'Z+e dZ,dZ-dZ.dZ/dZ0dZ1dZ2dZ3dZ4dZ5de6fdYZ7d e7fd!YZ8d"e7fd#YZ9d$e6fd%YZ:d&e6fd'YZ;d(e6fd)YZ<d*e6fd+YZ=d,e>fd-YZ?d.efd/YZ@dS(0s6 controller.py, TargetController and related classes. iN(tcopytdeepcopy(tEFI_NUMUSERPAR(tSystemFirmware(tV_ROOT(tget_system_memorytgpt_firmware_check(tSimpleXmlHandlerBase(tINSTALL_LOGGER_NAME(tTarget(tLogicaltBEtVdevtZpool(tDiskt GPTPartitiont PartitiontSlice(tSizet4it4gbiit2gbt3gbis etc/vfstabsTargetController datatlogicaltrpooltbpooltvdevtnonetmirrort BadDiskErrorcBseZdZddZRS(s General purpose error class for when TargetController is unable to access a disk. Optionally, the disk property will be set to provide a reference to the disk that caused the exception/error. cCs#tt|j|||_dS(N(tsuperRt__init__tdisk(tselftmsgR ((s controller.pyR_sN(t__name__t __module__t__doc__tNoneR(((s controller.pyRWstBadDiskBlockSizeErrorcBseZdZRS(s Specific exception sub-class for when a disk block size is imcompatible with installation. Specifically this means UEFI64 on a non 512b block size disk at this time because of pcfs block size limitations (see PSARC 2008/769) (R#R$R%(((s controller.pyR'dstSubSizeDiskErrorcBseZdZRS(so Specific exception sub-class for when a disk is below the minimum required size for installation. (R#R$R%(((s controller.pyR(mstSwapDumpGeneralErrorcBseZdZRS(sB General exception for errors computing swap and dump values. (R#R$R%(((s controller.pyR)tstSwapDumpSpaceErrorcBseZdZRS(sF Not enough space in the target disk for successful installation. (R#R$R%(((s controller.pyR*zstNoDesiredRootZpoolErrorcBseZdZRS(sW Exception for when the root pool is not found in the desired target tree. (R#R$R%(((s controller.pyR+stBadZpoolVdevErrorcBseZdZRS(sT Exception for when a zpool does not have the expected number of vdevs. (R#R$R%(((s controller.pyR,stTargetControllercBseZdZdZdZeeedZeeededZd*de e dZ dZ ed*dZed*d Zd*ed Zeed Zeed Zeeed Zd*d*dZedZdZdZdZedZedZedZdZdZ dZ!dZ"dZ#dZ$dZ%d*d*dZ&dZ'edZ(ed Z)ed!Z*d"Z+d*d#Z,d*d$Z-d%Z.d&Z/d'Z0d(Z1d)Z2RS(+sf The TargetController (TC) class is intended to be used by all the Install client apps for the purpose of selecting the disk or disks for the install. It creates and sets up the "desired targets" area in the DataObjectCache (DOC) in the manner expected by the TargetInstantiation (TI) checkpoint, which will typically be run later. TC also calculates minimum and recommended sizes for the installation target, based on the passed-in image size and calculates sizes for swap and dump devices, if required. NB: Where TC methods accept Disk objects as parameters, these can be the actual Disk objects discovered by TargetDiscovery (TD), which the calling application will have retrieved from the "discovered targets" area of the DOC. Or, they can be other Disk objects which represent the same disks as the "discovered targets". In this case, TargetController will locate the corresponding Disks from "discovered targets" before processing them. Where TC methods return Disk objects, these will always be (possibly modified) copies of those discovered disks: TC takes in references to the "discovered targets", makes copies of those objects, places the copies in "desired targets" and returns the copies to the calling code. TC does not itself control changes to the partitioning layout of the selected disk(s) - this is done directly by the application by operating on the Disk objects returned from TC's methods. TC also maintains a backup of the previously selected disk(s), so that if the user makes changes to the partitioning layout of the selected disk(s); then changes the selected disk(s); and then changes back to the original disk(s), the previous partitioning layout that they configured will not be lost. APIs: from solaris_install.target.controller import TargetController, BadDiskError, SwapDumpGeneralError, SwapDumpSpaceError target_controller = TargetController(doc, debug=False) disks = target_controller.initialize( image_size=FALLBACK_IMAGE_SIZE, no_initial_disk=False, no_initial_logical=False, install_mountpoint="/a", unique_zpool_name=False) logical = target_controller.apply_default_logical( logical=None, mountpoint="/a", redundancy="none", unique_zpool_name=True) disk = target_controller.select_initial_disk() disks = target_controller.select_disk( disks, use_whole_disk=False) disks = target_controller.add_disk( disks, use_whole_disk=False) disks = target_controller.reset_layout( disk=None, use_whole_disk=False) target_controller.apply_default_layout( disk, use_whole_disk, wipe_disk=False, in_zpool=DEFAULT_ZPOOL_NAME, in_vdev=DEFAULT_VDEV_NAME) swap_type, swap_size, dump_type, dump_size = target_controller.calc_swap_dump_size( installation_size, available_size, swap_included=False) target_controller.setup_vfstab_for_swap( pool_name, basedir) min_size = target_controller.minimum_target_size rec_size = target_controller.recommended_target_size type = TargetController.SWAP_DUMP_ZVOL type = TargetController.SWAP_DUMP_NONE tZVOLR&cCstjta||_||_||_||_d|_ d|_ d|_ d|_ d|_ t|_d|_d|_d|_d|_d|_d|_t|_tj|_d|_tj|_d|_d|_tjj |_!t"|_#t$j%|_&t't|j(dS(s Initializer method. Called by the constructor. Parameters: doc. A reference to a DataObjectCache instance where TD places details of the "discovered targets" and where TC will create the "desired targets" area. debug=False. If True, XML is generated for the backup area and will get written to logs. dry_run=False. If True, certain actions will be prevented from running so the disks are not modified require_boot_pool=False. If True, TC will require the configuration of a "boot pool" regardless of whether the configuration of the "root pool" actually requires it. (This flag exists for testing purposes; it allows TC to be instantiated by the caller with this condition hardwired so that a "boot pool" can be configured.) Returns: Nothing Raises: Nothing N()tloggingt getLoggerRtLOGGERt_doct_debugt_dry_runt_require_boot_poolR&t_discovered_roott_discovered_diskst_dedicated_bpool_diskst _desired_roott _backup_areatFalset _need_backupt_logicalt_bet _image_sizet _mem_sizet_minimum_target_sizet_recommended_target_sizet_swap_dump_computedR-tSWAP_DUMP_NONEt _swap_typet _swap_sizet _dump_typet _dump_sizet_minimum_bpool_target_sizeRtgettfw_namet _firmwareRt _gpt_boottplatformt processort_this_processorRR(R!tdoctdebugtdry_runtrequire_boot_pool((s controller.pyRs6                       s/ac Cs|jjjdtj|_|jd kr<tdn||_y!t dt t j |_ Wn$t k rtjdtnXd |_d |_|jjjdtjdtttj|_|jjj|j|rtS|jd|d||_|jj|j|r.tS|j}|j}|d kratdn|jdtd t}| st |d krt!d n|d } |j"|gt|j#| j#} t$|_%| S(s- Creates the top-level structure for the "desired targets" area in the DOC and, optionally, selects an initial disk to be the target for this install. The TargetDiscovery checkpoint must have run successfully, saving details of the system's storage devices in the "discovered targets" area of the DOC, before initialize() is called, or an error is raised. Parameters: image_size=FALLBACK_IMAGE_SIZE. If specified, must be a Size object. TC will use this value to compute the minimum disk size that can be selected. no_initial_logical=False. If set to True, then initialize will not set up a default rpool structure. Will also ensure no default disk is selected. no_initial_disk=False. If set to True, then initialize will not select an initial disk. This may be useful for non- interactive installers, where they know before calling TC which disk(s) they will be installing on. install_mountpoint="/a". The mountpoint attribute of the created BE will be set to this value. unique_zpool_name=False If set to True, will ensure that the name of the pool will not match any existing zpools on the system. Returns: A list of the initially selected Disks. Raises: BadDiskError BadZpoolVdevError NoDesiredRootZpoolError tnamesNo discovered targets availables%ds+Unable to determine amount of system memoryt class_typet mountpointtunique_zpool_names&Root pool not found in DESIRED target.tnot_found_is_errisFTargetController can only add disks for a zpool with exactly one vdev.iN(&R2t persistenttget_first_childR t DISCOVEREDR6R&RR?RRtmb_unitsR@tOSErrorR1terrorR)RARBtdelete_childrentDESIREDR9tinsert_childrentlisttapply_default_logicalR=tselect_initial_diskt_get_desired_root_zpoolR+t get_childrenR R;tlenR,t _add_disksRUtTrueR<( R!t image_sizetno_initial_logicaltno_initial_disktinstall_mountpointRXt initial_diskRtvdevsRt return_disks((s controller.pyt initialize$sH& !             cCs|dkrtt}n|jtdt}|rq|jrq|j||_t j dt|j fqqn|j t |t|_||j_|j|j|S(s/ Create a default logical layout that includes the root pool. Only create logical element if not already done so, if not none it is assumed this logical contains no children. Optionally, ensure root pool name is unique and does not exist already. tis_roots3Default root zpool '%s' exists, Using '%s' instead.N(R&R tDEFAULT_LOGICAL_NAMEt add_zpooltDEFAULT_ZPOOL_NAMERjtexistst_get_unique_pool_namet_nameR1twarningRUtadd_vdevtDEFAULT_VDEV_NAMER R>RWRb(R!RRWt redundancyRXR((s controller.pyRds     cCs|jdkrdS|jjdt}|dkr8dSx'|jdtD]}|jrK|SqKW|jtdt }|j r|j ||_ t jdt|jfn|jtt|S(s Adds a default boot pool to the the DESIRED tree. Assumes that the logical element in the DESIRED root already exists. Returns a handle to the boot pool Zpool object. RVtis_boots7Default boot pool name '%s' exists, Using '%s' instead.N(R9R&R[R RgR R~RutDEFAULT_BPOOL_NAMERjRwRxRyR1RzRUR{R|tVDEV_REDUNDANCY_NONE(R!RtzpoolR((s controller.pytapply_default_boot_pools    c Cs|jd kr"|jdtnt|tr=|g}n|j|}|d kr|j}|d krtdqn|j |||j r|j }|jj dtdt n<|j |j}x'|D]}|jj d|dtqW|jr|j|n|jdtdt }| sIt|dkrXtdn|d}t|dkrt|_n t|_|j|||j|j}|t krt|_n t |_|S( sK Select one or more disks to be used for the install of the root pool or the boot pool. If any disk(s) were previously selected for that pool, they will be replaced with the newly selected disks. Parameters: disks, either a single Disk object or a list of Disks use_whole_disk=False, specifies whether the selected disk(s) are to be used as entire disks or whether they will be divided into partitions zpool=None, specifies the zpool these disks are being added for. If None, disks are added to the root pool. Returns: A list containing the selected disk(s). Raises: BadDiskError BadZpoolVdevError NoDesiredRootZPoolError Rms&Root pool not found in DESIRED target.RVRYtchildrenisFTargetController can only add disks for a zpool with exactly one vdev.iN(R9R&RrRjt isinstanceRt#_get_corresponding_discovered_disksRfR+t_check_disks_are_suitableRst_get_desired_disksR`R;RUR<t _backup_disksRgR RhR,tVDEV_REDUNDANCY_MIRRORR}RRi( R!tdiskstuse_whole_diskRtprevious_disksR RpRRq((s controller.pyt select_disksB                 c Cs|jd kr"|jdtnt|tr=|g}n|j|}|d kr|j}|d krtdqn|j |||j }|d k rxF|D];}x2|D]*}|j |rt dd|qqWqWn|j dtdt}| s$t|dkr3tdn|d }|j|||j|j} t|j |jdkrt|_n t|_| S( s Add one or more disks to the currently selected disks for the given zpool pool. Parameters: disks, either a single Disk object or a list of Disks. use_whole_disk=False, specifies whether the selected disk(s) are to be used as entire disks or whether they will be divided into partitions zpool, the zpool this disk is being added for. If None, disk is added to the root pool. Returns: A list containing the added disk(s). Raises: BadDiskError BadZpoolVdevError NoDesiredRootZpoolError Rms&Root pool not found in DESIRED target.sAttempt to add same disk twice!R RVRYisFTargetController can only add disks for a zpool with exactly one vdev.iN(R9R&RrRjRRRRfR+RRt name_matchesRRgR R;RhR,RiRURR}R( R!RRRt current_diskst current_disktnew_diskRpRRq((s controller.pytadd_disk-s6             c Cs|jdkrtdn|j}t}|dkrxQ|D]9}x0|jD]%}|j|rV|j|qVqVWqFWn |j|t}x|D]}x9|jD]}|j|jkrPqqWtdd|xL|D]2}|j|r|jj d|dt PqqWtdd||dk rt |} |j | |t |j|j|j| ||j|jr|jj| d|jn|j| qqW|S( sb Resets the partitioning layout of either all currently selected disks, or a specific disk, back to their original layout from "discovered targets". However for disks that have no usable partitions and/or slices the call to apply_default_layout will also reset the suggested layout before returning. Parameters: disk=None. If not given, or None, then all the currently selected disks are reset. Otherwise disk must be a single Disk object. use_whole_disk=False, specifies whether the selected disk(s) are to be used as entire disks or whether they will be divided into partitions Returns: A list containing the reset Disks. Raises: BadDiskError sNo selected disks to reset!s1Trying to reset a disk not in discovered targets!R RRYs#Trying to reset an unselected disk!tbeforeN(R9R&RRRctdiscovered_disksRtappendtctdR`RjRtapply_default_layoutR;tin_zpooltin_vdevt _fixup_diskRbR=( R!R RRRRtdiscovered_diskRqt disc_diskt copy_disk((s controller.pyt reset_layoutssF               c Cs|jdkrtdn|jdt}|s |jj}d}d }|jdkr|jd}|d7}|j |j j}n|j d} | j |} |j t ||| tjdtjdd t}||_||_|jjd d nd S( s Attempt to apply the default GPT layout to a disk. Only apply this to disks if we are not using whole disk. tGPTsOA default GPT layout can not be applied to a disk that doesn't have a GPT labelRViti386itpartition_typetsolaristforcetkeycSs t|jS(N(tintRU(tgpe((s controller.pytsN(tlabelt RuntimeErrortget_descendantsRtgpt_primary_table_sizetsectorsR&RPtadd_gpt_systemt start_sectortsizetadd_gpt_reservetadd_gptpartitiontstrRt sector_unitst alias_to_guidRjRRt _childrentsort( R!R RRt partitionststarttindext sol_partitiontboot_partitiontresv_partitiont sol_sectors((s controller.pyt_apply_default_gpt_layouts*      c Cst}|jdkr$tdn|jsx|jD]}|j|jkr7|jdkrx>tttgD]-}|j d|}|rntdqnqnW|j |jt }qq7q7Wn|j dt}|j dt} |j dkr|jj} nd} |jjj| } | rr| rr|j dkry|jd| | tjdd d tj} | jd t } n!|jd | | tjd t } |rxn|jD]c}|j|jkr|jj|d ||jjd|dt d|_|j|gqqWx[|jD]J}|j|jkr|j j|d ||j jd|dt qqWqnOt!}|j"}|dk r|dk r||j#kr|j$}n |j%}x@| D]8}|j#dkr|j&|kr|j'|PqqqW|j dkrS|rdS|jdt|jd | | tjd t } nnxk|D]_}|j(rZ|jdkrZd|_)|j*rtj|_+n|rdS|jd t } PqZqZWdSt,| _-|| _.|| _/dS(s Attempt to apply the default VTOC layout to a disk. Only apply this to disks if we are not using whole disk. tVTOCsQA default VTOC layout can not be applied to a disk that doesn't have a VTOC labelRVs$Non-empty disks can not be relabeledRit1RitbootidRt0RRRYt2tsparcNtcreate(0R;RRR4RRRRRRgt_set_vtoc_label_and_geometryRjRRPtgeometrytcylsizet disk_proptdev_sizeRt add_partitionRRtACTIVEtcreate_entire_partition_slicet add_sliceR6RbR`R&R7RRR9Rctget_desired_boot_zpoolRUtminimum_bpool_target_sizetminimum_target_sizeRRt is_solaristactiont is_primaryRRttagRR(R!R RRt relabeledRtobjRRtslicesRt slice_sizet new_partitiont new_slicet desired_diskt slice_listRtmin_sizetslct partition((s controller.pyt_apply_default_vtoc_layouts                       cCs$|s |rx'tttgD]}|jd|qW|jrKd|_n d|_t|_|r|jdkrt|_||_ ||_ ndSnQ|jsxEttgD]+}|j d|}|rd|_PqqWd|_n|jdkr |j |||n|j |||dS(s Attempt to apply the default layout to a disk. Only apply to disks if we are not using whole disk. If wipe disk specified then delete all existing partition and slice information from the disk supplied. NOTE: When use_whole_disk is True this method does not perform ANY necessary steps to ensure the disk is bootable. The way to ensure that condition is to invoke select_disk() on a disk with use_whole_disk=True which eventually calls _fixup_disk(): select_disk(uwd=True)->_add_disks(uwd=True)->_fixup_disk(uwd=True) _fixup_disk() handles the whole disk layout correctly based on the GPT capabilities of the system. RVRRN(RRRR`RMRRjt write_cachet whole_diskRRRgRR(R!R Rt wipe_diskRRRR((s controller.pyRs.           cCs|dkr6|j}|dkr6tdq6n|dkrNt}n|}t}x[|jD]P}|jr|j rqgnx.|D]}|j|rPqqW|j|qgWxc|D][}|j sqn|j |sPn|j ||sPn|jr|j sPqn|SWxu|D]a}|j r@q(n|j |sUq(n|j ||smq(n|jr|j sq(qn|SWt ddS(s Iterate through the disks discovered by TD and select one of them to be the initial install target for the given zpool. If no zpool is given, we assume we're selecting a disk for the root zpool. Any disks dedicated to the boot pool will only be selectable for the boot pool. Parameters: - unavailable_disks: A list of disks that should not be selected under any circumstance (presumably because they are reserved for use by another zpool. - zpool: The zpool this disk is being selected for. If value is None, disk is selected to the root pool. Returns: The selected Disk object Raises: SubSizeDiskError, BadDiskError s&Root pool not found in DESIRED target.s5None of the available disks are suitable for install!N(R&RfR+RcRt isdedicatedR~RRt is_boot_diskt_is_block_size_compatiblet_is_big_enoughtis_firmware_bootableR(R!tunavailable_disksRt avoid_diskstavailable_disksR t avoid_disk((s controller.pyResN                c Cs|jr%|j|j|j|jfS||krZtjd|tjd|tn|jdt j }|jdt j }|j }d}t }|dkrt }ntjd|tjd|tjd|j||r[|r|} n?||} || kr;tjd| tjd|tn|jt|d | }nQt|d |} | dkr|j| ttt}|j| |}nt t|t j |_|dkrtj|_nt t|t j |_|dkrtj|_ntjd |jtjd |jtjd |jtjd |jt |_|j|j|j|jfS(s Calculate swap/dump, based on the amount of system memory, installation size and available size. The following rules are used for determining the type of swap to be created, whether swap zvol is required and the size of swap to be created. memory type required size -------------------------------------------------- <3G zvol yes 1G (MIN_SWAP_SIZE) 3G-4G zvol no 1G 4G-16G zvol no 2G 16G-128G zvol no 4G >128G zvol no 4G (MAX_SWAP_SIZE) The following rules are used for calculating the amount of required space for dump. memory type size -------------------------------------------------- <0.5G zvol 256MB (MIN_DUMP_SIZE) >0.5G zvol 1/2 of memory If slice/zvol is required, and there's not enough space in the, target, an error will be raised. If swap zvol is not required, spaces in the root pool will first be allocated for installation data, any left over space up to 80% of the total root pool size, will be allocated for swap/dump. This is to make sure that the root pool is less than 80% full. Size of all calculation is done in MB Parameters: - installation_size: Size object. The size required for the installation - available_size: Size object. The available size on the target disk. - swap_included=False: Boolean. Indicates whether required swap space is already included and validated in the installation size. Returns: Tuple consisting of: swap_type, swap_size, dump_type, dump_size whose types are: string, Size object, string, Size object Raise: SwapDumpSpaceError s#Space required for installation: %ssTotal available space: %stunitsisInstallation size: %ssAvailable size: %ssMemory: %s. Swap Required: %ss6Space required for installation with required swap: %sg?s Swap Type: %ss Swap Size: %ss Dump Type: %ss Dump Size: %s(RCRERFRGRHR1R_R*RJRR]t_get_required_swap_sizeR;RjRRR@t_calc_dump_sizeRt_calc_swap_sizet MIN_SWAP_SIZEt MIN_DUMP_SIZERR-tSWAP_DUMP_ZVOL( R!tinstallation_sizetavailable_sizet swap_includedtinstallation_size_mbtavailable_size_mbt swap_size_mbt dump_size_mbt swap_requiredtrequired_size_mbt free_space_mb((s controller.pytcalc_swap_dump_size$sd5                   c Cs|j|}|dkrdStjj|t}yBt|d-}|jd|ddddddfWdQXWn6tk r}t j d|t j |t nXdS(s,Add the swap device to /etc/vfstab. Nsa+s%s %s %s %s %s %s %s t-tswaptnosFailed to write to %s( t_get_swap_deviceR&tostpathtjoint VFSTAB_FILEtopentwritetIOErrorR1R_t exceptionR)(R!t pool_nametbasedirt swap_devicetfnametvftioe((s controller.pytsetup_vfstab_for_swaps  ' cCsZ|jr tS|j}|dkr)tSx*|jd|jD]}|js?tSq?WtS(s Returns True if the root pool has been configured in such a way that requires the configuration of a separate boot pool. Return False otherwise. RN(R5RjRfR&R;RRUR(R!RR ((s controller.pytcheck_rpool_requires_bpools    cCsf|jdkrdS|jjdt}|dkr8dSx'|jdtD]}|jrK|SqKWdS(s Return the boot pool Zpool object from the desired target tree. May return None if boot pool Zpool object does not exist. RVN(R9R&R[R RgR R~(R!RR((s controller.pyRs  cCsa|jdkrZ|j}|jjdtjt|}tt|tj|_n|jS(s The minimum amount of space required for an installation. This takes into account MIN_SWAP_SIZE required for low-memory system. Returns: Size object RN( RAR&RR?RJRR]tOVERHEADR(R!Rt min_size_mb((s controller.pyRs   cCsQ|jdkrJ|jjdtjt}tt|tj|_n|jS(s The recommended size to perform an installation. This takes into account estimated space to perform an upgrade. Returns: Size object RN(RBR&RRJRR]tFUTURE_UPGRADE_SPACER(R!t rec_size_mb((s controller.pytrecommended_target_sizes  cCs5|jdkr.ttttj|_n|jS(s The minimum size required for a boot pool disk for an installation Hard coded as 512 MB Returns: Size object N(RIR&RRtMIN_BPOOL_TARGET_SIZER](R!((s controller.pyR s cCs_t|}d}|jt||_x-|jrW|d7}|jt||_q+W|jS(s Get the next available pool name that does not exist, via appending ascending numbers to end of the zpool.name. i(RRURRyRw(R!Rtztmptzcount((s controller.pyRxs   c Cs$d}|s|j|}nt}|dk r|jjd|dtx|jD]C}|j||||rV|jj |d|j |j}qVqVWnx}|D]u}t |}|j ||t d|d||j||||sqn|jj |d|j |j|qW|S(s Convenience method called from select_disk(), add_disk() and initialize() to add a disk to the desired tree. RRYRRRN(R&t_fetch_from_backupRcR:R`RjRRR9RbR=RRR;R( R!RRRRtbackupRqR R((s controller.pyRi)s0         cCst}x|D]~}t}xZ|jD]O}||krOt}|j|Pn|j|r&t}|j|Pq&q&W|stdqqW|S(s Given a list of Disk object, return the corresponding Disks from "desired targets" which represent those same disks. disks - A list of disks to find a match for in the known discovered disk s)No equivalent Disk in discovered targets!(RcR;RRjRRR(R!RRqR tfoundR((s controller.pyRLs     cCsx|D]}||jkr1tdd|n|j||sXtdd|n|jr|j rtdd|n|j|std|jj d|qqWdS(s} Convenience method that checks that the passed-in disks are all from the "discovered targets" list created by TD and that they are all large enough for installing Solaris. If either check fails for any disk an exception is raised. Returns: nothing Raises: BadDiskBlockSizeError, SubSizeDiskError, BadDiskError s"Disk is not in discovered targets!R s"Disk is not big enough for installsDisk is not firmware bootables%Disk block size is not compatible: %dN( RRRR(R~RRR'Rt blocksize(R!RRR ((s controller.pyRfs       cCs|dkrdS|jdkrGtt|_|jjj|jn|jj}|dk rx<|D]1}|j|ri|jj d|dt qiqiWnt d}|j||jj|dS(s Make a backup of a set of disks. If the backup area does not already exist, create it. If there is already an older entry in the backup area for the exact combination of disks being backed up, then delete that backup entry. Finally, construct a new backup entry for the passed-in disks and save it in the backup area. Returns: nothing. NRRYR( R&R:R tDATA_AREA_NAMER2RZRbRgtcontains_same_disksR`RjtTargetControllerBackupEntry(R!Rtbackup_entriest backup_entrytnew_backup_entry((s controller.pyRs     cCsZ|jdkrdS|jj}|dkr2dSx!|D]}|j|r9|Sq9WdS(s Retrieve a list of Disks from the backup area that matches the passed-in disks, if such a backup exists. Iterates through the backup area, comparing the Disks in each backup entry with the list of Disks passed in. If a match is found, then return the corresponding list of Disks from the backup area. The Disks passed in are typically objects taken directly from the "discovered targets" area, while the returned Disks are copies of those objects containing the most recent layout modifications which the user has made for those disks. Returns a TargetControllerBackupEntry object if a match is found. Otherwise, returns None. N(R:R&RgR!(R!RR#R$((s controller.pyRs  cCs|s |jdt}|sR|d k r6||_n|d k rN||_ntSt|dkrC|djdkrd}|jj j |}|dj }|j d||t jdt} t| _t| _|d k r|| _n|d k r|| _ntS|d k r ||d_n|d k r<||d_ntSq|j} |d k r| d k r|| jkr|j} n |j} xx|D]p} | jdkrt| jdkr| j| kr|d k r|| _n|d k r|| _ntSqqWtSn|j|t|_|jdddt}|r|d} |d k rc|| _n|d k r{|| _nt| _tStSd S( s Prepare the passed-in Disk object for placement in the "desired target" area. NOTE: if use_whole_disk is True then it is presumed that the caller "_fixup_disk()" has determined that the system needs to use VTOC for the whole disk layout rather than GPT due to lack of firmware GPT boot capability. Returns: True if disk or slice successfully setup; False if not. RViiRRRiRUN(RRR&RRRjRhRURRRtparentRRRRRRRRRRRR;RR(R!R RRRRRRR&RRRt nextslicet slice_zero((s controller.pyt_fixup_vtoc_disksl                  $             c Cs|s^|jdt}|sR|dk r6||_n|dk rN||_ntSd}|j}|dk r|dk r||jkr|j}n |j }x|D]m} t | jt kr| j r| j|kr| j|krPq|dkr| j |kr| }qqqW|dk rW|dk r<||_n|dk rW||_qWntSngx'tttgD]} |jd| qnWt|_|dk r||_n|dk r||_ntSdS(sT Prepare the passed-in Disk object for placement in the "desired targets" area. NOTE: if use_whole_disk is True then it is presumed that the caller "_fixup_disk()" has determined that the system supports GPT boot NOTE: even though this method always returns True, it does so for seamless equivalance with _fixup_disk() and fixup_vtoc_disk() _fixup_vtoc_disk() returns False if certain unique VTOC conditions are not satsified, which don't apply here. Returns: True RVN(RRR&RRRjRRURRRRRRRRR`R( R!R RRRRtfallback_partitionRRt nextpartitionR((s controller.pyt_fixup_gpt_disk2sJ                   cCsf|r'|jrd|_q'd|_n|jdkrL|j||||S|j||||SdS(sd Prepare the passed-in Disk object for placement in the "desired targets" area. RRN(RMRR,R)(R!R RRR((s controller.pyR~s    cCs#|j}|dkrtS|S(sJ Returns a list of disks currently in the "desired targets" area. N(RR&Rc(R!R((s controller.pyt desired_diskss  cCsD|jdkr=|jdkr"dS|jjdt|_n|jS(s. Returns a list of the disks discovered by TD.RVN(R7R&R6RgR(R!((s controller.pyRs  cCsz|jdkrs|jdkr"dSg|_xE|jD]7}|jr5|jj|jkr5|jj|q5q5Wn|jS(s Returns a list of disks discovered by TD that are dedicated and big enough for being used for the boot pool. N(R8R&RRRRRR(R!R ((s controller.pytdedicated_bpool_diskss  cCsf|jdkrdS|jjdt}|dkr8dSx'|jdtD]}|jrK|SqKWdS(s Returns the root pool Zpool object from the desired target tree. May return None if root pool Zpool object does not yet exist. RVN(R9R&R[R RgR Rs(R!RR((s controller.pyRfs  cCs|jdkrdS|jjdt}|dkr8|St}x|D]}|j|krm|j|qHxu|jD]j}|j|kr|j|nt|t rwx3|jD]%}|j|kr|j|qqWqwqwWqHW|S(s Returns the list of disks currently in desired targets. If 'in_zpool' given, only returns the desired disks that are assigned to that pool or have children assigned to that pool. RVN( R9R&RgRRcRRRRR(R!RRRqR tchildR((s controller.pyRs"   cCsr|jdk rn|jjdk rn|dk rR|jrR|jj|jkrktSqn|jj|jkrntSntS(s7 Returns True if the passed in Disk is big enough to install the root pool, or the boot pool; otherwise returns False. Parameters: zpool is a Zpool object, it specifies the zpool that the disk is being added for. If None, disk is added to the root pool. N(RR&RR~RRjRR;(R!R R((s controller.pyRs !cCs)|jdkr%|jjdkr%tStS(s Returns True if the passed in Disk has a compatible block size to install Solaris; otherwise returns False. tuefi64i(RLRRR;Rj(R!R ((s controller.pyRs cCs|dkrdS|jtdkr@tdjtj}nH|jtdkrptdjtj}ntdjtj}||kr|}nt|S(s Calculates size of swap based on amount of physical memory. The following rule is applied: memory swap size -------------------------------------------------- <4G 1G 4G-16G 2G >16G 4G Parameters: - available_space: Space that can be dedicated to swap (MB) Returns: size of swap in MB iRt1gbt16gbR(R@RRJR]R(R!tavailable_spaceR((s controller.pyRs   cCsf|dkrdS|jtdkr.t}n|jjtjd}||kr\|}nt|S(s] Calculates size of dump based on amount of physical memory. The following rule is applied: memory dump size -------------------------------------------------- <0.5G 256mb (MIN_DUMP_SIZE) >0.5G 1/2 of memory If calculated space is more than the available space, the dump size will be trimmed down to the available space. Parameters: - available_space: Space that can be dedicated to dump (MB) Returns: size of dump in MB is0.5gbi(R@RRRJR]R(R!R3R((s controller.pyR4s    cCs|jtkrtSdS(sE Determines whether swap is required. If so, the amount of space used for swap is returned. If swap is not required, 0 will be returned. Value returned is in MB. If system memory is less than 3GB, swap is required. Minimum required space for swap is MIN_SWAP_SIZE. i(R@t ZVOL_REQ_MEMR(R!((s controller.pyRUs cCs"|jtjkrd|dSdS(s9 Return the string representing the device used for swap s/dev/zvol/dsk/s/swapN(RER-RR&(R!R ((s controller.pyRcs N(3R#R$R%RRDR;RtFALLBACK_IMAGE_SIZERrR&RRjRdRRRRRvR|RRRReRRRRtpropertyRRRRxRiRRRRR)R,RR-RR.RfRRRRRRR(((s controller.pyR-sfU 8o  [FO 3  >\      #  ! $ g K       " ! R"cBs5eZdZdZdZedZdZRS(s This class is only used within the TargetController class. Class for storing backups of previously selected disks, so that they can be retrieved, with any layout changes that the user has made, if those disks are selected again. Each backup entry consists of a top-level object plus a unique conbination of one or more disk objects, which are stored as its children. Only a unique conbination of selected disks can be restored: eg if the user had previously selected a disk on its own and is now selecting that disk as part of a tuple of disks, the backup will not be retrieved. This is a sub-class of SimpleXmlHandlerBase, meaning it can be stored in the DOC. R$cCstt|j|dS(s Initializer method.N(RR"R(R!RU((s controller.pyRscCs|jdtS(s> Returns all the children objects, which will always be disks.RY(RgRj(R!((s controller.pyRscCss|j}t|t|kr%tSxG|D]?}t}x&|D]}|j|r?t}q?q?W|s,tSq,WtS(s Returns True if this objects's children represent the same list of Disks as the list of Disks passed in. Otherwise, returns False. Disks being the "same" means the current object and the passed-in list contain the same number of Disks and each of the passed-in Disks has the same identifier as one of the current object's Disks. (RRhR;RRj(R!Rtbackup_entry_disksR Rt backup_disk((s controller.pyR!s    (R#R$R%tTAG_NAMERR6RR!(((s controller.pyR"ks  (AR%R/RRNRRt libefi.constRtbootmgmt.bootinfoRt libadm.constRtsolaris_installRRt"solaris_install.data_object.simpleRtsolaris_install.loggerRtsolaris_install.targetR tsolaris_install.target.logicalR R R R tsolaris_install.target.physicalRRRRtsolaris_install.target.sizeRR&R1tgb_unitsR5RRJR]t MAX_SWAP_SIZERRRR4RRR RtRvRR|RRt ExceptionRR'R(R)R*R+R,tobjectR-R"(((s controller.pyts\   ""