def check_system(self): """ After a system has had all of its packages installed and it's in stable-state, this is a method we can use to verify that everything is still kosher """ Logger.info("System-check starting...") bom_pkns = self.config.get_bom_pkns() pdat = self.progress.get_progress_data() full_pdat = self.progress.get_progress_data(False) full_installed_pkns, _full_bpkns = Progress.get_installed(full_pdat) msg = "Packages that are installed: %s" Logger.info(msg % " ".join(full_installed_pkns)) installed_pkns, broken_pkns = Progress.get_installed(pdat) should_be_installed, shouldnt_be_installed = self._check_bom(bom_pkns) # check the configuration for each installed package pkg_info = { "Packages installed properly": installed_pkns, "Packages to be RECONFIGURED": {}, "Packages to be INSTALLED": should_be_installed, "Packages to be REMOVED": shouldnt_be_installed, "Packages that are BROKEN": broken_pkns, } for pkn in shouldnt_be_installed: pkg_info["Packages installed properly"].remove(pkn) for pkn in installed_pkns: differences = self._check_configuration_hash(pkn) if differences: if pkn in pkg_info["Packages installed properly"]: pkg_info["Packages to be RECONFIGURED"][pkn] = differences pkg_info["Packages installed properly"].remove(pkn) for key in pkg_info: Logger.info("==OUTPUT==:%s: %s" % (key, pkg_info[key])) return pkg_info
def use_pkg(self, pkn, action, script_name, arguments): """ Main entry point to the class. Performs an action using a package pkn -- name of the package to use action -- STATUS, INSTALL, UNINSTALL, CONFIGURE, VERIFY, or EXEC script_name -- the name of a method to run within the package arguments -- arguments to the executable, typically a restore target """ try: pkg = self._get_new_pkg(pkn) if pkg.status == FAIL: self.operation_status = FAIL return self._cleanup() if action == INSTALL: pdat = self.progress.get_progress_data(False) installed_pkns, broken_pkns = Progress.get_installed(pdat) if pkn in [installed_pkns + broken_pkns]: Logger.error("Package %s cannot be installed." % pkn) self.operation_status = FAIL return FAIL add_pkd = {pkn: pkg} status = self._install_pkgs(add_pkd) if action == UNINSTALL: pdat = self.progress.get_progress_data(False) installed_pkns, broken_pkns = Progress.get_installed(pdat) bom_pkns = installed_pkns if pkn in bom_pkns: bom_pkns.remove(pkn) self.config.set_bom_pkgs(bom_pkns) add_pkd, del_pkd, uninstall_order = self._ck_inst_stat([]) status = self._uninstall_pkgs(del_pkd, uninstall_order) if action == VERIFY: status = pkg.verify() if action == CONFIGURE: self._check_configuration(pkg) status = pkg.configure() hash_path = make_path(pkg.get_path(), HASH_FILE) msg = "Writing configuration fingerprint to %s" % hash_path Logger.info(msg) self.config.save_hash(hash_path) if action in [EXECUTE, BACKUP, RESTORE]: status = pkg.execute_maint_script(script_name, arguments) if status == FAIL: self.operation_status = FAIL msg = "Finished %s for %s." msg = msg % (ACTION_REVERSE_LOOKUP[action], pkn) self.operation_output.append(msg) status = self._cleanup() return self._cleanup() except Exceptions.BadPackage, err: errmsg = "Cannot perform action %s on package %s: %s" errmsg = errmsg % (ACTION_REVERSE_LOOKUP[action], err.pkn, err.errmsg) Logger.warning(errmsg) Logger.info("RETURN FAIL 1") return FAIL
def _check_bom(self, bom_pkns): """ Check through what should be installed on the system and what is installed on the system and determine what packages aren't installed that should be and what packages are installed that shouldn't be. bom_pkns -- A list of package names that are intended to be installed on the machine (i.e. the 'bill of materials' """ should_be_installed = [] shouldnt_be_installed = [] pdat = self.progress.get_progress_data(False) installed_pkns, broken_pkns = Progress.get_installed(pdat) all_pkns = set(installed_pkns).union(broken_pkns) all_plus_missing_pkns = self._examine_dependencies(all_pkns) missing_pkns = list(all_plus_missing_pkns - all_pkns) bom_pkns = bom_pkns + missing_pkns dependency_errors = self._get_dependency_errors(bom_pkns, pdat) if dependency_errors: errmsg = "The following packages are installed as " "dependencies %s" % dependency_errors Logger.debug(errmsg) bom_pkns += dependency_errors for pkn in installed_pkns: if pkn not in bom_pkns: shouldnt_be_installed.append(pkn) for pkn in bom_pkns: if pkn not in installed_pkns: should_be_installed.append(pkn) return should_be_installed, shouldnt_be_installed
def _find_install_order(self, pkd): """Returns a list of pkns in the correct installation order - Create chains of package dependencies, the priority of each chain is the package with the highest priority in it, unless that package is already installed. - A package can appear in more than one chain - A package chain can have a single package in it if it does not have any dependencies and is not dependent on others. pkd -- dictionary of package objects """ chains = self._create_pkg_chains(pkd) pdat = self.progress.get_progress_data(False) installed_pkns, _broken_pkns = Progress.get_installed(pdat) # - Put all the packages of each chain into the installation # order, excluding those that have already been installed in order # of chain priority. If a package is already in the installation # List, do not put it in twice. install_order = [] while chains: top_priority = self._get_top_priority(chains) for priority, chain in chains: if priority == top_priority: for pkn in chain: if pkn not in installed_pkns: if pkn not in install_order: install_order.append(pkn) chains.remove([priority, chain]) return install_order
def _create_pkg_chains(self, pkd): """ Create package chains based on current set of packages pkd -- dictionary of package objects """ chains = [] pkg_data = self.progress.get_progress_data(False) installed_pkns, broken_pkns = Progress.get_installed(pkg_data) for pkn in pkd.keys(): if pkn in broken_pkns: Logger.warning("Skipping broken package %s" % pkn) continue if pkn not in installed_pkns: chain_priority = pkd[pkn].priority else: chain_priority = 0 try: new_chain = PackageChain( chain_priority, pkn, pkd, installed_pkns, broken_pkns, self.repository, self.config, self.instance_name, self.record_errors, ) except Exceptions.BadPackage, err: errmsg = "Package %s will not be installed because it is " "dependent upon one or more broken packages" Logger.warning(errmsg % pkn) Logger.warning(str(err)) continue chains.append([new_chain.priority, new_chain.chain])
def get_names_from_progress(self, stripped=False): progress_data = self.get_progress_data() if progress_data == None: (installed_package_names, broken_package_names) = ([], []) else: pkg_info = Progress.get_installed_uninstalled_times(progress_data) unstripped_inst_pkgs = [package_name[0] for package_name in pkg_info["installed"]] unstripped_broken_pkgs = [package_name[0] for package_name in pkg_info["broken_installed"]] unstripped_broken_pkgs += [package_name[0] for package_name in pkg_info["broken_uninstalled"]] if stripped: installed_package_names = [Progress.strip_version(x) for x in unstripped_inst_pkgs] broken_package_names = [Progress.strip_version(x) for x in unstripped_broken_pkgs] else: installed_package_names = unstripped_inst_pkgs broken_package_names = unstripped_broken_pkgs return (set(installed_package_names), set(broken_package_names))
def get_all_package_names(self, config_packages): "Get package names from status and this object and union them together." try: package_list = self.get_package_names_from_progress().get(PROGRESS, {}) except MachineStatusException: package_list = [] package_names = set([Progress.strip_version(x) for x in package_list]) package_names = package_names.union(set(config_packages)) return list(package_names)
def __init__(self, repository, config, instance_name): """ repository -- object that keeps track of package data config -- object that keeps track of this machine's configuration instance_name -- the name of this machine """ self.repository = repository self.config = config self.instance_name = instance_name self.record_errors = True self.operation_status = OK self.operation_output = [] self.progress = Progress(instance_name)
def _get_pkd_to_remove(self, del_pkns): """ Given a list of package names that we want to delete, let's create a proper dictionary of package objects which provide complete dependency information and specify the proper uninstallation order del_pkns -- a list of package names that need to be removed """ "Getting packages to remove..." uninstall_order = [] pdat = self.progress.get_progress_data(False) installed_pkns, _broken_pkns = Progress.get_installed(pdat) del_pkd = self._get_del_pkd(del_pkns) if set(installed_pkns) == set(del_pkd.keys()): return del_pkd, del_pkd.keys() if del_pkns: del_pkd, uninstall_order = self._get_uninst_pkg_dep(del_pkd, del_pkns, installed_pkns) return del_pkd, uninstall_order
class Bombardier: """Master class for managing system actions""" def __init__(self, repository, config, instance_name): """ repository -- object that keeps track of package data config -- object that keeps track of this machine's configuration instance_name -- the name of this machine """ self.repository = repository self.config = config self.instance_name = instance_name self.record_errors = True self.operation_status = OK self.operation_output = [] self.progress = Progress(instance_name) def _get_dependency_errors(self, bom_pkns, pdat): """ Pulls dependency error data out of status.yml bom_pkns -- the names of packages which are on the bill of materials for this machine pdat -- a dictionary which maintains information about what packages are installed, uninstalled, verified, etc. """ dependency_names = set([]) install_progress = pdat.get("install-progress", {}) for pkn in install_progress: pkd = install_progress[pkn] dep_errors = set(pkd.get("DEPENDENCY_ERRORS", [])) dependency_names = dependency_names.union(dep_errors) dependency_names = list(dependency_names - set(bom_pkns)) return dependency_names def _create_pkg_chains(self, pkd): """ Create package chains based on current set of packages pkd -- dictionary of package objects """ chains = [] pkg_data = self.progress.get_progress_data(False) installed_pkns, broken_pkns = Progress.get_installed(pkg_data) for pkn in pkd.keys(): if pkn in broken_pkns: Logger.warning("Skipping broken package %s" % pkn) continue if pkn not in installed_pkns: chain_priority = pkd[pkn].priority else: chain_priority = 0 try: new_chain = PackageChain( chain_priority, pkn, pkd, installed_pkns, broken_pkns, self.repository, self.config, self.instance_name, self.record_errors, ) except Exceptions.BadPackage, err: errmsg = "Package %s will not be installed because it is " "dependent upon one or more broken packages" Logger.warning(errmsg % pkn) Logger.warning(str(err)) continue chains.append([new_chain.priority, new_chain.chain]) return chains