in src/core/src/core_logic/PatchInstaller.py [0:0]
def install_updates(self, maintenance_window, package_manager, simulate=False):
"""wrapper function of installing updates"""
self.composite_logger.log("\n\nGetting available updates...")
package_manager.refresh_repo()
packages, package_versions = package_manager.get_available_updates(self.package_filter) # Initial, ignoring exclusions
self.telemetry_writer.write_event("Initial package list: " + str(packages), Constants.TelemetryEventLevel.Verbose)
not_included_packages, not_included_package_versions = self.get_not_included_updates(package_manager, packages)
self.telemetry_writer.write_event("Not Included package list: " + str(not_included_packages), Constants.TelemetryEventLevel.Verbose)
excluded_packages, excluded_package_versions = self.get_excluded_updates(package_manager, packages, package_versions)
self.telemetry_writer.write_event("Excluded package list: " + str(excluded_packages), Constants.TelemetryEventLevel.Verbose)
packages, package_versions = self.filter_out_excluded_updates(packages, package_versions, excluded_packages) # honoring exclusions
# For ubuntu VMs, filter out esm_packages, if the VM is not attached.
# These packages will already be marked with version as 'UA_ESM_REQUIRED'.
# Esm packages will not be dependent packages to non-esm packages. This is confirmed by Canonical. So, once these are removed from processing, we need not worry about handling it in our batch / sequential patch processing logic.
# Adding this after filtering excluded packages, so we don`t un-intentionally mark excluded esm-package status as failed.
packages, package_versions, self.skipped_esm_packages, self.skipped_esm_package_versions, self.esm_packages_found_without_attach = package_manager.separate_out_esm_packages(packages, package_versions)
self.telemetry_writer.write_event("Final package list: " + str(packages), Constants.TelemetryEventLevel.Verbose)
# Set initial statuses
if not package_manager.get_package_manager_setting(Constants.PACKAGE_MGR_SETTING_REPEAT_PATCH_OPERATION, False): # 'Not included' list is not accurate when a repeat is required
self.status_handler.set_package_install_status(not_included_packages, not_included_package_versions, Constants.NOT_SELECTED)
self.status_handler.set_package_install_status(excluded_packages, excluded_package_versions, Constants.EXCLUDED)
self.status_handler.set_package_install_status(packages, package_versions, Constants.PENDING)
self.status_handler.set_package_install_status(self.skipped_esm_packages, self.skipped_esm_package_versions, Constants.FAILED)
self.composite_logger.log("\nList of packages to be updated: \n" + str(packages))
sec_packages, sec_package_versions = self.package_manager.get_security_updates()
self.telemetry_writer.write_event("Security packages out of the final package list: " + str(sec_packages), Constants.TelemetryEventLevel.Verbose)
self.status_handler.set_package_install_status_classification(sec_packages, sec_package_versions, classification="Security")
# Set the security-esm package status.
package_manager.set_security_esm_package_status(Constants.INSTALLATION, packages)
self.composite_logger.log("\nNote: Packages that are neither included nor excluded may still be installed if an included package has a dependency on it.")
# We will see this as packages going from NotSelected --> Installed. We could remove them preemptively from not_included_packages, but we're explicitly choosing not to.
self.composite_logger.log("[Progress Legend: (A)ttempted, (S)ucceeded, (F)ailed, (D)ependencies est.* (Important: Dependencies are excluded in all other counts)]")
installed_update_count = 0 # includes dependencies
patch_installation_successful = True
maintenance_window_exceeded = False
all_packages, all_package_versions = package_manager.get_all_updates(cached=False)
self.telemetry_writer.write_event("All available packages list: " + str(all_packages), Constants.TelemetryEventLevel.Verbose)
self.last_still_needed_packages = list(all_packages)
self.last_still_needed_package_versions = list(all_package_versions)
packages, package_versions, install_update_count_in_batch_patching, patch_installation_successful = self.batch_patching(all_packages, all_package_versions,
packages, package_versions, maintenance_window,
package_manager)
installed_update_count = install_update_count_in_batch_patching
attempted_parent_package_install_count_in_batch_patching = self.attempted_parent_package_install_count
successful_parent_package_install_count_in_batch_patching = self.successful_parent_package_install_count
if len(packages) == 0:
self.log_final_metrics(maintenance_window, patch_installation_successful, maintenance_window_exceeded, installed_update_count)
return installed_update_count, patch_installation_successful, maintenance_window_exceeded
else:
progress_status = self.progress_template.format(str(datetime.timedelta(minutes=maintenance_window.get_remaining_time_in_minutes())), str(self.attempted_parent_package_install_count), str(self.successful_parent_package_install_count), str(self.failed_parent_package_install_count), str(installed_update_count - self.successful_parent_package_install_count),
"Following packages are not attempted or failed in batch installation: " + str(packages))
self.composite_logger.log(progress_status)
stopwatch_for_sequential_install_process = Stopwatch(self.env_layer, self.telemetry_writer, self.composite_logger)
stopwatch_for_sequential_install_process.start()
for package, version in zip(packages, package_versions):
if package not in self.last_still_needed_packages:
self.composite_logger.log("The following package is already installed, it could have been installed as dependent package of some other package: " + package)
self.attempted_parent_package_install_count += 1
self.successful_parent_package_install_count += 1
continue
single_package_install_stopwatch = Stopwatch(self.env_layer, self.telemetry_writer, self.composite_logger)
single_package_install_stopwatch.start()
# Extension state check
if self.lifecycle_manager is not None:
self.lifecycle_manager.lifecycle_status_check() # may terminate the code abruptly, as designed
# maintenance window check
remaining_time = maintenance_window.get_remaining_time_in_minutes()
if maintenance_window.is_package_install_time_available(package_manager, remaining_time, number_of_packages_in_batch=1) is False:
error_msg = "Stopped patch installation as it is past the maintenance window cutoff time."
self.composite_logger.log_error("\n" + error_msg)
self.status_handler.add_error_to_status(error_msg, Constants.PatchOperationErrorCodes.DEFAULT_ERROR)
maintenance_window_exceeded = True
self.status_handler.set_maintenance_window_exceeded(True)
break
# point in time status
progress_status = self.progress_template.format(str(datetime.timedelta(minutes=remaining_time)), str(self.attempted_parent_package_install_count), str(self.successful_parent_package_install_count), str(self.failed_parent_package_install_count), str(installed_update_count - self.successful_parent_package_install_count),
"Processing package: " + str(package) + " (" + str(version) + ")")
self.composite_logger.log(progress_status)
# include all dependencies (with specified versions) explicitly
# package_and_dependencies initially contains only one package. The dependencies are added in the list by method include_dependencies
package_and_dependencies = [package]
package_and_dependency_versions = [version]
self.include_dependencies(package_manager, [package], [version], all_packages, all_package_versions, packages, package_versions, package_and_dependencies, package_and_dependency_versions)
# parent package install (+ dependencies) and parent package result management
install_result = package_manager.install_update_and_dependencies_and_get_status(package_and_dependencies, package_and_dependency_versions, simulate)
# Update reboot pending status in status_handler
self.status_handler.set_reboot_pending(self.package_manager.is_reboot_pending())
if install_result == Constants.FAILED:
self.status_handler.set_package_install_status(package_manager.get_product_name(str(package_and_dependencies[0])), str(package_and_dependency_versions[0]), Constants.FAILED)
self.failed_parent_package_install_count += 1
patch_installation_successful = False
elif install_result == Constants.INSTALLED:
self.status_handler.set_package_install_status(package_manager.get_product_name(str(package_and_dependencies[0])), str(package_and_dependency_versions[0]), Constants.INSTALLED)
self.successful_parent_package_install_count += 1
if package in self.last_still_needed_packages:
index = self.last_still_needed_packages.index(package)
self.last_still_needed_packages.pop(index)
self.last_still_needed_package_versions.pop(index)
installed_update_count += 1
self.attempted_parent_package_install_count += 1
number_of_dependencies_installed = 0
number_of_dependencies_failed = 0
# dependency package result management
for dependency, dependency_version in zip(package_and_dependencies, package_and_dependency_versions):
if dependency not in self.last_still_needed_packages or dependency == package:
continue
if package_manager.is_package_version_installed(dependency, dependency_version):
self.composite_logger.log_debug(" - Marking dependency as succeeded: " + str(dependency) + "(" + str(dependency_version) + ")")
self.status_handler.set_package_install_status(package_manager.get_product_name(str(dependency)), str(dependency_version), Constants.INSTALLED)
index = self.last_still_needed_packages.index(dependency)
self.last_still_needed_packages.pop(index)
self.last_still_needed_package_versions.pop(index)
installed_update_count += 1
number_of_dependencies_installed += 1
else:
# status is not logged by design here, in case you were wondering if that's a bug
message = " - [Info] Dependency appears to have failed to install (note: it *may* be retried): " + str(dependency) + "(" + str(dependency_version) + ")"
self.composite_logger.log_debug(message)
number_of_dependencies_failed += 1
# dependency package result management fallback (not reliable enough to be used as primary, and will be removed; remember to retain last_still_needed refresh when you do that)
installed_update_count += self.perform_status_reconciliation_conditionally(package_manager, condition=(self.attempted_parent_package_install_count % Constants.PACKAGE_STATUS_REFRESH_RATE_IN_SECONDS == 0)) # reconcile status after every 10 attempted installs
package_install_perf_log = "[{0}={1}][{2}={3}][{4}={5}][{6}={7}][{8}={9}][{10}={11}][{12}={13}][{14}={15}]".format(Constants.PerfLogTrackerParams.TASK, "InstallPackage",
"PackageName", package, "PackageVersion", version, "PackageAndDependencies", str(package_and_dependencies),"PackageAndDependencyVersions", str(package_and_dependency_versions),
"PackageInstallResult", str(install_result), "NumberOfDependenciesInstalled", str(number_of_dependencies_installed), "NumberOfDependenciesFailed", str(number_of_dependencies_failed))
single_package_install_stopwatch.stop_and_write_telemetry(str(package_install_perf_log))
self.composite_logger.log_debug("\nPerforming final system state reconciliation...")
installed_update_count += self.perform_status_reconciliation_conditionally(package_manager, True)
self.log_final_metrics(maintenance_window, patch_installation_successful, maintenance_window_exceeded, installed_update_count)
install_update_count_in_sequential_patching = installed_update_count - install_update_count_in_batch_patching
attempted_parent_package_install_count_in_sequential_patching = self.attempted_parent_package_install_count - attempted_parent_package_install_count_in_batch_patching
successful_parent_package_install_count_in_sequential_patching = self.successful_parent_package_install_count - successful_parent_package_install_count_in_batch_patching
failed_parent_package_install_count_after_sequential_patching = self.failed_parent_package_install_count
sequential_processing_perf_log = "[{0}={1}][{2}={3}][{4}={5}][{6}={7}][{8}={9}]".format(Constants.PerfLogTrackerParams.TASK, "InstallPackagesSequentially", "InstalledPackagesCountInSequentialProcessing",
install_update_count_in_sequential_patching, "AttemptedParentPackageInstallCount", attempted_parent_package_install_count_in_sequential_patching,
"SuccessfulParentPackageInstallCount", successful_parent_package_install_count_in_sequential_patching, "FailedParentPackageInstallCount",
failed_parent_package_install_count_after_sequential_patching)
stopwatch_for_sequential_install_process.stop_and_write_telemetry(sequential_processing_perf_log)
return installed_update_count, patch_installation_successful, maintenance_window_exceeded