1# Copyright (C) 2017-2018 Red Hat, Inc. 2# 3# This program is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 2 of the License, or 6# (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU Library General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 17from collections import OrderedDict 18 19import hawkey 20import libdnf.smartcols 21import libdnf.module 22import dnf.selector 23import dnf.exceptions 24 25from dnf.module.exceptions import EnableMultipleStreamsException 26from dnf.util import logger 27from dnf.i18n import _, P_, ucd 28 29import functools 30 31STATE_DEFAULT = libdnf.module.ModulePackageContainer.ModuleState_DEFAULT 32STATE_ENABLED = libdnf.module.ModulePackageContainer.ModuleState_ENABLED 33STATE_DISABLED = libdnf.module.ModulePackageContainer.ModuleState_DISABLED 34STATE_UNKNOWN = libdnf.module.ModulePackageContainer.ModuleState_UNKNOWN 35MODULE_TABLE_HINT = _("\n\nHint: [d]efault, [e]nabled, [x]disabled, [i]nstalled") 36MODULE_INFO_TABLE_HINT = _("\n\nHint: [d]efault, [e]nabled, [x]disabled, [i]nstalled, [a]ctive") 37 38 39def _profile_comparison_key(profile): 40 return profile.getName() 41 42 43class ModuleBase(object): 44 # :api 45 46 def __init__(self, base): 47 # :api 48 self.base = base 49 50 def enable(self, module_specs): 51 # :api 52 no_match_specs, error_specs, solver_errors, module_dicts = \ 53 self._resolve_specs_enable_update_sack(module_specs) 54 for spec, (nsvcap, module_dict) in module_dicts.items(): 55 if nsvcap.profile: 56 logger.info(_("Ignoring unnecessary profile: '{}/{}'").format( 57 nsvcap.name, nsvcap.profile)) 58 if no_match_specs or error_specs or solver_errors: 59 raise dnf.exceptions.MarkingErrors(no_match_group_specs=no_match_specs, 60 error_group_specs=error_specs, 61 module_depsolv_errors=solver_errors) 62 63 def disable(self, module_specs): 64 # :api 65 no_match_specs, solver_errors = self._modules_reset_or_disable(module_specs, STATE_DISABLED) 66 if no_match_specs or solver_errors: 67 raise dnf.exceptions.MarkingErrors(no_match_group_specs=no_match_specs, 68 module_depsolv_errors=solver_errors) 69 70 def install(self, module_specs, strict=True): 71 # :api 72 no_match_specs, error_specs, solver_errors, module_dicts = \ 73 self._resolve_specs_enable_update_sack(module_specs) 74 75 # <package_name, set_of_spec> 76 fail_safe_repo = hawkey.MODULE_FAIL_SAFE_REPO_NAME 77 install_dict = {} 78 install_set_artifacts = set() 79 fail_safe_repo_used = False 80 for spec, (nsvcap, moduledict) in module_dicts.items(): 81 for name, streamdict in moduledict.items(): 82 for stream, module_list in streamdict.items(): 83 install_module_list = [x for x in module_list 84 if self.base._moduleContainer.isModuleActive(x.getId())] 85 if not install_module_list: 86 logger.error(_("All matches for argument '{0}' in module '{1}:{2}' are not " 87 "active").format(spec, name, stream)) 88 error_specs.append(spec) 89 continue 90 profiles = [] 91 latest_module = self._get_latest(install_module_list) 92 if latest_module.getRepoID() == fail_safe_repo: 93 msg = _( 94 "Installing module '{0}' from Fail-Safe repository {1} is not allowed") 95 logger.critical(msg.format(latest_module.getNameStream(), fail_safe_repo)) 96 fail_safe_repo_used = True 97 if nsvcap.profile: 98 profiles.extend(latest_module.getProfiles(nsvcap.profile)) 99 if not profiles: 100 available_profiles = latest_module.getProfiles() 101 if available_profiles: 102 profile_names = ", ".join(sorted( 103 [profile.getName() for profile in available_profiles])) 104 msg = _("Unable to match profile for argument {}. Available " 105 "profiles for '{}:{}': {}").format( 106 spec, name, stream, profile_names) 107 else: 108 msg = _("Unable to match profile for argument {}").format(spec) 109 logger.error(msg) 110 no_match_specs.append(spec) 111 continue 112 else: 113 profiles_strings = self.base._moduleContainer.getDefaultProfiles( 114 name, stream) 115 if not profiles_strings: 116 available_profiles = latest_module.getProfiles() 117 if available_profiles: 118 profile_names = ", ".join(sorted( 119 [profile.getName() for profile in available_profiles])) 120 msg = _("No default profiles for module {}:{}. Available profiles" 121 ": {}").format( 122 name, stream, profile_names) 123 else: 124 msg = _("No profiles for module {}:{}").format(name, stream) 125 logger.error(msg) 126 error_specs.append(spec) 127 for profile in set(profiles_strings): 128 module_profiles = latest_module.getProfiles(profile) 129 if not module_profiles: 130 logger.error( 131 _("Default profile {} not available in module {}:{}").format( 132 profile, name, stream)) 133 error_specs.append(spec) 134 135 profiles.extend(module_profiles) 136 for profile in profiles: 137 self.base._moduleContainer.install(latest_module ,profile.getName()) 138 for pkg_name in profile.getContent(): 139 install_dict.setdefault(pkg_name, set()).add(spec) 140 for module in install_module_list: 141 install_set_artifacts.update(module.getArtifacts()) 142 if fail_safe_repo_used: 143 raise dnf.exceptions.Error(_( 144 "Installing module from Fail-Safe repository is not allowed")) 145 __, profiles_errors = self._install_profiles_internal( 146 install_set_artifacts, install_dict, strict) 147 if profiles_errors: 148 error_specs.extend(profiles_errors) 149 150 if no_match_specs or error_specs or solver_errors: 151 raise dnf.exceptions.MarkingErrors(no_match_group_specs=no_match_specs, 152 error_group_specs=error_specs, 153 module_depsolv_errors=solver_errors) 154 155 def switch_to(self, module_specs, strict=True): 156 # :api 157 no_match_specs, error_specs, module_dicts = self._resolve_specs_enable(module_specs) 158 # collect name of artifacts from new modules for distrosync 159 new_artifacts_names = set() 160 # collect name of artifacts from active modules for distrosync before sack update 161 active_artifacts_names = set() 162 src_arches = {"nosrc", "src"} 163 for spec, (nsvcap, moduledict) in module_dicts.items(): 164 for name in moduledict.keys(): 165 for module in self.base._moduleContainer.query(name, "", "", "", ""): 166 if self.base._moduleContainer.isModuleActive(module): 167 for artifact in module.getArtifacts(): 168 arch = artifact.rsplit(".", 1)[1] 169 if arch in src_arches: 170 continue 171 pkg_name = artifact.rsplit("-", 2)[0] 172 active_artifacts_names.add(pkg_name) 173 174 solver_errors = self._update_sack() 175 176 dependency_error_spec = self._enable_dependencies(module_dicts) 177 if dependency_error_spec: 178 error_specs.extend(dependency_error_spec) 179 180 # <package_name, set_of_spec> 181 fail_safe_repo = hawkey.MODULE_FAIL_SAFE_REPO_NAME 182 install_dict = {} 183 install_set_artifacts = set() 184 fail_safe_repo_used = False 185 186 # list of name: [profiles] for module profiles being removed 187 removed_profiles = self.base._moduleContainer.getRemovedProfiles() 188 189 for spec, (nsvcap, moduledict) in module_dicts.items(): 190 for name, streamdict in moduledict.items(): 191 for stream, module_list in streamdict.items(): 192 install_module_list = [x for x in module_list 193 if self.base._moduleContainer.isModuleActive(x.getId())] 194 if not install_module_list: 195 "No active matches for argument '{0}' in module '{1}:{2}'" 196 logger.error(_("No active matches for argument '{0}' in module " 197 "'{1}:{2}'").format(spec, name, stream)) 198 error_specs.append(spec) 199 continue 200 profiles = [] 201 latest_module = self._get_latest(install_module_list) 202 if latest_module.getRepoID() == fail_safe_repo: 203 msg = _( 204 "Installing module '{0}' from Fail-Safe repository {1} is not allowed") 205 logger.critical(msg.format(latest_module.getNameStream(), fail_safe_repo)) 206 fail_safe_repo_used = True 207 if nsvcap.profile: 208 profiles.extend(latest_module.getProfiles(nsvcap.profile)) 209 if not profiles: 210 available_profiles = latest_module.getProfiles() 211 if available_profiles: 212 profile_names = ", ".join(sorted( 213 [profile.getName() for profile in available_profiles])) 214 msg = _("Unable to match profile for argument {}. Available " 215 "profiles for '{}:{}': {}").format( 216 spec, name, stream, profile_names) 217 else: 218 msg = _("Unable to match profile for argument {}").format(spec) 219 logger.error(msg) 220 no_match_specs.append(spec) 221 continue 222 elif name in removed_profiles: 223 224 for profile in removed_profiles[name]: 225 module_profiles = latest_module.getProfiles(profile) 226 if not module_profiles: 227 logger.warning( 228 _("Installed profile '{0}' is not available in module " 229 "'{1}' stream '{2}'").format(profile, name, stream)) 230 continue 231 profiles.extend(module_profiles) 232 for profile in profiles: 233 self.base._moduleContainer.install(latest_module, profile.getName()) 234 for pkg_name in profile.getContent(): 235 install_dict.setdefault(pkg_name, set()).add(spec) 236 for module in install_module_list: 237 artifacts = module.getArtifacts() 238 install_set_artifacts.update(artifacts) 239 for artifact in artifacts: 240 arch = artifact.rsplit(".", 1)[1] 241 if arch in src_arches: 242 continue 243 pkg_name = artifact.rsplit("-", 2)[0] 244 new_artifacts_names.add(pkg_name) 245 if fail_safe_repo_used: 246 raise dnf.exceptions.Error(_( 247 "Installing module from Fail-Safe repository is not allowed")) 248 install_base_query, profiles_errors = self._install_profiles_internal( 249 install_set_artifacts, install_dict, strict) 250 if profiles_errors: 251 error_specs.extend(profiles_errors) 252 253 # distrosync module name 254 all_names = set() 255 all_names.update(new_artifacts_names) 256 all_names.update(active_artifacts_names) 257 remove_query = self.base.sack.query().filterm(empty=True) 258 base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() 259 260 for pkg_name in all_names: 261 query = base_no_source_query.filter(name=pkg_name) 262 installed = query.installed() 263 if not installed: 264 continue 265 available = query.available() 266 if not available: 267 logger.warning(_("No packages available to distrosync for package name " 268 "'{}'").format(pkg_name)) 269 if pkg_name not in new_artifacts_names: 270 remove_query = remove_query.union(query) 271 continue 272 273 only_new_module = query.intersection(install_base_query) 274 if only_new_module: 275 query = only_new_module 276 sltr = dnf.selector.Selector(self.base.sack) 277 sltr.set(pkg=query) 278 self.base._goal.distupgrade(select=sltr) 279 self.base._remove_if_unneeded(remove_query) 280 281 if no_match_specs or error_specs or solver_errors: 282 raise dnf.exceptions.MarkingErrors(no_match_group_specs=no_match_specs, 283 error_group_specs=error_specs, 284 module_depsolv_errors=solver_errors) 285 286 def reset(self, module_specs): 287 # :api 288 no_match_specs, solver_errors = self._modules_reset_or_disable(module_specs, STATE_UNKNOWN) 289 if no_match_specs: 290 raise dnf.exceptions.MarkingErrors(no_match_group_specs=no_match_specs, 291 module_depsolv_errors=solver_errors) 292 293 def upgrade(self, module_specs): 294 # :api 295 no_match_specs = [] 296 fail_safe_repo = hawkey.MODULE_FAIL_SAFE_REPO_NAME 297 fail_safe_repo_used = False 298 299 # Remove source packages because they cannot be installed or upgraded 300 base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() 301 302 for spec in module_specs: 303 module_list, nsvcap = self._get_modules(spec) 304 if not module_list: 305 no_match_specs.append(spec) 306 continue 307 update_module_list = [x for x in module_list 308 if self.base._moduleContainer.isModuleActive(x.getId())] 309 if not update_module_list: 310 logger.error(_("Unable to resolve argument {}").format(spec)) 311 continue 312 module_dict = self._create_module_dict_and_enable(update_module_list, spec, False) 313 upgrade_package_set = set() 314 for name, streamdict in module_dict.items(): 315 for stream, module_list_from_dict in streamdict.items(): 316 upgrade_package_set.update(self._get_package_name_set_and_remove_profiles( 317 module_list_from_dict, nsvcap)) 318 latest_module = self._get_latest(module_list_from_dict) 319 if latest_module.getRepoID() == fail_safe_repo: 320 msg = _( 321 "Upgrading module '{0}' from Fail-Safe repository {1} is not allowed") 322 logger.critical(msg.format(latest_module.getNameStream(), fail_safe_repo)) 323 fail_safe_repo_used = True 324 if nsvcap.profile: 325 profiles_set = latest_module.getProfiles(nsvcap.profile) 326 if not profiles_set: 327 continue 328 for profile in profiles_set: 329 upgrade_package_set.update(profile.getContent()) 330 else: 331 for profile in latest_module.getProfiles(): 332 upgrade_package_set.update(profile.getContent()) 333 for artifact in latest_module.getArtifacts(): 334 subj = hawkey.Subject(artifact) 335 for nevra_obj in subj.get_nevra_possibilities( 336 forms=[hawkey.FORM_NEVRA]): 337 upgrade_package_set.add(nevra_obj.name) 338 339 if not upgrade_package_set: 340 logger.error(_("Unable to match profile in argument {}").format(spec)) 341 query = base_no_source_query.filter(name=upgrade_package_set) 342 if query: 343 sltr = dnf.selector.Selector(self.base.sack) 344 sltr.set(pkg=query) 345 self.base._goal.upgrade(select=sltr) 346 if fail_safe_repo_used: 347 raise dnf.exceptions.Error(_( 348 "Upgrading module from Fail-Safe repository is not allowed")) 349 return no_match_specs 350 351 def remove(self, module_specs): 352 # :api 353 no_match_specs = [] 354 remove_package_set = set() 355 356 for spec in module_specs: 357 module_list, nsvcap = self._get_modules(spec) 358 if not module_list: 359 no_match_specs.append(spec) 360 continue 361 module_dict = self._create_module_dict_and_enable(module_list, spec, False) 362 remove_packages_names = [] 363 for name, streamdict in module_dict.items(): 364 for stream, module_list_from_dict in streamdict.items(): 365 remove_packages_names.extend(self._get_package_name_set_and_remove_profiles( 366 module_list_from_dict, nsvcap, True)) 367 if not remove_packages_names: 368 logger.error(_("Unable to match profile in argument {}").format(spec)) 369 remove_package_set.update(remove_packages_names) 370 371 if remove_package_set: 372 keep_pkg_names = self.base._moduleContainer.getInstalledPkgNames() 373 remove_package_set = remove_package_set.difference(keep_pkg_names) 374 if remove_package_set: 375 query = self.base.sack.query().installed().filterm(name=remove_package_set) 376 if query: 377 self.base._remove_if_unneeded(query) 378 return no_match_specs 379 380 def get_modules(self, module_spec): 381 # :api 382 return self._get_modules(module_spec) 383 384 def _get_modules(self, module_spec): 385 # used by ansible (lib/ansible/modules/packaging/os/dnf.py) 386 subj = hawkey.Subject(module_spec) 387 for nsvcap in subj.nsvcap_possibilities(): 388 name = nsvcap.name if nsvcap.name else "" 389 stream = nsvcap.stream if nsvcap.stream else "" 390 version = "" 391 context = nsvcap.context if nsvcap.context else "" 392 arch = nsvcap.arch if nsvcap.arch else "" 393 if nsvcap.version and nsvcap.version != -1: 394 version = str(nsvcap.version) 395 modules = self.base._moduleContainer.query(name, stream, version, context, arch) 396 if modules: 397 return modules, nsvcap 398 return (), None 399 400 def _get_latest(self, module_list): 401 latest = None 402 if module_list: 403 latest = module_list[0] 404 for module in module_list[1:]: 405 if module.getVersionNum() > latest.getVersionNum(): 406 latest = module 407 return latest 408 409 def _create_module_dict_and_enable(self, module_list, spec, enable=True): 410 moduleDict = {} 411 for module in module_list: 412 moduleDict.setdefault( 413 module.getName(), {}).setdefault(module.getStream(), []).append(module) 414 415 for moduleName, streamDict in moduleDict.items(): 416 moduleState = self.base._moduleContainer.getModuleState(moduleName) 417 if len(streamDict) > 1: 418 if moduleState != STATE_DEFAULT and moduleState != STATE_ENABLED \ 419 and moduleState != STATE_DISABLED: 420 streams_str = "', '".join( 421 sorted(streamDict.keys(), key=functools.cmp_to_key(self.base.sack.evr_cmp))) 422 msg = _("Argument '{argument}' matches {stream_count} streams ('{streams}') of " 423 "module '{module}', but none of the streams are enabled or " 424 "default").format( 425 argument=spec, stream_count=len(streamDict), streams=streams_str, 426 module=moduleName) 427 raise EnableMultipleStreamsException(moduleName, msg) 428 if moduleState == STATE_ENABLED: 429 stream = self.base._moduleContainer.getEnabledStream(moduleName) 430 else: 431 stream = self.base._moduleContainer.getDefaultStream(moduleName) 432 if not stream or stream not in streamDict: 433 raise EnableMultipleStreamsException(moduleName) 434 for key in sorted(streamDict.keys()): 435 if key == stream: 436 if enable: 437 self.base._moduleContainer.enable(moduleName, key) 438 continue 439 del streamDict[key] 440 elif enable: 441 for key in streamDict.keys(): 442 self.base._moduleContainer.enable(moduleName, key) 443 assert len(streamDict) == 1 444 return moduleDict 445 446 def _resolve_specs_enable(self, module_specs): 447 no_match_specs = [] 448 error_spec = [] 449 module_dicts = {} 450 for spec in module_specs: 451 module_list, nsvcap = self._get_modules(spec) 452 if not module_list: 453 no_match_specs.append(spec) 454 continue 455 try: 456 module_dict = self._create_module_dict_and_enable(module_list, spec, True) 457 module_dicts[spec] = (nsvcap, module_dict) 458 except (RuntimeError, EnableMultipleStreamsException) as e: 459 error_spec.append(spec) 460 logger.error(ucd(e)) 461 logger.error(_("Unable to resolve argument {}").format(spec)) 462 return no_match_specs, error_spec, module_dicts 463 464 def _update_sack(self): 465 hot_fix_repos = [i.id for i in self.base.repos.iter_enabled() if i.module_hotfixes] 466 try: 467 solver_errors = self.base.sack.filter_modules( 468 self.base._moduleContainer, hot_fix_repos, self.base.conf.installroot, 469 self.base.conf.module_platform_id, update_only=True, 470 debugsolver=self.base.conf.debug_solver) 471 except hawkey.Exception as e: 472 raise dnf.exceptions.Error(ucd(e)) 473 return solver_errors 474 475 def _enable_dependencies(self, module_dicts): 476 error_spec = [] 477 for spec, (nsvcap, moduleDict) in module_dicts.items(): 478 for streamDict in moduleDict.values(): 479 for modules in streamDict.values(): 480 try: 481 self.base._moduleContainer.enableDependencyTree( 482 libdnf.module.VectorModulePackagePtr(modules)) 483 except RuntimeError as e: 484 error_spec.append(spec) 485 logger.error(ucd(e)) 486 logger.error(_("Unable to resolve argument {}").format(spec)) 487 return error_spec 488 489 def _resolve_specs_enable_update_sack(self, module_specs): 490 no_match_specs, error_spec, module_dicts = self._resolve_specs_enable(module_specs) 491 492 solver_errors = self._update_sack() 493 494 dependency_error_spec = self._enable_dependencies(module_dicts) 495 if dependency_error_spec: 496 error_spec.extend(dependency_error_spec) 497 498 return no_match_specs, error_spec, solver_errors, module_dicts 499 500 def _modules_reset_or_disable(self, module_specs, to_state): 501 no_match_specs = [] 502 for spec in module_specs: 503 module_list, nsvcap = self._get_modules(spec) 504 if not module_list: 505 logger.error(_("Unable to resolve argument {}").format(spec)) 506 no_match_specs.append(spec) 507 continue 508 if nsvcap.stream or nsvcap.version or nsvcap.context or nsvcap.arch or nsvcap.profile: 509 logger.info(_("Only module name is required. " 510 "Ignoring unneeded information in argument: '{}'").format(spec)) 511 module_names = set() 512 for module in module_list: 513 module_names.add(module.getName()) 514 for name in module_names: 515 if to_state == STATE_UNKNOWN: 516 self.base._moduleContainer.reset(name) 517 if to_state == STATE_DISABLED: 518 self.base._moduleContainer.disable(name) 519 520 solver_errors = self._update_sack() 521 return no_match_specs, solver_errors 522 523 def _get_package_name_set_and_remove_profiles(self, module_list, nsvcap, remove=False): 524 package_name_set = set() 525 latest_module = self._get_latest(module_list) 526 installed_profiles_strings = set(self.base._moduleContainer.getInstalledProfiles( 527 latest_module.getName())) 528 if not installed_profiles_strings: 529 return set() 530 if nsvcap.profile: 531 profiles_set = latest_module.getProfiles(nsvcap.profile) 532 if not profiles_set: 533 return set() 534 for profile in profiles_set: 535 if profile.getName() in installed_profiles_strings: 536 if remove: 537 self.base._moduleContainer.uninstall(latest_module, profile.getName()) 538 package_name_set.update(profile.getContent()) 539 else: 540 for profile_string in installed_profiles_strings: 541 if remove: 542 self.base._moduleContainer.uninstall(latest_module, profile_string) 543 for profile in latest_module.getProfiles(profile_string): 544 package_name_set.update(profile.getContent()) 545 return package_name_set 546 547 def _get_info_profiles(self, module_specs): 548 output = set() 549 for module_spec in module_specs: 550 module_list, nsvcap = self._get_modules(module_spec) 551 if not module_list: 552 logger.info(_("Unable to resolve argument {}").format(module_spec)) 553 continue 554 555 if nsvcap.profile: 556 logger.info(_("Ignoring unnecessary profile: '{}/{}'").format( 557 nsvcap.name, nsvcap.profile)) 558 for module in module_list: 559 560 lines = OrderedDict() 561 lines["Name"] = module.getFullIdentifier() 562 563 for profile in sorted(module.getProfiles(), key=_profile_comparison_key): 564 lines[profile.getName()] = "\n".join( 565 [pkgName for pkgName in profile.getContent()]) 566 567 output.add(self._create_simple_table(lines).toString()) 568 return "\n\n".join(sorted(output)) 569 570 def _profile_report_formatter(self, modulePackage, default_profiles, enabled_str): 571 installed_profiles = self.base._moduleContainer.getInstalledProfiles( 572 modulePackage.getName()) 573 available_profiles = modulePackage.getProfiles() 574 profiles_str = "" 575 for profile in sorted(available_profiles, key=_profile_comparison_key): 576 profiles_str += "{}{}".format( 577 profile.getName(), " [d]" if profile.getName() in default_profiles else "") 578 profiles_str += " [i], " if profile.getName() in installed_profiles and enabled_str \ 579 else ", " 580 return profiles_str[:-2] 581 582 def _summary_report_formatter(self, summary): 583 return summary.strip().replace("\n", " ") 584 585 def _module_strs_formatter(self, modulePackage, markActive=False): 586 default_str = "" 587 enabled_str = "" 588 disabled_str = "" 589 if modulePackage.getStream() == self.base._moduleContainer.getDefaultStream( 590 modulePackage.getName()): 591 default_str = " [d]" 592 if self.base._moduleContainer.isEnabled(modulePackage): 593 if not default_str: 594 enabled_str = " " 595 enabled_str += "[e]" 596 elif self.base._moduleContainer.isDisabled(modulePackage): 597 if not default_str: 598 disabled_str = " " 599 disabled_str += "[x]" 600 if markActive and self.base._moduleContainer.isModuleActive(modulePackage): 601 if not default_str: 602 disabled_str = " " 603 disabled_str += "[a]" 604 return default_str, enabled_str, disabled_str 605 606 def _get_info(self, module_specs): 607 output = set() 608 for module_spec in module_specs: 609 module_list, nsvcap = self._get_modules(module_spec) 610 if not module_list: 611 logger.info(_("Unable to resolve argument {}").format(module_spec)) 612 continue 613 614 if nsvcap.profile: 615 logger.info(_("Ignoring unnecessary profile: '{}/{}'").format( 616 nsvcap.name, nsvcap.profile)) 617 for modulePackage in module_list: 618 default_str, enabled_str, disabled_str = self._module_strs_formatter( 619 modulePackage, markActive=True) 620 default_profiles = self.base._moduleContainer.getDefaultProfiles( 621 modulePackage.getName(), modulePackage.getStream()) 622 623 profiles_str = self._profile_report_formatter( 624 modulePackage, default_profiles, enabled_str) 625 626 lines = OrderedDict() 627 lines["Name"] = modulePackage.getName() 628 lines["Stream"] = modulePackage.getStream() + default_str + enabled_str + \ 629 disabled_str 630 lines["Version"] = modulePackage.getVersion() 631 lines["Context"] = modulePackage.getContext() 632 lines["Architecture"] = modulePackage.getArch() 633 lines["Profiles"] = profiles_str 634 lines["Default profiles"] = " ".join(default_profiles) 635 lines["Repo"] = modulePackage.getRepoID() 636 lines["Summary"] = modulePackage.getSummary() 637 lines["Description"] = modulePackage.getDescription() 638 req_set = set() 639 for req in modulePackage.getModuleDependencies(): 640 for require_dict in req.getRequires(): 641 for mod_require, stream in require_dict.items(): 642 req_set.add("{}:[{}]".format(mod_require, ",".join(stream))) 643 lines["Requires"] = "\n".join(sorted(req_set)) 644 demodularized = modulePackage.getDemodularizedRpms() 645 if demodularized: 646 lines["Demodularized rpms"] = "\n".join(demodularized) 647 lines["Artifacts"] = "\n".join(sorted(modulePackage.getArtifacts())) 648 output.add(self._create_simple_table(lines).toString()) 649 str_table = "\n\n".join(sorted(output)) 650 if str_table: 651 str_table += MODULE_INFO_TABLE_HINT 652 return str_table 653 654 @staticmethod 655 def _create_simple_table(lines): 656 table = libdnf.smartcols.Table() 657 table.enableNoheadings(True) 658 table.setColumnSeparator(" : ") 659 660 column_name = table.newColumn("Name") 661 column_value = table.newColumn("Value") 662 column_value.setWrap(True) 663 column_value.setSafechars("\n") 664 column_value.setNewlineWrapFunction() 665 666 for line_name, value in lines.items(): 667 if value is None: 668 value = "" 669 line = table.newLine() 670 line.getColumnCell(column_name).setData(line_name) 671 line.getColumnCell(column_value).setData(str(value)) 672 673 return table 674 675 def _get_full_info(self, module_specs): 676 output = set() 677 for module_spec in module_specs: 678 module_list, nsvcap = self._get_modules(module_spec) 679 if not module_list: 680 logger.info(_("Unable to resolve argument {}").format(module_spec)) 681 continue 682 683 if nsvcap.profile: 684 logger.info(_("Ignoring unnecessary profile: '{}/{}'").format( 685 nsvcap.name, nsvcap.profile)) 686 for modulePackage in module_list: 687 info = modulePackage.getYaml() 688 if info: 689 output.add(info) 690 output_string = "\n\n".join(sorted(output)) 691 return output_string 692 693 def _what_provides(self, rpm_specs): 694 output = set() 695 modulePackages = self.base._moduleContainer.getModulePackages() 696 baseQuery = self.base.sack.query().filterm(empty=True).apply() 697 getBestInitQuery = self.base.sack.query(flags=hawkey.IGNORE_MODULAR_EXCLUDES) 698 699 for spec in rpm_specs: 700 subj = dnf.subject.Subject(spec) 701 baseQuery = baseQuery.union(subj.get_best_query( 702 self.base.sack, with_nevra=True, with_provides=False, with_filenames=False, 703 query=getBestInitQuery)) 704 705 baseQuery.apply() 706 707 for modulePackage in modulePackages: 708 artifacts = modulePackage.getArtifacts() 709 if not artifacts: 710 continue 711 query = baseQuery.filter(nevra_strict=artifacts) 712 if query: 713 for pkg in query: 714 string_output = "" 715 profiles = [] 716 for profile in sorted(modulePackage.getProfiles(), key=_profile_comparison_key): 717 if pkg.name in profile.getContent(): 718 profiles.append(profile.getName()) 719 lines = OrderedDict() 720 lines["Module"] = modulePackage.getFullIdentifier() 721 lines["Profiles"] = " ".join(sorted(profiles)) 722 lines["Repo"] = modulePackage.getRepoID() 723 lines["Summary"] = modulePackage.getSummary() 724 725 table = self._create_simple_table(lines) 726 727 string_output += "{}\n".format(self.base.output.term.bold(str(pkg))) 728 string_output += "{}".format(table.toString()) 729 output.add(string_output) 730 731 return "\n\n".join(sorted(output)) 732 733 def _create_and_fill_table(self, latest): 734 table = libdnf.smartcols.Table() 735 table.setTermforce(libdnf.smartcols.Table.TermForce_AUTO) 736 table.enableMaxout(True) 737 column_name = table.newColumn("Name") 738 column_stream = table.newColumn("Stream") 739 column_profiles = table.newColumn("Profiles") 740 column_profiles.setWrap(True) 741 column_info = table.newColumn("Summary") 742 column_info.setWrap(True) 743 744 if not self.base.conf.verbose: 745 column_info.hidden = True 746 747 for latest_per_repo in latest: 748 for nameStreamArch in latest_per_repo: 749 if len(nameStreamArch) == 1: 750 modulePackage = nameStreamArch[0] 751 else: 752 active = [module for module in nameStreamArch 753 if self.base._moduleContainer.isModuleActive(module)] 754 if active: 755 modulePackage = active[0] 756 else: 757 modulePackage = nameStreamArch[0] 758 line = table.newLine() 759 default_str, enabled_str, disabled_str = self._module_strs_formatter( 760 modulePackage, markActive=False) 761 default_profiles = self.base._moduleContainer.getDefaultProfiles( 762 modulePackage.getName(), modulePackage.getStream()) 763 profiles_str = self._profile_report_formatter(modulePackage, default_profiles, 764 enabled_str) 765 line.getColumnCell(column_name).setData(modulePackage.getName()) 766 line.getColumnCell( 767 column_stream).setData( 768 modulePackage.getStream() + default_str + enabled_str + disabled_str) 769 line.getColumnCell(column_profiles).setData(profiles_str) 770 summary_str = self._summary_report_formatter(modulePackage.getSummary()) 771 line.getColumnCell(column_info).setData(summary_str) 772 773 return table 774 775 def _get_brief_description(self, module_specs, module_state): 776 modules = [] 777 if module_specs: 778 for spec in module_specs: 779 module_list, nsvcap = self._get_modules(spec) 780 modules.extend(module_list) 781 else: 782 modules = self.base._moduleContainer.getModulePackages() 783 latest = self.base._moduleContainer.getLatestModulesPerRepo(module_state, modules) 784 if not latest: 785 return "" 786 787 table = self._create_and_fill_table(latest) 788 current_repo_id_index = 0 789 already_printed_lines = 0 790 try: 791 repo_name = self.base.repos[latest[0][0][0].getRepoID()].name 792 except KeyError: 793 repo_name = latest[0][0][0].getRepoID() 794 versions = len(latest[0]) 795 header = self._format_header(table) 796 str_table = self._format_repoid(repo_name) 797 str_table += header 798 for i in range(0, table.getNumberOfLines()): 799 if versions + already_printed_lines <= i: 800 already_printed_lines += versions 801 current_repo_id_index += 1 802 # Fail-Safe repository is not in self.base.repos 803 try: 804 repo_name = self.base.repos[ 805 latest[current_repo_id_index][0][0].getRepoID()].name 806 except KeyError: 807 repo_name = latest[current_repo_id_index][0][0].getRepoID() 808 versions = len(latest[current_repo_id_index]) 809 str_table += "\n" 810 str_table += self._format_repoid(repo_name) 811 str_table += header 812 813 line = table.getLine(i) 814 str_table += table.toString(line, line) 815 return str_table + MODULE_TABLE_HINT 816 817 def _format_header(self, table): 818 line = table.getLine(0) 819 return table.toString(line, line).split('\n', 1)[0] + '\n' 820 821 def _format_repoid(self, repo_name): 822 return "{}\n".format(self.base.output.term.bold(repo_name)) 823 824 def _install_profiles_internal(self, install_set_artifacts, install_dict, strict): 825 # Remove source packages because they cannot be installed or upgraded 826 base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() 827 install_base_query = base_no_source_query.filter(nevra_strict=install_set_artifacts) 828 error_specs = [] 829 830 # add hot-fix packages 831 hot_fix_repos = [i.id for i in self.base.repos.iter_enabled() if i.module_hotfixes] 832 hotfix_packages = base_no_source_query.filter( 833 reponame=hot_fix_repos, name=install_dict.keys()) 834 install_base_query = install_base_query.union(hotfix_packages) 835 836 for pkg_name, set_specs in install_dict.items(): 837 query = install_base_query.filter(name=pkg_name) 838 if not query: 839 # package can also be non-modular or part of another stream 840 query = base_no_source_query.filter(name=pkg_name) 841 if not query: 842 for spec in set_specs: 843 logger.error(_("Unable to resolve argument {}").format(spec)) 844 logger.error(_("No match for package {}").format(pkg_name)) 845 error_specs.extend(set_specs) 846 continue 847 self.base._goal.group_members.add(pkg_name) 848 sltr = dnf.selector.Selector(self.base.sack) 849 sltr.set(pkg=query) 850 self.base._goal.install(select=sltr, optional=(not strict)) 851 return install_base_query, error_specs 852 853 854def format_modular_solver_errors(errors): 855 msg = dnf.util._format_resolve_problems(errors) 856 return "\n".join( 857 [P_('Modular dependency problem:', 'Modular dependency problems:', len(errors)), msg]) 858