o Hcf@s"ddlZddlZddlZddlZddlmZddlmZmZmZm Z m Z m Z m Z m Z ddlmZmZmZmZmZmZmZmZmZmZmZddlmZddlmZmZmZm Z m!Z!m"Z"m#Z#m$Z$ddl%m&Z&ddl'm(Z(m)Z)dd l*m+Z+e,Z-e.e/e0Z1Gd d d Z2Gd d d ej3dZ4dS)N)datetime)AnyDictListOptionalSetTupleTypeUnion) apiaptconfigcontract event_logger exceptionshttpmessagessnapsystemutil) _is_attached)ApplicabilityStatusApplicationStatusCanDisableFailureCanDisableFailureReasonCanEnableFailureCanEnableFailureReasonContractStatusUserFacingStatus)status_cache_file)MessagingOperationsDictStaticAffordance)is_config_value_truec@s$eZdZdeddejfddZdS)EntitlementWithMessage entitlement UAEntitlement named_msgcCs||_||_dSN)r$r&)selfr$r&r)d?Z=de;fd@dAZ>de;fdBdCZ?deee e@ffdDdEZAdFeBjCdeeeDde@fffdGdHZEdFeBjCdefdIdJZFdefdKdLZGdFeBjCdefdMdNZHdefdOdPZIejdFeBjCdefdQdRZJdee'fdSdTZKdefdUdVZLdFeBjCdeee e!j"ffdWdXZMdYdZZNd[d\ZOdFeBjCdeee e!j"ffd]d^ZP dd_edeee eQffd`daZRdFeBjCdeee eQffdbdcZSejdFeBjCdefdddeZTdefdfdgZUdee*dfdhdiZVdFeBjCdeee e!j"ffdjdkZWdefdldmZX ddnedoeddfdpdqZYdeeZe e!j"ffdrdsZ[de\fdtduZ]dee^e e!j"ffdvdwZ_ejdee`e e!j"ffdxdyZadeee e!j"ffdzd{Zbde efd|d}Zcdefd~dZdde`fddZedeeefde edefddZf ddeeefdeeefdedefddZgdFeBjCfddZhdS)r%NFr)TreturncCdS)z?The lowercase name of this entitlement, in case it is a variantr0r)r(r)r)r* variant_nameYzUAEntitlement.variant_namecCs$|jg}|j|jkr||j|S)z1The list of names this entitlement may be called.)namepresentation_nameappend)r( valid_namesr)r)r*r9^s  zUAEntitlement.valid_namescCr2)z,The human readable title of this entitlementNr)r3r)r)r*titlefzUAEntitlement.titlecCr2)z&A sentence describing this entitlementNr)r3r)r)r* descriptionlr;zUAEntitlement.descriptioncCs<|jr|jS|jjjr|jdidid|jS|jS)z/The user-facing name shown for this entitlementr$ affordances presentedAs) is_variantr4cfgmachine_token_file is_presententitlement_cfggetr6r3r)r)r*r7rs   zUAEntitlement.presentation_nameplatform_checkcCr2)zVerify specific platform checks for a service. This should only be used if the service requires custom platform checks to check if it is available or not in the machine. TNr))r(rEr)r)r*verify_platform_checkssz$UAEntitlement.verify_platform_checkscCsF|j}|jr!dd|jD}ddtjdg|}||7}|S)z$Help information for the entitlementcSsg|] \}}d||jqS)z * {}: {})formatr<).0r4 variant_clsr)r)r* s z+UAEntitlement.help_info.. z ) help_textvariantsitemsjoinrCLI_HELP_VARIANTS_HEADER)r(rM variant_items variant_textr)r)r* help_infoszUAEntitlement.help_info.cCr2)Nr)r)r3r)r)r*static_affordancesz UAEntitlement.static_affordancescC|jS)a  Return a list of packages that aren't compatible with the entitlement. When we are enabling the entitlement we can directly ask the user if those entitlements can be disabled before proceding. Overridden in livepatch and fips )_incompatible_servicesr3r)r)r*incompatible_servicesz#UAEntitlement.incompatible_servicescCrW)a Return a list of packages that must be active before enabling this service. When we are enabling the entitlement we can directly ask the user if those entitlements can be enabled before proceding. Overridden in ros and ros-updates. )_required_servicesr3r)r)r*required_servicesrZzUAEntitlement.required_servicescCrW)a Return a list of packages that depend on this service. We will use that list during disable operations, where a disable operation will also disable all of the services required by the original service Overriden in esm-apps and esm-infra )_dependent_servicesr3r)r)r*dependent_servicess z UAEntitlement.dependent_servicescCiSr'r)r3r)r)r* _get_variantsszUAEntitlement._get_variantscCsPt}|}|didg}|D]}|did}|r%||q|S)zV Fetch all available variants defined in the Contract Server response r$ overridesselectorvariant)set_base_entitlement_cfgrDadd)r(valid_variantsrCraoverridercr)r)r*_get_contract_variantss z$UAEntitlement._get_contract_variantscCs`|}|}d|vrd|di}ni}t|D] }||vr%||||<qt|dkr.|SiS)Ngeneric)r`risortedlen)r(service_variantscontract_variantsrgrcr)r)r*_get_valid_variantss  z!UAEntitlement._get_valid_variantscCs|jriS|S)zf Return a list of services that are considered a variant of the main service. )r?rpr3r)r)r*rNszUAEntitlement.variantscs$jsiSfddDS)zw On a variant, return the other variants of the main service. On a non-variant, returns empty. cs i|] \}}|jkr||qSr))r4)rIr6clsr3r)r* s  z0UAEntitlement.other_variants..)r?rprOr3r)r3r*other_variantss   zUAEntitlement.other_variantscCsZ|jD]%}|jdkr q||j|j|j|j|jd}|\}}|t j kr*|SqdS)z On an enabled service class, return the variant that is enabled. Return None if no variants exist or none are enabled (e.g. access-only) rj)r@ assume_yes allow_beta called_name access_onlyN) rNvaluesr4r@rtru _called_namerwapplication_statusrENABLED)r(rJrcstatus_r)r)r*enabled_variants   zUAEntitlement.enabled_variantcCr_r'r)r3r)r)r* messagingrVzUAEntitlement.messagingr@rtrurvrwpurge extra_argscCsV|st}||_||_||_||_||_|dur||_ng|_||_d|_ d|_ dS)z]Setup UAEntitlement instance @param config: Parsed configuration dictionary NF) r UAConfigr@rtrurwrrry_valid_service_is_sources_list_updated)r(r@rtrurvrwrrr)r)r*r+s zUAEntitlement.__init__cCs.|jdur|j p|jpt|jjd|_|jS)z2Check if the service is marked as valid (non-beta)Nzfeatures.allow_beta)ris_betarur"r@r3r)r)r* valid_service4s  zUAEntitlement.valid_servicecCst|jjj|jiSr')copydeepcopyr@rA entitlementsrDr6r3r)r)r*re@sz#UAEntitlement._base_entitlement_cfgcCs*|}|jr |s |Stj||jd|S)N) orig_accessrc)rer?rapply_contract_overridesr4r(rCr)r)r*rCEs zUAEntitlement.entitlement_cfgcCr2)z The number of steps that are reported as progress while enabling this specific entitlement that are not shared with other entitlements. Nr)r3r)r)r* enable_stepsRzUAEntitlement.enable_stepscCr2)z The number of steps that are reported as progress while disabling this specific entitlement that are not shared with other entitlements. Nr)r3r)r)r* disable_stepsZrzUAEntitlement.disable_stepscCs|}|jdidid}|dur t|dkr |d7}|jdidid}|durJs z5UAEntitlement.handle_required_snaps..)keyr6classicConfinementSupportFchannel)r)rclassic_confinement_support)#rCrDris_snapd_installedrrINSTALLING_PACKAGESrH install_snapdis_snapd_installed_as_a_snap install_snaprProcessExecutionErrorrwarningEXECUTING_COMMAND_FAILEDrun_snapd_wait_cmd refresh_snapeventrrvalidate_proxyr@rPROXY_VALIDATION_SNAP_HTTP_URLrPROXY_VALIDATION_SNAP_HTTPS_URLconfigure_snap_proxySNAP_INSTALL_RETRIESrINSTALLING_REQUIRED_SNAPSrl get_snap_infoSnapNotInstalledError INSTALLING_REQUIRED_SNAP_PACKAGE) r(rrerrsnap_pkg snap_namerrr)r)r*r s        z#UAEntitlement.handle_required_snapscsP|jdidid}|sdSdd|D}ttfdd|DS)/install packages necessary to enable a service.r$rrTcSg|]}|dqSr6r)rIpackager)r)r*rKpzAUAEntitlement.are_required_packages_installed..csg|]}|vqSr)r))rIrequiredinstalled_packagesr)r*rKtr)rCrDr get_installed_packages_namesall)r(r package_namesr)rr*are_required_packages_installedcs z-UAEntitlement.are_required_packages_installedcCsp|jdidid}|sdS||dd|D}td||tjjd |d t |dS) rr$rrTcSrrr)rr)r)r*rKrz:UAEntitlement.handle_required_packages..zInstalling packages %r r) rCrD_update_sources_listrrrrrrHrPr run_apt_install_command)r(rrrr)r)r*rws    z&UAEntitlement.handle_required_packagescCs|jdidid}|sdSdd|D}t|dkr!dStd|d |}ttj j |d t |tj j |d dS) rr$rrTcSs g|] }|ddr|dqS)removeOnDisableFr6rrr)r)r*rKs  zCUAEntitlement.handle_removing_required_packages..rzUninstalling packages %rrr)rCrDrmrrrPrrrUNINSTALLING_PACKAGESrHr remove_packagesUNINSTALLING_PACKAGES_FAILED)r(rrpackage_names_strr)r)r*!handle_removing_required_packagess.     z/UAEntitlement.handle_removing_required_packagescCr2)a Enable specific entitlement. This should be implemented by subclasses. This method does the actual enablement, and does not check can_enable or handle pre_enable or post_enable messaging. @return: True on success, False otherwise. Nr)r(rr)r)r*rs zUAEntitlement._perform_enablecCsBg}|jD]}||j\}}|tjtjfvr||q|S)zI :return: List of incompatible services that are enabled )rYr$r@rzrr{WARNINGr8r(rservice ent_statusr}r)r)r*rs  z,UAEntitlement.blocking_incompatible_servicescCt|dkS)z Check for incompatible services. :return: True if there are incompatible services enabled False if there are no incompatible services enabled r)rmrr3r)r)r*rz*UAEntitlement.detect_incompatible_servicescCstj|jjdd}|D]?}|j|jdd}tjj|j|jd}|r)d|fS| dtj j|jd| |\}}|sL||rG|j fSd fSq d S) a) Prompt user when incompatible services are found during enable. When enabling a service, we may find that there is an incompatible service already enable. In that situation, we can ask the user if the incompatible service should be disabled before proceeding. There are also different ways to configure that behavior: We can disable removing incompatible service during enable by adding the following lines into uaclient.conf: features: block_disable_on_enable: true z features.block_disable_on_enable)r path_to_valueT)rt)service_being_enabledrFrr NrF) rr"r@rr$r#E_INCOMPATIBLE_SERVICE_STOPS_ENABLErHr:rDISABLING_INCOMPATIBLE_SERVICEdisabler)r(rcfg_block_disable_on_enabler ente_msgrrr)r)r*rs.  z*UAEntitlement.handle_incompatible_servicescCst|dkS)z Check if all required services are active :return: True if all required services are active False is at least one of the required services is disabled r)rmrr3r)r)r*rr z,UAEntitlement.check_required_services_activecCsBg}|jD]}||j\}}|tjtjfvr||q|S)zF :return: List of required services that are disabled )r\r$r@rzrr{rr8r r)r)r*rs  z(UAEntitlement.blocking_required_servicescCs|D]?}|j|jdd}|dtjj|jd||\}}|sCd}|r4|j r4|j j r4d|j j }tj j||jd}||fSqdS) a, Prompt user when required services are found during enable. When enabling a service, we may find that there are required services that must be enabled first. In that situation, we can ask the user if the required service should be enabled before proceeding. T)rurrr0rL)rr rF) rr$r@rrENABLING_REQUIRED_SERVICErHr:rrmsgERROR_ENABLING_REQUIRED_SERVICE)r(rrrrr error_msgrr)r)r*r s"    z'UAEntitlement._enable_required_servicesignore_dependent_servicescCs|\}}|tjkrdttjtjj|jddfS| \}}|j r(|t j kr7dttj tjj|jddfS|jrG|sG|rGdttjfS|js[|jr[dttjtjj|jdfSdS)zReport whether or not disabling is possible for the entitlement. :return: (True, None) if can disable (False, CanDisableFailure) if can't disable FrrrF)rzrrrrALREADY_DISABLEDrrHr:rrCrrNOT_APPLICABLECANNOT_DISABLE_NOT_APPLICABLEr^detect_dependent_servicesACTIVE_DEPENDENT_SERVICESsupports_purgerPURGE_NOT_SUPPORTEDDISABLE_PURGE_NOT_SUPPORTED)r(rrzr}rr)r)r* can_disable>sJ      zUAEntitlement.can_disablecCs|d|jd|\}}|s3|durdS|jtjkr/||\}}|s.||_d|fSnd|fS| |s:dS| s@dS|d|jddS)aDisable specific entitlement @param silent: Boolean set True to silence print/log of messages @return: tuple of (success, optional reason) (True, None) on success. (False, reason) otherwise. reason is only non-None if it is a populated CanDisableFailure reason. This may expand to include other types of reasons in the future. r pre_disableNrF post_disablerF) rrrDr$rrr _disable_dependent_servicesr_perform_disabler)r(rr$rrrr)r)r*r}s(  zUAEntitlement.disablecCr2)a\ Disable specific entitlement. This should be implemented by subclasses. This method does the actual disable, and does not check can_disable or handle pre_disable or post_disable messaging. @param silent: Boolean set True to silence print/log of messages @return: True on success, False otherwise. Nr)rr)r)r*r(s zUAEntitlement._perform_disablecCr )z Check for depedent services. :return: True if there are dependent services enabled False if there are no dependent services enabled r)rmrr3r)r)r*rr z'UAEntitlement.detect_dependent_servicescCs:g}|jD]}||j\}}|tjkr||q|S)zo Return list of depedent services that must be disabled before disabling this service. )r^r@rzrr{r8)r(blockingdependent_service_clsr r}r)r)r*rs   z)UAEntitlement.blocking_dependent_servicescCs|D]>}||jdd}|dtjj|jd||\}}|sBd}|r3|jr3|jj r3d|jj }tj j||jd}d|fSqd S) ay Disable dependent services When performing a disable operation, we might have other services that depend on the original services. If that is true, we will alert the user about this and prompt for confirmation to disable these services as well. @param silent: Boolean set True to silence print/log of messages T)r@rtr)rr0rL)rrFrF) rr@rrDISABLING_DEPENDENT_SERVICErHr:rrr"FAILED_DISABLING_DEPENDENT_SERVICE)r(rr*rrrrrr)r)r*r's&    z)UAEntitlement._disable_dependent_servicescCr2)z=Check if system needs to be rebooted because of this service.Fr)r3r)r)r*_check_for_rebootrVzUAEntitlement._check_for_reboot operationsilentcCs,|r|sttjj|ddSdSdS)zCheck if user should be alerted that a reboot must be performed. @param operation: The operation being executed. @param silent: Boolean set True to silence print/log of messages )r.N)r-rrrENABLE_REBOOT_REQUIRED_TMPLrH)r(r.r/r)r)r*_check_for_reboot_msgs z#UAEntitlement._check_for_reboot_msgcCs*|j}|s tjtjfS|jD]\}}}||krtj|fSq|ddi}|dd}|jrR|durRt |vrRt |}tjtj j|jt d|dfS|dd}|jru|durut j|vrutjtjj|jt jdfSt } |d d} |d d} |jr| dur| j| vrtjtjj|j| jd| d fS|jr| r| jdur| jdurtjj|j| j| d } z| d \} }t | }t |}Wnt!yt"#d| tj| fYSw| j|krtj| fS| j|kr| j|krtj| fS|di}|$|\}}|stj|fStjdfS)aCheck all contract affordances to vet current platform Affordances are a list of support constraints for the entitlement. Examples include a list of supported series, architectures for kernel revisions. :return: tuple of (ApplicabilityStatus, NamedMessage). APPLICABLE if platform passes all defined affordances, INAPPLICABLE if it doesn't meet all of the provided constraints. r$r= architecturesNz, )r:archsupported_archesseries)r:r5 kernelFlavorsminKernelVersion)r:kernelsupported_kernels)r:r8 min_kernel.z$Could not parse minKernelVersion: %splatformChecks)%rCr APPLICABLEr"NO_ENTITLEMENT_AFFORDANCES_CHECKEDrUrrDaffordance_check_archr get_dpkg_archrdeduplicate_archesINAPPLICABLE_ARCHrHr:rPaffordance_check_seriesget_release_infor5INAPPLICABLE_SERIESpretty_versionget_kernel_infoaffordance_check_kernel_flavorflavorINAPPLICABLE_KERNEL uname_release#affordance_check_kernel_min_versionmajorminorINAPPLICABLE_KERNEL_VERsplitint ValueErrorrrrG)r(rC error_messagefunctorexpected_resultr=affordance_archesdeduplicated_archesaffordance_series kernel_infoaffordance_kernelsaffordance_min_kernel invalid_msg kernel_major kernel_minormin_kern_majormin_kern_minoraffordances_platform_checkrrr)r)r*rs                     z"UAEntitlement.applicability_statuscCs6t|jjs tjS|j}|r|ddrtjStjS)z=Return whether the user is entitled to the entitlement or notr$entitled)rr@ is_attachedrrrCrDrrr)r)r*rns zUAEntitlement.contract_statuscCs|\}}|tjkrtj|fS|j}|s tjtjj |j dfS|d dddur5tjtjj |j dfS| \}}|t jkrEtj|fS|t jkrOtj|fS|\}}|r\tj|fStj|fS)z4Return (user-facing status, details) for entitlementrr$rbF)rrr=rrrC UNAVAILABLErSERVICE_NOT_ENTITLEDrHr:rDrzrrINACTIVErenabled_warning_statusACTIVE)r( applicabilityrrCrz explanationrwarn_msgr)r)r*user_facing_statusws*           z UAEntitlement.user_facing_statuscCr2)z The current status of application of this entitlement :return: A tuple of (ApplicationStatus, human-friendly reason) Nr)r3r)r)r*rz z UAEntitlement.application_statuscCr2)z If the entitlment is enabled, are there any warnings? The message is displayed as a Warning Notice in status output :return: A tuple of (warning bool, human-friendly reason) rr)r3r)r)r*rgrmz$UAEntitlement.enabled_warning_statuscCsdSr'r)r3r)r)r*status_description_overrider5z)UAEntitlement.status_description_overridecCs4|jd}|s dSt|d}|tkrdSdS)zs  (4 (