patchman 4.0.8__tar.gz → 4.0.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patchman-4.0.8 → patchman-4.0.10}/PKG-INFO +1 -1
- patchman-4.0.10/VERSION.txt +1 -0
- patchman-4.0.10/arch/migrations/0002_alter_machinearchitecture_options_and_more.py +21 -0
- patchman-4.0.10/domains/migrations/0002_alter_domain_options.py +17 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/apps.py +16 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/managers.py +1 -1
- patchman-4.0.10/errata/migrations/0008_add_cached_count_fields.py +38 -0
- patchman-4.0.10/errata/migrations/0009_backfill_cached_counts.py +45 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/models.py +8 -3
- patchman-4.0.10/errata/signals.py +60 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/tables.py +5 -5
- {patchman-4.0.8 → patchman-4.0.10}/errata/templates/errata/erratum_detail.html +6 -4
- patchman-4.0.10/errata/tests/test_models.py +241 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/views.py +1 -1
- {patchman-4.0.8 → patchman-4.0.10}/hosts/apps.py +3 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/managers.py +1 -1
- patchman-4.0.10/hosts/migrations/0010_alter_hostrepo_options.py +17 -0
- patchman-4.0.10/hosts/migrations/0011_host_bug_updates_count_host_errata_count_and_more.py +33 -0
- patchman-4.0.10/hosts/migrations/0012_backfill_cached_counts.py +33 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/models.py +18 -10
- {patchman-4.0.8 → patchman-4.0.10}/hosts/serializers.py +2 -2
- patchman-4.0.10/hosts/signals.py +45 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/tables.py +1 -1
- patchman-4.0.10/hosts/tasks.py +43 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/utils.py +46 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/views.py +10 -14
- {patchman-4.0.8 → patchman-4.0.10}/modules/managers.py +1 -1
- {patchman-4.0.8 → patchman-4.0.10}/modules/views.py +2 -2
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/apps.py +3 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/forms.py +1 -1
- patchman-4.0.10/operatingsystems/migrations/0009_osvariant_hosts_count.py +18 -0
- patchman-4.0.10/operatingsystems/migrations/0010_backfill_osvariant_counts.py +29 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/models.py +2 -0
- patchman-4.0.10/operatingsystems/signals.py +56 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/templates/operatingsystems/osrelease_detail.html +6 -4
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/templates/operatingsystems/osvariant_delete_multiple.html +1 -1
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/templates/operatingsystems/osvariant_detail.html +1 -1
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/views.py +8 -8
- {patchman-4.0.8 → patchman-4.0.10}/packages/managers.py +1 -1
- patchman-4.0.10/packages/migrations/0006_alter_packageupdate_options.py +17 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/utils.py +41 -29
- {patchman-4.0.8 → patchman-4.0.10}/packages/views.py +5 -5
- {patchman-4.0.8 → patchman-4.0.10}/patchman/settings.py +1 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman.egg-info/PKG-INFO +1 -1
- {patchman-4.0.8 → patchman-4.0.10}/patchman.egg-info/SOURCES.txt +18 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/views.py +12 -24
- {patchman-4.0.8 → patchman-4.0.10}/repos/apps.py +3 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/forms.py +2 -2
- {patchman-4.0.8 → patchman-4.0.10}/repos/managers.py +1 -1
- patchman-4.0.10/repos/migrations/0007_alter_mirror_options_alter_mirrorpackage_options_and_more.py +25 -0
- patchman-4.0.10/repos/migrations/0008_mirror_packages_count.py +18 -0
- patchman-4.0.10/repos/migrations/0009_backfill_mirror_counts.py +27 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/models.py +10 -16
- patchman-4.0.10/repos/signals.py +28 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/tables.py +1 -1
- {patchman-4.0.8 → patchman-4.0.10}/repos/utils.py +3 -2
- {patchman-4.0.8 → patchman-4.0.10}/repos/views.py +14 -18
- {patchman-4.0.8 → patchman-4.0.10}/sbin/patchman +3 -50
- {patchman-4.0.8 → patchman-4.0.10}/scripts/rpm-post-install.sh +0 -1
- {patchman-4.0.8 → patchman-4.0.10}/security/managers.py +1 -1
- patchman-4.0.10/security/migrations/0008_alter_cwe_options_alter_reference_options.py +21 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/tasks.py +2 -2
- {patchman-4.0.8 → patchman-4.0.10}/security/utils.py +2 -2
- {patchman-4.0.8 → patchman-4.0.10}/security/views.py +3 -3
- {patchman-4.0.8 → patchman-4.0.10}/util/__init__.py +1 -1
- {patchman-4.0.8 → patchman-4.0.10}/util/context_processors.py +2 -2
- {patchman-4.0.8 → patchman-4.0.10}/util/management/commands/revoke_api_key.py +1 -1
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/dashboard.html +6 -2
- {patchman-4.0.8 → patchman-4.0.10}/util/templatetags/common.py +3 -6
- {patchman-4.0.8 → patchman-4.0.10}/util/views.py +33 -8
- patchman-4.0.8/VERSION.txt +0 -1
- patchman-4.0.8/errata/tests/test_models.py +0 -122
- patchman-4.0.8/hosts/tasks.py +0 -80
- {patchman-4.0.8 → patchman-4.0.10}/AUTHORS +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/COPYING +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/INSTALL.md +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/MANIFEST.in +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/README.md +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/apps.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/tests/test_models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/utils.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/arch/views.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/apps.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/tests/test_models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/domains/views.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/migrations/0002_alter_erratumreference_unique_together.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/migrations/0003_delete_erratumreference_alter_erratum_references.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/migrations/0004_rename_packages_erratum_fixed_packages.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/migrations/0005_erratum_affected_packages_and_more.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/migrations/0006_alter_erratum_options.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/migrations/0007_alter_erratum_fixed_packages.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/distros/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/distros/alma.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/distros/arch.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/distros/centos.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/distros/debian.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/distros/rocky.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/distros/ubuntu.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/repos/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/sources/repos/yum.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/tasks.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/templates/errata/erratum_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/tests/test_integration.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/errata/utils.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/etc/patchman/apache.conf.example +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/etc/patchman/celery.conf +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/etc/patchman/local_settings.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/etc/systemd/system/patchman-celery-beat.service +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/etc/systemd/system/patchman-celery-worker.service +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/forms.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0002_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0003_host_modules.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0004_remove_host_tags_host_tags.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0005_rename_os_host_osvariant.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0006_migrate_to_tz_aware.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0007_alter_host_tags.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0008_alter_host_options.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/0009_host_errata.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/templates/hosts/host_delete.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/templates/hosts/host_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/templates/hosts/host_edit.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/templates/hosts/host_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/templatetags/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/templatetags/report_alert.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/tests/test_find_updates.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/tests/test_managers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/tests/test_models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/hosts/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/apps.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/migrations/0002_auto_20240204_2214.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/migrations/0003_alter_module_options.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/migrations/0004_alter_module_options.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/migrations/0005_alter_module_unique_together.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/tables.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/templates/modules/module_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/templates/modules/module_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/modules/utils.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/fixtures/os.json +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/fixtures/osgroup.json +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/managers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0002_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0003_os_arch.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0003_osgroup_codename.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0004_alter_osgroup_unique_together.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0005_rename_osgroup_osrelease_rename_os_osvariant_and_more.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0006_osrelease_cpe_name_osvariant_codename.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0007_alter_osrelease_unique_together.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/0008_alter_osrelease_options_alter_osvariant_options.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/tables.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/templates/operatingsystems/os_landing.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/templates/operatingsystems/osrelease_delete.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/templates/operatingsystems/osrelease_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/templates/operatingsystems/osvariant_delete.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/templates/operatingsystems/osvariant_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/tests/test_models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/operatingsystems/utils.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/apps.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/migrations/0002_auto_20250207_1319.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/migrations/0002_delete_erratum_delete_erratumreference.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/migrations/0003_auto_20250207_1746.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/migrations/0004_alter_package_options_alter_packagecategory_options_and_more.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/migrations/0005_alter_package_packagetype.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/tables.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/templates/packages/package_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/templates/packages/package_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/templates/packages/package_name_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/templates/packages/package_name_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/tests/test_models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/tests/test_version_compare.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/packages/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/celery.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/receivers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/signals.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/sqlite3/base.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/static/css/base.css +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/static/js/expandable-text.js +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/static/js/sidebar.js +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman/wsgi.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman.egg-info/dependency_links.txt +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman.egg-info/requires.txt +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/patchman.egg-info/top_level.txt +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/apps.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/migrations/0002_report_modules.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/migrations/0003_remove_report_accessed.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/migrations/0004_migrate_to_tz_aware.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/migrations/0005_alter_report_options.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tables.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tasks.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/templates/reports/report.txt +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/templates/reports/report_delete.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/templates/reports/report_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/templates/reports/report_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/test_edge_cases.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/test_integration.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/test_models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/test_parsing.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/test_serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/test_tasks.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/tests/test_utils.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/reports/utils.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/migrations/0002_alter_repository_repotype.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/migrations/0003_migrate_to_tz_aware.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/migrations/0004_rename_file_checksum_mirror_package_checksum.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/migrations/0005_rename_package_checksum_mirror_packages_checksum.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/migrations/0006_mirror_errata_checksum_mirror_modules_checksum.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/repo_types/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/repo_types/arch.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/repo_types/deb.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/repo_types/gentoo.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/repo_types/rpm.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/repo_types/yast.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/repo_types/yum.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/tasks.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/mirror_delete.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/mirror_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/mirror_edit.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/mirror_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/mirror_with_repo_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/repo_delete.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/repo_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/repo_edit.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templates/repos/repo_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/templatetags/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/tests/test_managers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/tests/test_mirror_sync.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/tests/test_models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/repos/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/requirements.txt +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/sbin/patchman-manage +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/sbin/patchman-set-secret-key +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/scripts/clear-django-logs.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/scripts/create_graph.sh +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/scripts/rpm-install.sh +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/admin.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/apps.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/migrations/0001_initial.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/migrations/0002_alter_cve_options.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/migrations/0003_alter_cve_description_alter_cwe_description.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/migrations/0004_alter_cve_options.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/migrations/0005_reference_cve_references.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/migrations/0006_alter_cve_options_alter_cvss_unique_together.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/migrations/0007_remove_cve_title.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/migrations/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/serializers.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/tables.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/templates/security/cve_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/templates/security/cve_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/templates/security/cwe_detail.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/templates/security/cwe_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/templates/security/reference_list.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/templates/security/security_landing.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/tests/test_api.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/tests/test_models.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/security/urls.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/setup.cfg +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/setup.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/apps.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/filterspecs.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/logging.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/management/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/management/commands/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/management/commands/create_api_key.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/management/commands/list_api_keys.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/tables.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/tasks.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/404.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/500.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/base.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/bulk_actions.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/navbar.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/objectlist.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/registration/login.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/searchbar.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templates/table.html +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/templatetags/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/tests/__init__.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/tests/test_commands.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/tests/test_utils.py +0 -0
- {patchman-4.0.8 → patchman-4.0.10}/util/urls.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.0.10
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Generated by Django 4.2.28 on 2026-02-11 04:55
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('arch', '0001_initial'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterModelOptions(
|
|
14
|
+
name='machinearchitecture',
|
|
15
|
+
options={'ordering': ['name'], 'verbose_name': 'Machine Architecture'},
|
|
16
|
+
),
|
|
17
|
+
migrations.AlterModelOptions(
|
|
18
|
+
name='packagearchitecture',
|
|
19
|
+
options={'ordering': ['name'], 'verbose_name': 'Package Architecture'},
|
|
20
|
+
),
|
|
21
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated by Django 4.2.28 on 2026-02-11 04:55
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('domains', '0001_initial'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterModelOptions(
|
|
14
|
+
name='domain',
|
|
15
|
+
options={'ordering': ['name']},
|
|
16
|
+
),
|
|
17
|
+
]
|
|
@@ -19,3 +19,19 @@ from django.apps import AppConfig
|
|
|
19
19
|
|
|
20
20
|
class ErrataConfig(AppConfig):
|
|
21
21
|
name = 'errata'
|
|
22
|
+
|
|
23
|
+
def ready(self):
|
|
24
|
+
from datetime import timedelta
|
|
25
|
+
|
|
26
|
+
from django.db.models.signals import post_save
|
|
27
|
+
from django.utils import timezone
|
|
28
|
+
|
|
29
|
+
import errata.signals # noqa: F401
|
|
30
|
+
|
|
31
|
+
def set_initial_last_run(sender, instance, created, **kwargs):
|
|
32
|
+
if created and instance.name == 'update_errata_cves_cwes_every_12_hours':
|
|
33
|
+
instance.last_run_at = timezone.now() - timedelta(days=1)
|
|
34
|
+
instance.save(update_fields=['last_run_at'])
|
|
35
|
+
|
|
36
|
+
from django_celery_beat.models import PeriodicTask
|
|
37
|
+
post_save.connect(set_initial_last_run, sender=PeriodicTask)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Generated by Django 4.2.28 on 2026-02-13 06:44
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('errata', '0007_alter_erratum_fixed_packages'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='erratum',
|
|
15
|
+
name='affected_packages_count',
|
|
16
|
+
field=models.PositiveIntegerField(default=0),
|
|
17
|
+
),
|
|
18
|
+
migrations.AddField(
|
|
19
|
+
model_name='erratum',
|
|
20
|
+
name='cves_count',
|
|
21
|
+
field=models.PositiveIntegerField(default=0),
|
|
22
|
+
),
|
|
23
|
+
migrations.AddField(
|
|
24
|
+
model_name='erratum',
|
|
25
|
+
name='fixed_packages_count',
|
|
26
|
+
field=models.PositiveIntegerField(default=0),
|
|
27
|
+
),
|
|
28
|
+
migrations.AddField(
|
|
29
|
+
model_name='erratum',
|
|
30
|
+
name='osreleases_count',
|
|
31
|
+
field=models.PositiveIntegerField(default=0),
|
|
32
|
+
),
|
|
33
|
+
migrations.AddField(
|
|
34
|
+
model_name='erratum',
|
|
35
|
+
name='references_count',
|
|
36
|
+
field=models.PositiveIntegerField(default=0),
|
|
37
|
+
),
|
|
38
|
+
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright 2026 Marcus Furlong <furlongm@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# This file is part of Patchman.
|
|
4
|
+
#
|
|
5
|
+
# Patchman is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
|
7
|
+
# the Free Software Foundation, version 3 only.
|
|
8
|
+
#
|
|
9
|
+
# Patchman is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with Patchman. If not, see <http://www.gnu.org/licenses/>
|
|
16
|
+
|
|
17
|
+
from django.db import migrations
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def backfill_counts(apps, schema_editor):
|
|
21
|
+
Erratum = apps.get_model('errata', 'Erratum')
|
|
22
|
+
for erratum in Erratum.objects.all().iterator():
|
|
23
|
+
erratum.affected_packages_count = erratum.affected_packages.count()
|
|
24
|
+
erratum.fixed_packages_count = erratum.fixed_packages.count()
|
|
25
|
+
erratum.osreleases_count = erratum.osreleases.count()
|
|
26
|
+
erratum.cves_count = erratum.cves.count()
|
|
27
|
+
erratum.references_count = erratum.references.count()
|
|
28
|
+
erratum.save(update_fields=[
|
|
29
|
+
'affected_packages_count',
|
|
30
|
+
'fixed_packages_count',
|
|
31
|
+
'osreleases_count',
|
|
32
|
+
'cves_count',
|
|
33
|
+
'references_count',
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Migration(migrations.Migration):
|
|
38
|
+
|
|
39
|
+
dependencies = [
|
|
40
|
+
('errata', '0008_add_cached_count_fields'),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
operations = [
|
|
44
|
+
migrations.RunPython(backfill_counts, migrations.RunPython.noop),
|
|
45
|
+
]
|
|
@@ -40,6 +40,11 @@ class Erratum(models.Model):
|
|
|
40
40
|
osreleases = models.ManyToManyField(OSRelease, blank=True)
|
|
41
41
|
cves = models.ManyToManyField(CVE, blank=True)
|
|
42
42
|
references = models.ManyToManyField(Reference, blank=True)
|
|
43
|
+
affected_packages_count = models.PositiveIntegerField(default=0)
|
|
44
|
+
fixed_packages_count = models.PositiveIntegerField(default=0)
|
|
45
|
+
osreleases_count = models.PositiveIntegerField(default=0)
|
|
46
|
+
cves_count = models.PositiveIntegerField(default=0)
|
|
47
|
+
references_count = models.PositiveIntegerField(default=0)
|
|
43
48
|
|
|
44
49
|
objects = ErratumManager()
|
|
45
50
|
|
|
@@ -49,9 +54,9 @@ class Erratum(models.Model):
|
|
|
49
54
|
ordering = ['-issue_date', 'name']
|
|
50
55
|
|
|
51
56
|
def __str__(self):
|
|
52
|
-
text = f'{self.name} ({self.e_type}), {self.
|
|
53
|
-
text += f'affecting {self.
|
|
54
|
-
text += f'providing {self.
|
|
57
|
+
text = f'{self.name} ({self.e_type}), {self.cves_count} related CVEs, '
|
|
58
|
+
text += f'affecting {self.osreleases_count} OS Releases, '
|
|
59
|
+
text += f'providing {self.fixed_packages_count} fixed Packages'
|
|
55
60
|
return text
|
|
56
61
|
|
|
57
62
|
def get_absolute_url(self):
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Copyright 2026 Marcus Furlong <furlongm@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# This file is part of Patchman.
|
|
4
|
+
#
|
|
5
|
+
# Patchman is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
|
7
|
+
# the Free Software Foundation, version 3 only.
|
|
8
|
+
#
|
|
9
|
+
# Patchman is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with Patchman. If not, see <http://www.gnu.org/licenses/>
|
|
16
|
+
|
|
17
|
+
from django.db.models.signals import m2m_changed
|
|
18
|
+
from django.dispatch import receiver
|
|
19
|
+
|
|
20
|
+
from errata.models import Erratum
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@receiver(m2m_changed, sender=Erratum.affected_packages.through)
|
|
24
|
+
def update_affected_packages_count(sender, instance, action, **kwargs):
|
|
25
|
+
"""Update affected_packages_count when Erratum.affected_packages M2M changes."""
|
|
26
|
+
if action in ('post_add', 'post_remove', 'post_clear'):
|
|
27
|
+
instance.affected_packages_count = instance.affected_packages.count()
|
|
28
|
+
instance.save(update_fields=['affected_packages_count'])
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@receiver(m2m_changed, sender=Erratum.fixed_packages.through)
|
|
32
|
+
def update_fixed_packages_count(sender, instance, action, **kwargs):
|
|
33
|
+
"""Update fixed_packages_count when Erratum.fixed_packages M2M changes."""
|
|
34
|
+
if action in ('post_add', 'post_remove', 'post_clear'):
|
|
35
|
+
instance.fixed_packages_count = instance.fixed_packages.count()
|
|
36
|
+
instance.save(update_fields=['fixed_packages_count'])
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@receiver(m2m_changed, sender=Erratum.osreleases.through)
|
|
40
|
+
def update_osreleases_count(sender, instance, action, **kwargs):
|
|
41
|
+
"""Update osreleases_count when Erratum.osreleases M2M changes."""
|
|
42
|
+
if action in ('post_add', 'post_remove', 'post_clear'):
|
|
43
|
+
instance.osreleases_count = instance.osreleases.count()
|
|
44
|
+
instance.save(update_fields=['osreleases_count'])
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@receiver(m2m_changed, sender=Erratum.cves.through)
|
|
48
|
+
def update_cves_count(sender, instance, action, **kwargs):
|
|
49
|
+
"""Update cves_count when Erratum.cves M2M changes."""
|
|
50
|
+
if action in ('post_add', 'post_remove', 'post_clear'):
|
|
51
|
+
instance.cves_count = instance.cves.count()
|
|
52
|
+
instance.save(update_fields=['cves_count'])
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@receiver(m2m_changed, sender=Erratum.references.through)
|
|
56
|
+
def update_references_count(sender, instance, action, **kwargs):
|
|
57
|
+
"""Update references_count when Erratum.references M2M changes."""
|
|
58
|
+
if action in ('post_add', 'post_remove', 'post_clear'):
|
|
59
|
+
instance.references_count = instance.references.count()
|
|
60
|
+
instance.save(update_fields=['references_count'])
|
|
@@ -19,31 +19,31 @@ from util.tables import BaseTable
|
|
|
19
19
|
|
|
20
20
|
ERRATUM_NAME_TEMPLATE = '<a href="{{ record.get_absolute_url }}">{{ record.name }}</a>'
|
|
21
21
|
PACKAGES_AFFECTED_TEMPLATE = (
|
|
22
|
-
'{% with count=record.
|
|
22
|
+
'{% with count=record.affected_packages_count %}'
|
|
23
23
|
'{% if count != 0 %}'
|
|
24
24
|
'<a href="{% url \'packages:package_list\' %}?erratum_id={{ record.id }}&type=affected">{{ count }}</a>'
|
|
25
25
|
'{% else %}{% endif %}{% endwith %}'
|
|
26
26
|
)
|
|
27
27
|
PACKAGES_FIXED_TEMPLATE = (
|
|
28
|
-
'{% with count=record.
|
|
28
|
+
'{% with count=record.fixed_packages_count %}'
|
|
29
29
|
'{% if count != 0 %}'
|
|
30
30
|
'<a href="{% url \'packages:package_list\' %}?erratum_id={{ record.id }}&type=fixed">{{ count }}</a>'
|
|
31
31
|
'{% else %}{% endif %}{% endwith %}'
|
|
32
32
|
)
|
|
33
33
|
OSRELEASES_TEMPLATE = (
|
|
34
|
-
'{% with count=record.
|
|
34
|
+
'{% with count=record.osreleases_count %}'
|
|
35
35
|
'{% if count != 0 %}'
|
|
36
36
|
'<a href="{% url \'operatingsystems:osrelease_list\' %}?erratum_id={{ record.id }}">{{ count }}</a>'
|
|
37
37
|
'{% else %}{% endif %}{% endwith %}'
|
|
38
38
|
)
|
|
39
39
|
ERRATUM_CVES_TEMPLATE = (
|
|
40
|
-
'{% with count=record.
|
|
40
|
+
'{% with count=record.cves_count %}'
|
|
41
41
|
'{% if count != 0 %}'
|
|
42
42
|
'<a href="{% url \'security:cve_list\' %}?erratum_id={{ record.id }}">{{ count }}</a>'
|
|
43
43
|
'{% else %}{% endif %}{% endwith %}'
|
|
44
44
|
)
|
|
45
45
|
REFERENCES_TEMPLATE = (
|
|
46
|
-
'{% with count=record.
|
|
46
|
+
'{% with count=record.references_count %}'
|
|
47
47
|
'{% if count != 0 %}'
|
|
48
48
|
'<a href="{% url \'security:reference_list\' %}?erratum_id={{ record.id }}">{{ count }}</a>'
|
|
49
49
|
'{% else %}{% endif %}{% endwith %}'
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
{% block content %}
|
|
10
10
|
|
|
11
|
+
{% with affected_count=erratum.affected_packages_count fixed_count=erratum.fixed_packages_count %}
|
|
11
12
|
<ul class="nav nav-tabs">
|
|
12
13
|
<li class="active"><a data-toggle="tab" href="#erratum_details">Details</a></li>
|
|
13
|
-
<li><a data-toggle="tab" href="#erratum_affected_packages">Packages Affected ({{
|
|
14
|
-
<li><a data-toggle="tab" href="#erratum_fixed_packages">Packages Fixed ({{
|
|
14
|
+
<li><a data-toggle="tab" href="#erratum_affected_packages">Packages Affected ({{ affected_count }})</a></li>
|
|
15
|
+
<li><a data-toggle="tab" href="#erratum_fixed_packages">Packages Fixed ({{ fixed_count }})</a></li>
|
|
15
16
|
</ul>
|
|
16
17
|
|
|
17
18
|
<div class="tab-content">
|
|
@@ -22,8 +23,8 @@
|
|
|
22
23
|
<tr><th class="col-sm-1">Type</th><td> {{ erratum.e_type }} </td></tr>
|
|
23
24
|
<tr><th class="col-sm-1">Published Date</th><td>{{ erratum.issue_date|date|default_if_none:'' }}</td></tr>
|
|
24
25
|
<tr><th class="col-sm-1">Synopsis</th><td> {{ erratum.synopsis }} </td></tr>
|
|
25
|
-
<tr><th class="col-sm-1">Packages Affected</th><td><a href="{% url 'packages:package_list' %}?erratum_id={{ erratum.id }}&type=affected"> {{
|
|
26
|
-
<tr><th class="col-sm-1">Packages Fixed</th><td><a href="{% url 'packages:package_list' %}?erratum_id={{ erratum.id }}&type=fixed"> {{
|
|
26
|
+
<tr><th class="col-sm-1">Packages Affected</th><td><a href="{% url 'packages:package_list' %}?erratum_id={{ erratum.id }}&type=affected"> {{ affected_count }} </a></td></tr>
|
|
27
|
+
<tr><th class="col-sm-1">Packages Fixed</th><td><a href="{% url 'packages:package_list' %}?erratum_id={{ erratum.id }}&type=fixed"> {{ fixed_count }} </a></td></tr>
|
|
27
28
|
<tr>
|
|
28
29
|
<th class="col-sm-2">OS Releases Affected</th>
|
|
29
30
|
<td>
|
|
@@ -78,5 +79,6 @@
|
|
|
78
79
|
</div>
|
|
79
80
|
</div>
|
|
80
81
|
</div>
|
|
82
|
+
{% endwith %}
|
|
81
83
|
|
|
82
84
|
{% endblock %}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Copyright 2025 Marcus Furlong <furlongm@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# This file is part of Patchman.
|
|
4
|
+
#
|
|
5
|
+
# Patchman is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
|
7
|
+
# the Free Software Foundation, version 3 only.
|
|
8
|
+
#
|
|
9
|
+
# Patchman is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with Patchman. If not, see <http://www.gnu.org/licenses/>
|
|
16
|
+
|
|
17
|
+
from django.test import TestCase, override_settings
|
|
18
|
+
from django.utils import timezone
|
|
19
|
+
|
|
20
|
+
from errata.models import Erratum
|
|
21
|
+
from operatingsystems.models import OSRelease
|
|
22
|
+
from security.models import CVE, Reference
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@override_settings(
|
|
26
|
+
CELERY_TASK_ALWAYS_EAGER=True,
|
|
27
|
+
CACHES={'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}
|
|
28
|
+
)
|
|
29
|
+
class ErratumMethodTests(TestCase):
|
|
30
|
+
"""Tests for Erratum model methods."""
|
|
31
|
+
|
|
32
|
+
def test_erratum_creation(self):
|
|
33
|
+
"""Test creating an Erratum."""
|
|
34
|
+
erratum = Erratum.objects.create(
|
|
35
|
+
name='USN-1234-1',
|
|
36
|
+
e_type='security',
|
|
37
|
+
synopsis='Security update',
|
|
38
|
+
issue_date=timezone.now(),
|
|
39
|
+
)
|
|
40
|
+
self.assertEqual(erratum.name, 'USN-1234-1')
|
|
41
|
+
|
|
42
|
+
def test_erratum_str(self):
|
|
43
|
+
"""Test Erratum __str__ method."""
|
|
44
|
+
erratum = Erratum.objects.create(
|
|
45
|
+
name='USN-1234-1',
|
|
46
|
+
e_type='security',
|
|
47
|
+
synopsis='Security update',
|
|
48
|
+
issue_date=timezone.now(),
|
|
49
|
+
)
|
|
50
|
+
self.assertIn('USN-1234-1', str(erratum))
|
|
51
|
+
|
|
52
|
+
def test_erratum_get_absolute_url(self):
|
|
53
|
+
"""Test Erratum.get_absolute_url()."""
|
|
54
|
+
erratum = Erratum.objects.create(
|
|
55
|
+
name='USN-1234-1',
|
|
56
|
+
e_type='security',
|
|
57
|
+
synopsis='Security update',
|
|
58
|
+
issue_date=timezone.now(),
|
|
59
|
+
)
|
|
60
|
+
url = erratum.get_absolute_url()
|
|
61
|
+
self.assertIn(erratum.name, url)
|
|
62
|
+
|
|
63
|
+
def test_erratum_unique_name(self):
|
|
64
|
+
"""Test Erratum name is unique."""
|
|
65
|
+
Erratum.objects.create(
|
|
66
|
+
name='USN-1234-1',
|
|
67
|
+
e_type='security',
|
|
68
|
+
synopsis='Security update',
|
|
69
|
+
issue_date=timezone.now(),
|
|
70
|
+
)
|
|
71
|
+
from django.db import IntegrityError
|
|
72
|
+
with self.assertRaises(IntegrityError):
|
|
73
|
+
Erratum.objects.create(
|
|
74
|
+
name='USN-1234-1',
|
|
75
|
+
e_type='bugfix',
|
|
76
|
+
synopsis='Bugfix update',
|
|
77
|
+
issue_date=timezone.now(),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def test_erratum_with_cves(self):
|
|
81
|
+
"""Test Erratum with associated CVEs."""
|
|
82
|
+
erratum = Erratum.objects.create(
|
|
83
|
+
name='USN-1234-1',
|
|
84
|
+
e_type='security',
|
|
85
|
+
synopsis='Security update',
|
|
86
|
+
issue_date=timezone.now(),
|
|
87
|
+
)
|
|
88
|
+
cve = CVE.objects.create(cve_id='CVE-2024-12345')
|
|
89
|
+
erratum.cves.add(cve)
|
|
90
|
+
self.assertIn(cve, erratum.cves.all())
|
|
91
|
+
|
|
92
|
+
def test_erratum_with_osreleases(self):
|
|
93
|
+
"""Test Erratum with associated OS releases."""
|
|
94
|
+
erratum = Erratum.objects.create(
|
|
95
|
+
name='USN-1234-1',
|
|
96
|
+
e_type='security',
|
|
97
|
+
synopsis='Security update',
|
|
98
|
+
issue_date=timezone.now(),
|
|
99
|
+
)
|
|
100
|
+
release = OSRelease.objects.create(name='Ubuntu 22.04')
|
|
101
|
+
erratum.osreleases.add(release)
|
|
102
|
+
self.assertIn(release, erratum.osreleases.all())
|
|
103
|
+
|
|
104
|
+
def test_security_erratum(self):
|
|
105
|
+
"""Test creating a security erratum."""
|
|
106
|
+
erratum = Erratum.objects.create(
|
|
107
|
+
name='RHSA-2024:1234',
|
|
108
|
+
e_type='security',
|
|
109
|
+
synopsis='Important security update',
|
|
110
|
+
issue_date=timezone.now(),
|
|
111
|
+
)
|
|
112
|
+
self.assertEqual(erratum.e_type, 'security')
|
|
113
|
+
|
|
114
|
+
def test_bugfix_erratum(self):
|
|
115
|
+
"""Test creating a bugfix erratum."""
|
|
116
|
+
erratum = Erratum.objects.create(
|
|
117
|
+
name='RHBA-2024:1234',
|
|
118
|
+
e_type='bugfix',
|
|
119
|
+
synopsis='Bug fix update',
|
|
120
|
+
issue_date=timezone.now(),
|
|
121
|
+
)
|
|
122
|
+
self.assertEqual(erratum.e_type, 'bugfix')
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@override_settings(
|
|
126
|
+
CELERY_TASK_ALWAYS_EAGER=True,
|
|
127
|
+
CACHES={'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}
|
|
128
|
+
)
|
|
129
|
+
class ErratumCachedCountTests(TestCase):
|
|
130
|
+
"""Tests for Erratum cached count fields and M2M signals."""
|
|
131
|
+
|
|
132
|
+
def setUp(self):
|
|
133
|
+
self.erratum = Erratum.objects.create(
|
|
134
|
+
name='USN-5678-1',
|
|
135
|
+
e_type='security',
|
|
136
|
+
synopsis='Security update',
|
|
137
|
+
issue_date=timezone.now(),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def test_initial_counts_are_zero(self):
|
|
141
|
+
"""Test that cached counts start at zero."""
|
|
142
|
+
self.assertEqual(self.erratum.cves_count, 0)
|
|
143
|
+
self.assertEqual(self.erratum.osreleases_count, 0)
|
|
144
|
+
self.assertEqual(self.erratum.affected_packages_count, 0)
|
|
145
|
+
self.assertEqual(self.erratum.fixed_packages_count, 0)
|
|
146
|
+
self.assertEqual(self.erratum.references_count, 0)
|
|
147
|
+
|
|
148
|
+
def test_cves_count_on_add(self):
|
|
149
|
+
"""Test cves_count increments on add."""
|
|
150
|
+
cve1 = CVE.objects.create(cve_id='CVE-2024-0001')
|
|
151
|
+
cve2 = CVE.objects.create(cve_id='CVE-2024-0002')
|
|
152
|
+
self.erratum.cves.add(cve1)
|
|
153
|
+
self.erratum.refresh_from_db()
|
|
154
|
+
self.assertEqual(self.erratum.cves_count, 1)
|
|
155
|
+
self.erratum.cves.add(cve2)
|
|
156
|
+
self.erratum.refresh_from_db()
|
|
157
|
+
self.assertEqual(self.erratum.cves_count, 2)
|
|
158
|
+
|
|
159
|
+
def test_cves_count_on_remove(self):
|
|
160
|
+
"""Test cves_count decrements on remove."""
|
|
161
|
+
cve = CVE.objects.create(cve_id='CVE-2024-0003')
|
|
162
|
+
self.erratum.cves.add(cve)
|
|
163
|
+
self.erratum.refresh_from_db()
|
|
164
|
+
self.assertEqual(self.erratum.cves_count, 1)
|
|
165
|
+
self.erratum.cves.remove(cve)
|
|
166
|
+
self.erratum.refresh_from_db()
|
|
167
|
+
self.assertEqual(self.erratum.cves_count, 0)
|
|
168
|
+
|
|
169
|
+
def test_cves_count_on_clear(self):
|
|
170
|
+
"""Test cves_count resets to zero on clear."""
|
|
171
|
+
cve1 = CVE.objects.create(cve_id='CVE-2024-0004')
|
|
172
|
+
cve2 = CVE.objects.create(cve_id='CVE-2024-0005')
|
|
173
|
+
self.erratum.cves.add(cve1, cve2)
|
|
174
|
+
self.erratum.refresh_from_db()
|
|
175
|
+
self.assertEqual(self.erratum.cves_count, 2)
|
|
176
|
+
self.erratum.cves.clear()
|
|
177
|
+
self.erratum.refresh_from_db()
|
|
178
|
+
self.assertEqual(self.erratum.cves_count, 0)
|
|
179
|
+
|
|
180
|
+
def test_osreleases_count_on_add(self):
|
|
181
|
+
"""Test osreleases_count increments on add."""
|
|
182
|
+
release = OSRelease.objects.create(name='Ubuntu 24.04')
|
|
183
|
+
self.erratum.osreleases.add(release)
|
|
184
|
+
self.erratum.refresh_from_db()
|
|
185
|
+
self.assertEqual(self.erratum.osreleases_count, 1)
|
|
186
|
+
|
|
187
|
+
def test_osreleases_count_on_remove(self):
|
|
188
|
+
"""Test osreleases_count decrements on remove."""
|
|
189
|
+
release = OSRelease.objects.create(name='Ubuntu 24.04')
|
|
190
|
+
self.erratum.osreleases.add(release)
|
|
191
|
+
self.erratum.refresh_from_db()
|
|
192
|
+
self.assertEqual(self.erratum.osreleases_count, 1)
|
|
193
|
+
self.erratum.osreleases.remove(release)
|
|
194
|
+
self.erratum.refresh_from_db()
|
|
195
|
+
self.assertEqual(self.erratum.osreleases_count, 0)
|
|
196
|
+
|
|
197
|
+
def test_references_count_on_add(self):
|
|
198
|
+
"""Test references_count increments on add."""
|
|
199
|
+
ref = Reference.objects.create(
|
|
200
|
+
ref_type='ADVISORY',
|
|
201
|
+
url='https://example.com/advisory/1',
|
|
202
|
+
)
|
|
203
|
+
self.erratum.references.add(ref)
|
|
204
|
+
self.erratum.refresh_from_db()
|
|
205
|
+
self.assertEqual(self.erratum.references_count, 1)
|
|
206
|
+
|
|
207
|
+
def test_references_count_on_remove(self):
|
|
208
|
+
"""Test references_count decrements on remove."""
|
|
209
|
+
ref = Reference.objects.create(
|
|
210
|
+
ref_type='ADVISORY',
|
|
211
|
+
url='https://example.com/advisory/2',
|
|
212
|
+
)
|
|
213
|
+
self.erratum.references.add(ref)
|
|
214
|
+
self.erratum.refresh_from_db()
|
|
215
|
+
self.assertEqual(self.erratum.references_count, 1)
|
|
216
|
+
self.erratum.references.remove(ref)
|
|
217
|
+
self.erratum.refresh_from_db()
|
|
218
|
+
self.assertEqual(self.erratum.references_count, 0)
|
|
219
|
+
|
|
220
|
+
def test_str_uses_cached_counts(self):
|
|
221
|
+
"""Test __str__ reflects cached count values."""
|
|
222
|
+
cve = CVE.objects.create(cve_id='CVE-2024-0010')
|
|
223
|
+
release = OSRelease.objects.create(name='RHEL 9')
|
|
224
|
+
self.erratum.cves.add(cve)
|
|
225
|
+
self.erratum.osreleases.add(release)
|
|
226
|
+
self.erratum.refresh_from_db()
|
|
227
|
+
result = str(self.erratum)
|
|
228
|
+
self.assertIn('1 related CVEs', result)
|
|
229
|
+
self.assertIn('affecting 1 OS Releases', result)
|
|
230
|
+
self.assertIn('providing 0 fixed Packages', result)
|
|
231
|
+
|
|
232
|
+
def test_counts_match_actual_m2m(self):
|
|
233
|
+
"""Test cached counts stay in sync with actual M2M counts."""
|
|
234
|
+
cve1 = CVE.objects.create(cve_id='CVE-2024-0020')
|
|
235
|
+
cve2 = CVE.objects.create(cve_id='CVE-2024-0021')
|
|
236
|
+
release = OSRelease.objects.create(name='Debian 12')
|
|
237
|
+
self.erratum.cves.add(cve1, cve2)
|
|
238
|
+
self.erratum.osreleases.add(release)
|
|
239
|
+
self.erratum.refresh_from_db()
|
|
240
|
+
self.assertEqual(self.erratum.cves_count, self.erratum.cves.count())
|
|
241
|
+
self.assertEqual(self.erratum.osreleases_count, self.erratum.osreleases.count())
|
|
@@ -29,7 +29,7 @@ from util.filterspecs import Filter, FilterBar
|
|
|
29
29
|
|
|
30
30
|
@login_required
|
|
31
31
|
def erratum_list(request):
|
|
32
|
-
errata = Erratum.objects.
|
|
32
|
+
errata = Erratum.objects.all()
|
|
33
33
|
|
|
34
34
|
if 'e_type' in request.GET:
|
|
35
35
|
errata = errata.filter(e_type=request.GET['e_type']).distinct()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated by Django 4.2.28 on 2026-02-11 04:55
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('hosts', '0009_host_errata'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterModelOptions(
|
|
14
|
+
name='hostrepo',
|
|
15
|
+
options={'ordering': ['host', 'repo']},
|
|
16
|
+
),
|
|
17
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Generated by Django 4.2.28 on 2026-02-11 04:56
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('hosts', '0010_alter_hostrepo_options'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='host',
|
|
15
|
+
name='bug_updates_count',
|
|
16
|
+
field=models.PositiveIntegerField(db_index=True, default=0),
|
|
17
|
+
),
|
|
18
|
+
migrations.AddField(
|
|
19
|
+
model_name='host',
|
|
20
|
+
name='errata_count',
|
|
21
|
+
field=models.PositiveIntegerField(db_index=True, default=0),
|
|
22
|
+
),
|
|
23
|
+
migrations.AddField(
|
|
24
|
+
model_name='host',
|
|
25
|
+
name='packages_count',
|
|
26
|
+
field=models.PositiveIntegerField(db_index=True, default=0),
|
|
27
|
+
),
|
|
28
|
+
migrations.AddField(
|
|
29
|
+
model_name='host',
|
|
30
|
+
name='sec_updates_count',
|
|
31
|
+
field=models.PositiveIntegerField(db_index=True, default=0),
|
|
32
|
+
),
|
|
33
|
+
]
|