patchman 3.0.13__tar.gz → 3.0.14__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.
Files changed (181) hide show
  1. {patchman-3.0.13 → patchman-3.0.14}/INSTALL.md +2 -2
  2. {patchman-3.0.13 → patchman-3.0.14}/PKG-INFO +13 -3
  3. patchman-3.0.14/VERSION.txt +1 -0
  4. {patchman-3.0.13 → patchman-3.0.14}/hosts/serializers.py +2 -2
  5. {patchman-3.0.13 → patchman-3.0.14}/hosts/templates/hosts/host_detail.html +2 -4
  6. patchman-3.0.14/modules/utils.py +69 -0
  7. {patchman-3.0.13 → patchman-3.0.14}/patchman.egg-info/PKG-INFO +13 -3
  8. {patchman-3.0.13 → patchman-3.0.14}/patchman.egg-info/SOURCES.txt +1 -0
  9. {patchman-3.0.13 → patchman-3.0.14}/patchman.egg-info/requires.txt +1 -1
  10. {patchman-3.0.13 → patchman-3.0.14}/repos/models.py +15 -4
  11. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/repo_detail.html +1 -3
  12. {patchman-3.0.13 → patchman-3.0.14}/repos/utils.py +2 -24
  13. {patchman-3.0.13 → patchman-3.0.14}/requirements.txt +1 -1
  14. {patchman-3.0.13 → patchman-3.0.14}/setup.cfg +1 -0
  15. {patchman-3.0.13 → patchman-3.0.14}/util/__init__.py +27 -24
  16. {patchman-3.0.13 → patchman-3.0.14}/util/templates/objectlist.html +1 -1
  17. {patchman-3.0.13 → patchman-3.0.14}/util/templates/searchbar.html +1 -1
  18. {patchman-3.0.13 → patchman-3.0.14}/util/templatetags/common.py +4 -8
  19. patchman-3.0.13/VERSION.txt +0 -1
  20. {patchman-3.0.13 → patchman-3.0.14}/AUTHORS +0 -0
  21. {patchman-3.0.13 → patchman-3.0.14}/COPYING +0 -0
  22. {patchman-3.0.13 → patchman-3.0.14}/MANIFEST.in +0 -0
  23. {patchman-3.0.13 → patchman-3.0.14}/README.md +0 -0
  24. {patchman-3.0.13 → patchman-3.0.14}/arch/__init__.py +0 -0
  25. {patchman-3.0.13 → patchman-3.0.14}/arch/admin.py +0 -0
  26. {patchman-3.0.13 → patchman-3.0.14}/arch/apps.py +0 -0
  27. {patchman-3.0.13 → patchman-3.0.14}/arch/migrations/0001_initial.py +0 -0
  28. {patchman-3.0.13 → patchman-3.0.14}/arch/migrations/__init__.py +0 -0
  29. {patchman-3.0.13 → patchman-3.0.14}/arch/models.py +0 -0
  30. {patchman-3.0.13 → patchman-3.0.14}/arch/serializers.py +0 -0
  31. {patchman-3.0.13 → patchman-3.0.14}/arch/views.py +0 -0
  32. {patchman-3.0.13 → patchman-3.0.14}/domains/__init__.py +0 -0
  33. {patchman-3.0.13 → patchman-3.0.14}/domains/admin.py +0 -0
  34. {patchman-3.0.13 → patchman-3.0.14}/domains/apps.py +0 -0
  35. {patchman-3.0.13 → patchman-3.0.14}/domains/migrations/0001_initial.py +0 -0
  36. {patchman-3.0.13 → patchman-3.0.14}/domains/migrations/__init__.py +0 -0
  37. {patchman-3.0.13 → patchman-3.0.14}/domains/models.py +0 -0
  38. {patchman-3.0.13 → patchman-3.0.14}/domains/serializers.py +0 -0
  39. {patchman-3.0.13 → patchman-3.0.14}/domains/views.py +0 -0
  40. {patchman-3.0.13 → patchman-3.0.14}/etc/patchman/apache.conf.example +0 -0
  41. {patchman-3.0.13 → patchman-3.0.14}/etc/patchman/local_settings.py +0 -0
  42. {patchman-3.0.13 → patchman-3.0.14}/hosts/__init__.py +0 -0
  43. {patchman-3.0.13 → patchman-3.0.14}/hosts/admin.py +0 -0
  44. {patchman-3.0.13 → patchman-3.0.14}/hosts/apps.py +0 -0
  45. {patchman-3.0.13 → patchman-3.0.14}/hosts/forms.py +0 -0
  46. {patchman-3.0.13 → patchman-3.0.14}/hosts/managers.py +0 -0
  47. {patchman-3.0.13 → patchman-3.0.14}/hosts/migrations/0001_initial.py +0 -0
  48. {patchman-3.0.13 → patchman-3.0.14}/hosts/migrations/0002_initial.py +0 -0
  49. {patchman-3.0.13 → patchman-3.0.14}/hosts/migrations/0003_host_modules.py +0 -0
  50. {patchman-3.0.13 → patchman-3.0.14}/hosts/migrations/__init__.py +0 -0
  51. {patchman-3.0.13 → patchman-3.0.14}/hosts/models.py +0 -0
  52. {patchman-3.0.13 → patchman-3.0.14}/hosts/templates/hosts/host_delete.html +0 -0
  53. {patchman-3.0.13 → patchman-3.0.14}/hosts/templates/hosts/host_edit.html +0 -0
  54. {patchman-3.0.13 → patchman-3.0.14}/hosts/templates/hosts/host_list.html +0 -0
  55. {patchman-3.0.13 → patchman-3.0.14}/hosts/templates/hosts/host_table.html +0 -0
  56. {patchman-3.0.13 → patchman-3.0.14}/hosts/templatetags/__init__.py +0 -0
  57. {patchman-3.0.13 → patchman-3.0.14}/hosts/templatetags/report_alert.py +0 -0
  58. {patchman-3.0.13 → patchman-3.0.14}/hosts/urls.py +0 -0
  59. {patchman-3.0.13 → patchman-3.0.14}/hosts/utils.py +0 -0
  60. {patchman-3.0.13 → patchman-3.0.14}/hosts/views.py +0 -0
  61. {patchman-3.0.13 → patchman-3.0.14}/modules/__init__.py +0 -0
  62. {patchman-3.0.13 → patchman-3.0.14}/modules/admin.py +0 -0
  63. {patchman-3.0.13 → patchman-3.0.14}/modules/apps.py +0 -0
  64. {patchman-3.0.13 → patchman-3.0.14}/modules/managers.py +0 -0
  65. {patchman-3.0.13 → patchman-3.0.14}/modules/migrations/0001_initial.py +0 -0
  66. {patchman-3.0.13 → patchman-3.0.14}/modules/migrations/0002_auto_20240204_2214.py +0 -0
  67. {patchman-3.0.13 → patchman-3.0.14}/modules/migrations/0003_alter_module_options.py +0 -0
  68. {patchman-3.0.13 → patchman-3.0.14}/modules/migrations/__init__.py +0 -0
  69. {patchman-3.0.13 → patchman-3.0.14}/modules/models.py +0 -0
  70. {patchman-3.0.13 → patchman-3.0.14}/modules/serializers.py +0 -0
  71. {patchman-3.0.13 → patchman-3.0.14}/modules/templates/modules/module_detail.html +0 -0
  72. {patchman-3.0.13 → patchman-3.0.14}/modules/templates/modules/module_list.html +0 -0
  73. {patchman-3.0.13 → patchman-3.0.14}/modules/templates/modules/module_table.html +0 -0
  74. {patchman-3.0.13 → patchman-3.0.14}/modules/urls.py +0 -0
  75. {patchman-3.0.13 → patchman-3.0.14}/modules/views.py +0 -0
  76. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/__init__.py +0 -0
  77. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/admin.py +0 -0
  78. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/apps.py +0 -0
  79. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/forms.py +0 -0
  80. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/migrations/0001_initial.py +0 -0
  81. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/migrations/0002_initial.py +0 -0
  82. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/migrations/__init__.py +0 -0
  83. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/models.py +0 -0
  84. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/serializers.py +0 -0
  85. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/templates/operatingsystems/operatingsystem_table.html +0 -0
  86. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/templates/operatingsystems/operatingsystemgroup_table.html +0 -0
  87. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/templates/operatingsystems/os_delete.html +0 -0
  88. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/templates/operatingsystems/os_detail.html +0 -0
  89. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/templates/operatingsystems/os_list.html +0 -0
  90. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/templates/operatingsystems/osgroup_delete.html +0 -0
  91. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/templates/operatingsystems/osgroup_detail.html +0 -0
  92. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/templates/operatingsystems/osgroup_list.html +0 -0
  93. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/urls.py +0 -0
  94. {patchman-3.0.13 → patchman-3.0.14}/operatingsystems/views.py +0 -0
  95. {patchman-3.0.13 → patchman-3.0.14}/packages/__init__.py +0 -0
  96. {patchman-3.0.13 → patchman-3.0.14}/packages/admin.py +0 -0
  97. {patchman-3.0.13 → patchman-3.0.14}/packages/apps.py +0 -0
  98. {patchman-3.0.13 → patchman-3.0.14}/packages/managers.py +0 -0
  99. {patchman-3.0.13 → patchman-3.0.14}/packages/migrations/0001_initial.py +0 -0
  100. {patchman-3.0.13 → patchman-3.0.14}/packages/migrations/__init__.py +0 -0
  101. {patchman-3.0.13 → patchman-3.0.14}/packages/models.py +0 -0
  102. {patchman-3.0.13 → patchman-3.0.14}/packages/serializers.py +0 -0
  103. {patchman-3.0.13 → patchman-3.0.14}/packages/templates/packages/package_detail.html +0 -0
  104. {patchman-3.0.13 → patchman-3.0.14}/packages/templates/packages/package_list.html +0 -0
  105. {patchman-3.0.13 → patchman-3.0.14}/packages/templates/packages/package_table.html +0 -0
  106. {patchman-3.0.13 → patchman-3.0.14}/packages/urls.py +0 -0
  107. {patchman-3.0.13 → patchman-3.0.14}/packages/utils.py +0 -0
  108. {patchman-3.0.13 → patchman-3.0.14}/packages/views.py +0 -0
  109. {patchman-3.0.13 → patchman-3.0.14}/patchman/__init__.py +0 -0
  110. {patchman-3.0.13 → patchman-3.0.14}/patchman/celery.py +0 -0
  111. {patchman-3.0.13 → patchman-3.0.14}/patchman/receivers.py +0 -0
  112. {patchman-3.0.13 → patchman-3.0.14}/patchman/settings.py +0 -0
  113. {patchman-3.0.13 → patchman-3.0.14}/patchman/signals.py +0 -0
  114. {patchman-3.0.13 → patchman-3.0.14}/patchman/static/css/base.css +0 -0
  115. {patchman-3.0.13 → patchman-3.0.14}/patchman/static/img/icon-alert.gif +0 -0
  116. {patchman-3.0.13 → patchman-3.0.14}/patchman/static/img/icon-no.gif +0 -0
  117. {patchman-3.0.13 → patchman-3.0.14}/patchman/static/img/icon-yes.gif +0 -0
  118. {patchman-3.0.13 → patchman-3.0.14}/patchman/static/js/ajax-jquery.js +0 -0
  119. {patchman-3.0.13 → patchman-3.0.14}/patchman/static/js/button-post.js +0 -0
  120. {patchman-3.0.13 → patchman-3.0.14}/patchman/urls.py +0 -0
  121. {patchman-3.0.13 → patchman-3.0.14}/patchman/wsgi.py +0 -0
  122. {patchman-3.0.13 → patchman-3.0.14}/patchman.egg-info/dependency_links.txt +0 -0
  123. {patchman-3.0.13 → patchman-3.0.14}/patchman.egg-info/top_level.txt +0 -0
  124. {patchman-3.0.13 → patchman-3.0.14}/reports/__init__.py +0 -0
  125. {patchman-3.0.13 → patchman-3.0.14}/reports/admin.py +0 -0
  126. {patchman-3.0.13 → patchman-3.0.14}/reports/apps.py +0 -0
  127. {patchman-3.0.13 → patchman-3.0.14}/reports/migrations/0001_initial.py +0 -0
  128. {patchman-3.0.13 → patchman-3.0.14}/reports/migrations/0002_report_modules.py +0 -0
  129. {patchman-3.0.13 → patchman-3.0.14}/reports/migrations/__init__.py +0 -0
  130. {patchman-3.0.13 → patchman-3.0.14}/reports/models.py +0 -0
  131. {patchman-3.0.13 → patchman-3.0.14}/reports/tasks.py +0 -0
  132. {patchman-3.0.13 → patchman-3.0.14}/reports/templates/reports/report.txt +0 -0
  133. {patchman-3.0.13 → patchman-3.0.14}/reports/templates/reports/report_delete.html +0 -0
  134. {patchman-3.0.13 → patchman-3.0.14}/reports/templates/reports/report_detail.html +0 -0
  135. {patchman-3.0.13 → patchman-3.0.14}/reports/templates/reports/report_list.html +0 -0
  136. {patchman-3.0.13 → patchman-3.0.14}/reports/templates/reports/report_table.html +0 -0
  137. {patchman-3.0.13 → patchman-3.0.14}/reports/urls.py +0 -0
  138. {patchman-3.0.13 → patchman-3.0.14}/reports/utils.py +0 -0
  139. {patchman-3.0.13 → patchman-3.0.14}/reports/views.py +0 -0
  140. {patchman-3.0.13 → patchman-3.0.14}/repos/__init__.py +0 -0
  141. {patchman-3.0.13 → patchman-3.0.14}/repos/admin.py +0 -0
  142. {patchman-3.0.13 → patchman-3.0.14}/repos/apps.py +0 -0
  143. {patchman-3.0.13 → patchman-3.0.14}/repos/forms.py +0 -0
  144. {patchman-3.0.13 → patchman-3.0.14}/repos/managers.py +0 -0
  145. {patchman-3.0.13 → patchman-3.0.14}/repos/migrations/0001_initial.py +0 -0
  146. {patchman-3.0.13 → patchman-3.0.14}/repos/migrations/__init__.py +0 -0
  147. {patchman-3.0.13 → patchman-3.0.14}/repos/serializers.py +0 -0
  148. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/mirror_delete.html +0 -0
  149. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/mirror_detail.html +0 -0
  150. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/mirror_edit.html +0 -0
  151. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/mirror_edit_repo.html +0 -0
  152. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/mirror_list.html +0 -0
  153. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/mirror_table.html +0 -0
  154. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/mirror_with_repo_list.html +0 -0
  155. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/repo_delete.html +0 -0
  156. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/repo_edit.html +0 -0
  157. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/repo_list.html +0 -0
  158. {patchman-3.0.13 → patchman-3.0.14}/repos/templates/repos/repository_table.html +0 -0
  159. {patchman-3.0.13 → patchman-3.0.14}/repos/templatetags/__init__.py +0 -0
  160. {patchman-3.0.13 → patchman-3.0.14}/repos/templatetags/repo_buttons.py +0 -0
  161. {patchman-3.0.13 → patchman-3.0.14}/repos/urls.py +0 -0
  162. {patchman-3.0.13 → patchman-3.0.14}/repos/views.py +0 -0
  163. {patchman-3.0.13 → patchman-3.0.14}/sbin/patchman +0 -0
  164. {patchman-3.0.13 → patchman-3.0.14}/sbin/patchman-manage +0 -0
  165. {patchman-3.0.13 → patchman-3.0.14}/sbin/patchman-set-secret-key +0 -0
  166. {patchman-3.0.13 → patchman-3.0.14}/scripts/clear-django-logs.py +0 -0
  167. {patchman-3.0.13 → patchman-3.0.14}/scripts/create_graph.sh +0 -0
  168. {patchman-3.0.13 → patchman-3.0.14}/scripts/rpm-install.sh +0 -0
  169. {patchman-3.0.13 → patchman-3.0.14}/scripts/rpm-post-install.sh +0 -0
  170. {patchman-3.0.13 → patchman-3.0.14}/setup.py +0 -0
  171. {patchman-3.0.13 → patchman-3.0.14}/util/apps.py +0 -0
  172. {patchman-3.0.13 → patchman-3.0.14}/util/filterspecs.py +0 -0
  173. {patchman-3.0.13 → patchman-3.0.14}/util/templates/404.html +0 -0
  174. {patchman-3.0.13 → patchman-3.0.14}/util/templates/500.html +0 -0
  175. {patchman-3.0.13 → patchman-3.0.14}/util/templates/base.html +0 -0
  176. {patchman-3.0.13 → patchman-3.0.14}/util/templates/dashboard.html +0 -0
  177. {patchman-3.0.13 → patchman-3.0.14}/util/templates/navbar.html +0 -0
  178. {patchman-3.0.13 → patchman-3.0.14}/util/templates/registration/login.html +0 -0
  179. {patchman-3.0.13 → patchman-3.0.14}/util/templatetags/__init__.py +0 -0
  180. {patchman-3.0.13 → patchman-3.0.14}/util/urls.py +0 -0
  181. {patchman-3.0.13 → patchman-3.0.14}/util/views.py +0 -0
@@ -63,8 +63,8 @@ apt -y install gcc libxml2-dev libxslt1-dev virtualenv python3-dev zlib1g-dev #
63
63
  dnf -y install gcc libxml2-devel libxslt-devel python3-virtualenv # (centos/rocky/alma)
64
64
  mkdir /srv/patchman
65
65
  cd /srv/patchman
66
- virtualenv .
67
- . bin/activate
66
+ python3 -m venv .venv
67
+ . .venv/bin/activate
68
68
  pip install --upgrade pip
69
69
  pip install patchman gunicorn whitenoise==3.3.1
70
70
  patchman-manage migrate
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: patchman
3
- Version: 3.0.13
3
+ Version: 3.0.14
4
4
  Summary: Django based patch status monitoring tool for linux systems
5
5
  Home-page: http://patchman.openbytes.ie/
6
6
  Author: Marcus Furlong
@@ -21,7 +21,7 @@ Requires-Dist: django-extensions==3.2.1
21
21
  Requires-Dist: django-bootstrap3==23.1
22
22
  Requires-Dist: progressbar==2.5
23
23
  Requires-Dist: python-debian==0.1.49
24
- Requires-Dist: lxml==4.9.4
24
+ Requires-Dist: lxml==5.2.2
25
25
  Requires-Dist: defusedxml==0.7.1
26
26
  Requires-Dist: PyYAML==6.0.1
27
27
  Requires-Dist: chardet==4.0.0
@@ -33,6 +33,16 @@ Requires-Dist: humanize==3.13.1
33
33
  Requires-Dist: version-utils==0.3.0
34
34
  Requires-Dist: python-magic==0.4.25
35
35
  Requires-Dist: pymemcache==4.0.0
36
+ Dynamic: author
37
+ Dynamic: author-email
38
+ Dynamic: classifier
39
+ Dynamic: description
40
+ Dynamic: description-content-type
41
+ Dynamic: home-page
42
+ Dynamic: keywords
43
+ Dynamic: license
44
+ Dynamic: requires-dist
45
+ Dynamic: summary
36
46
 
37
47
  # Patchman
38
48
 
@@ -0,0 +1 @@
1
+ 3.0.14
@@ -31,10 +31,10 @@ class HostSerializer(serializers.HyperlinkedModelSerializer):
31
31
  'updated_at', 'bugfix_update_count', 'security_update_count')
32
32
 
33
33
  def get_bugfix_update_count(self, obj):
34
- return len([u for u in obj.updates.all() if not u.security])
34
+ return obj.updates.filter(security=False).count()
35
35
 
36
36
  def get_security_update_count(self, obj):
37
- return len([u for u in obj.updates.all() if u.security])
37
+ return obj.updates.filter(security=True).count()
38
38
 
39
39
 
40
40
  class HostRepoSerializer(serializers.HyperlinkedModelSerializer):
@@ -138,9 +138,7 @@
138
138
 
139
139
  <div class="tab-pane fade in" id="host_modules">
140
140
  <div class="well well-sm">
141
- <div class="well">
142
- {% gen_table host.modules.all %}
143
- </div>
141
+ {% gen_table host.modules.all %}
144
142
  </div>
145
143
  </div>
146
144
 
@@ -148,7 +146,7 @@
148
146
  <div class="well well-sm">
149
147
  <button class="btn btn-primary btn-sm" data-toggle="collapse" data-target="#packages">Show/Hide Installed Packages</button>
150
148
  <div id="packages" class="collapse">
151
- <div class="well">
149
+ <div class="well well-sm">
152
150
  {% for package in host.packages.select_related %}
153
151
  <span class="label label-brick {% cycle 'bg-info' 'bg-success' %}">
154
152
  <a href="{{ package.name.get_absolute_url }}"> {{ package }} </a>
@@ -0,0 +1,69 @@
1
+ # Copyright 2024 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 IntegrityError, DatabaseError, transaction
18
+ from patchman.signals import error_message
19
+
20
+ from modules.models import Module
21
+ from arch.models import PackageArchitecture
22
+
23
+
24
+ def get_or_create_module(name, stream, version, context, arch, repo):
25
+ """ Get or create a module object
26
+ Returns the module and a boolean for created
27
+ """
28
+ created = False
29
+ with transaction.atomic():
30
+ m_arch, c = PackageArchitecture.objects.get_or_create(name=arch)
31
+ try:
32
+ with transaction.atomic():
33
+ module, created = Module.objects.get_or_create(
34
+ name=name,
35
+ stream=stream,
36
+ version=version,
37
+ context=context,
38
+ arch=m_arch,
39
+ repo=repo,
40
+ )
41
+ except IntegrityError as e:
42
+ error_message.send(sender=None, text=e)
43
+ module = Module.objects.get(
44
+ name=name,
45
+ stream=stream,
46
+ version=version,
47
+ context=context,
48
+ arch=m_arch,
49
+ repo=repo,
50
+ )
51
+ except DatabaseError as e:
52
+ error_message.send(sender=None, text=e)
53
+ return module, created
54
+
55
+
56
+ def get_matching_modules(name, stream, version, context, arch):
57
+ """ Return modules that match name, stream, version, context, and arch,
58
+ regardless of repo
59
+ """
60
+ with transaction.atomic():
61
+ m_arch, c = PackageArchitecture.objects.get_or_create(name=arch)
62
+ modules = Module.objects.filter(
63
+ name=name,
64
+ stream=stream,
65
+ version=version,
66
+ context=context,
67
+ arch=m_arch,
68
+ )
69
+ return modules
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: patchman
3
- Version: 3.0.13
3
+ Version: 3.0.14
4
4
  Summary: Django based patch status monitoring tool for linux systems
5
5
  Home-page: http://patchman.openbytes.ie/
6
6
  Author: Marcus Furlong
@@ -21,7 +21,7 @@ Requires-Dist: django-extensions==3.2.1
21
21
  Requires-Dist: django-bootstrap3==23.1
22
22
  Requires-Dist: progressbar==2.5
23
23
  Requires-Dist: python-debian==0.1.49
24
- Requires-Dist: lxml==4.9.4
24
+ Requires-Dist: lxml==5.2.2
25
25
  Requires-Dist: defusedxml==0.7.1
26
26
  Requires-Dist: PyYAML==6.0.1
27
27
  Requires-Dist: chardet==4.0.0
@@ -33,6 +33,16 @@ Requires-Dist: humanize==3.13.1
33
33
  Requires-Dist: version-utils==0.3.0
34
34
  Requires-Dist: python-magic==0.4.25
35
35
  Requires-Dist: pymemcache==4.0.0
36
+ Dynamic: author
37
+ Dynamic: author-email
38
+ Dynamic: classifier
39
+ Dynamic: description
40
+ Dynamic: description-content-type
41
+ Dynamic: home-page
42
+ Dynamic: keywords
43
+ Dynamic: license
44
+ Dynamic: requires-dist
45
+ Dynamic: summary
36
46
 
37
47
  # Patchman
38
48
 
@@ -53,6 +53,7 @@ modules/managers.py
53
53
  modules/models.py
54
54
  modules/serializers.py
55
55
  modules/urls.py
56
+ modules/utils.py
56
57
  modules/views.py
57
58
  modules/migrations/0001_initial.py
58
59
  modules/migrations/0002_auto_20240204_2214.py
@@ -4,7 +4,7 @@ django-extensions==3.2.1
4
4
  django-bootstrap3==23.1
5
5
  progressbar==2.5
6
6
  python-debian==0.1.49
7
- lxml==4.9.4
7
+ lxml==5.2.2
8
8
  defusedxml==0.7.1
9
9
  PyYAML==6.0.1
10
10
  chardet==4.0.0
@@ -15,6 +15,7 @@
15
15
  # You should have received a copy of the GNU General Public License
16
16
  # along with Patchman. If not, see <http://www.gnu.org/licenses/>
17
17
 
18
+ from django.conf import settings
18
19
  from django.db import models
19
20
  from django.urls import reverse
20
21
 
@@ -154,15 +155,25 @@ class Mirror(models.Model):
154
155
 
155
156
  def fail(self):
156
157
  """ Records that the mirror has failed
157
- Disables refresh on a mirror if it fails more than 28 times
158
+ Disables refresh on a mirror if it fails more than MAX_MIRROR_FAILURES times
159
+ Set MAX_MIRROR_FAILURES to -1 to disable marking mirrors as failures
160
+ Default is 28
158
161
  """
159
162
  text = f'No usable mirror found at {self.url!s}'
160
163
  error_message.send(sender=None, text=text)
164
+ default_max_mirror_failures = 28
165
+ if hasattr(settings, 'MAX_MIRROR_FAILURES') and \
166
+ isinstance(settings.MAX_MIRROR_FAILURES, int):
167
+ max_mirror_failures = settings.MAX_MIRROR_FAILURES
168
+ else:
169
+ max_mirror_failures = default_max_mirror_failures
161
170
  self.fail_count = self.fail_count + 1
162
- if self.fail_count > 28:
171
+ if max_mirror_failures == -1:
172
+ text = f'Mirror has failed {self.fail_count} times, but MAX_MIRROR_FAILURES=-1, not disabling refresh'
173
+ elif self.fail_count > max_mirror_failures:
163
174
  self.refresh = False
164
- text = 'Mirror has failed more than 28 times, disabling refresh'
165
- error_message.send(sender=None, text=text)
175
+ text = f'Mirror has failed {self.fail_count} times (max={max_mirror_failures}), disabling refresh'
176
+ error_message.send(sender=None, text=text)
166
177
 
167
178
  def update_packages(self, packages):
168
179
  """ Update the packages associated with a mirror
@@ -68,9 +68,7 @@
68
68
 
69
69
  <div class="tab-pane fade in" id="repo_modules">
70
70
  <div class="well well-sm">
71
- <div class="well">
72
- {% gen_table repo.module_set.all %}
73
- </div>
71
+ {% gen_table repo.module_set.all %}
74
72
  </div>
75
73
  </div>
76
74
  </div>
@@ -311,10 +311,6 @@ def extract_module_metadata(data, url, repo):
311
311
  raw_packages = modulemd.get('artifacts', {}).get('rpms', '')
312
312
  # raw_profiles = list(modulemd.get('profiles', {}).keys())
313
313
 
314
- package_arches = PackageArchitecture.objects.all()
315
- with transaction.atomic():
316
- m_arch, c = package_arches.get_or_create(name=arch)
317
-
318
314
  packages = set()
319
315
  p_type = Package.RPM
320
316
  for pkg_str in raw_packages:
@@ -322,26 +318,8 @@ def extract_module_metadata(data, url, repo):
322
318
  package = get_or_create_package(p_name, p_epoch, p_ver, p_rel, p_arch, p_type)
323
319
  packages.add(package)
324
320
 
325
- from modules.models import Module
326
- all_modules = Module.objects.all()
327
- try:
328
- with transaction.atomic():
329
- module, c = all_modules.get_or_create(name=m_name,
330
- stream=m_stream,
331
- version=m_version,
332
- context=m_context,
333
- arch=m_arch,
334
- repo=repo)
335
- except IntegrityError as e:
336
- error_message.send(sender=None, text=e)
337
- module = all_modules.get(name=m_name,
338
- stream=m_stream,
339
- version=m_version,
340
- context=m_context,
341
- arch=m_arch,
342
- repo=repo)
343
- except DatabaseError as e:
344
- error_message.send(sender=None, text=e)
321
+ from modules.utils import get_or_create_module
322
+ module, created = get_or_create_module(m_name, m_stream, m_version, m_context, arch, repo)
345
323
 
346
324
  package_ids = []
347
325
  for package in packages:
@@ -4,7 +4,7 @@ django-extensions==3.2.1
4
4
  django-bootstrap3==23.1
5
5
  progressbar==2.5
6
6
  python-debian==0.1.49
7
- lxml==4.9.4
7
+ lxml==5.2.2
8
8
  defusedxml==0.7.1
9
9
  PyYAML==6.0.1
10
10
  chardet==4.0.0
@@ -25,6 +25,7 @@ requires = /usr/bin/python3
25
25
  python3-importlib-metadata
26
26
  policycoreutils-python-utils
27
27
  httpd
28
+ python3-dnf-plugin-post-transaction-actions
28
29
 
29
30
  [install]
30
31
  optimize = 1
@@ -25,7 +25,7 @@ from colorama import Fore, Style
25
25
  from enum import Enum
26
26
  from hashlib import md5, sha1, sha256, sha512
27
27
  from progressbar import Bar, ETA, Percentage, ProgressBar
28
- from patchman.signals import error_message
28
+ from patchman.signals import error_message, info_message
29
29
 
30
30
 
31
31
  if ProgressBar.__dict__.get('maxval'):
@@ -52,12 +52,12 @@ def set_verbosity(value):
52
52
  verbose = value
53
53
 
54
54
 
55
- def create_pbar(ptext, plength, **kwargs):
55
+ def create_pbar(ptext, plength, ljust=35, **kwargs):
56
56
  """ Create a global progress bar if global verbose is True
57
57
  """
58
58
  global pbar, verbose
59
59
  if verbose and plength > 0:
60
- jtext = str(ptext).ljust(35)
60
+ jtext = str(ptext).ljust(ljust)
61
61
  if pbar2:
62
62
  pbar = ProgressBar(widgets=[Style.RESET_ALL + Fore.YELLOW + jtext,
63
63
  Percentage(), Bar(), ETA()],
@@ -79,34 +79,37 @@ def update_pbar(index, **kwargs):
79
79
  pmax = pbar.max_value
80
80
  else:
81
81
  pmax = pbar.maxval
82
- if index == pmax:
82
+ if index >= pmax:
83
83
  pbar.finish()
84
84
  print_nocr(Fore.RESET)
85
85
  pbar = None
86
86
 
87
87
 
88
- def download_url(res, text=''):
88
+ def download_url(res, text='', ljust=35):
89
89
  """ Display a progress bar to download the request content if verbose is
90
90
  True. Otherwise, just return the request content
91
91
  """
92
92
  global verbose
93
- if verbose and 'content-length' in res.headers:
94
- clen = int(res.headers['content-length'])
95
- create_pbar(text, clen)
96
- chunk_size = 16384
97
- i = 0
98
- data = b''
99
- for chunk in res.iter_content(chunk_size=chunk_size,
100
- decode_unicode=False):
101
- i += len(chunk)
102
- if i > clen:
103
- update_pbar(clen)
104
- else:
105
- update_pbar(i)
106
- data += chunk
107
- return data
108
- else:
109
- return res.content
93
+ if verbose:
94
+ content_length = res.headers.get('content-length')
95
+ if content_length:
96
+ clen = int(content_length)
97
+ create_pbar(text, clen, ljust)
98
+ chunk_size = 16384
99
+ i = 0
100
+ data = b''
101
+ for chunk in res.iter_content(chunk_size=chunk_size,
102
+ decode_unicode=False):
103
+ i += len(chunk)
104
+ if i > clen:
105
+ update_pbar(clen)
106
+ else:
107
+ update_pbar(i)
108
+ data += chunk
109
+ return data
110
+ else:
111
+ info_message.send(sender=None, text=text)
112
+ return res.content
110
113
 
111
114
 
112
115
  def print_nocr(text):
@@ -116,12 +119,12 @@ def print_nocr(text):
116
119
  sys.stdout.softspace = False
117
120
 
118
121
 
119
- def get_url(url):
122
+ def get_url(url, headers={}, params={}):
120
123
  """ Perform a http GET on a URL. Return None on error.
121
124
  """
122
125
  res = None
123
126
  try:
124
- res = requests.get(url, stream=True)
127
+ res = requests.get(url, headers=headers, params=params, stream=True)
125
128
  except requests.exceptions.Timeout:
126
129
  error_message.send(sender=None, text=f'Timeout - {url!s}')
127
130
  except requests.exceptions.TooManyRedirects:
@@ -6,7 +6,7 @@
6
6
 
7
7
  <div class="row">
8
8
  <div class="col-sm-10">
9
- {% searchform %}
9
+ {% searchform terms %}
10
10
  {% gen_table page.object_list %}
11
11
  <div class="centered">
12
12
  {% object_count page %}
@@ -2,7 +2,7 @@
2
2
  <div>
3
3
  <form method="get">
4
4
  <div class="input-group">
5
- <input type="text" class="form-control input-sm" placeholder="Search" name="search" value="{{ terms }}" id="search">
5
+ <input type="text" class="form-control input-sm" placeholder="Search" name="search" id="search" value="{{ terms }}">
6
6
  <div class="input-group-btn">
7
7
  <button class="btn btn-primary btn-sm" type="submit">{% bootstrap_icon "search" %}</button>
8
8
  </div>
@@ -18,6 +18,7 @@
18
18
 
19
19
  from humanize import naturaltime
20
20
  from datetime import datetime, timedelta
21
+ from urllib.parse import urlencode
21
22
 
22
23
  from django.conf import settings
23
24
  from django.template import Library
@@ -26,11 +27,6 @@ from django.utils.html import format_html
26
27
  from django.templatetags.static import static
27
28
  from django.core.paginator import Paginator
28
29
 
29
- try:
30
- from urllib.parse import urlencode
31
- except ImportError:
32
- from urllib import urlencode
33
-
34
30
  register = Library()
35
31
 
36
32
 
@@ -67,7 +63,7 @@ def no_yes_img(boolean, alt_yes='Not Required', alt_no='Required'):
67
63
 
68
64
  @register.simple_tag
69
65
  def gen_table(object_list, template_name=None):
70
- if object_list == '':
66
+ if not object_list:
71
67
  return ''
72
68
  if not template_name:
73
69
  app_label = object_list.model._meta.app_label
@@ -97,9 +93,9 @@ def get_querystring(request):
97
93
 
98
94
 
99
95
  @register.simple_tag
100
- def searchform():
96
+ def searchform(terms):
101
97
  template = get_template('searchbar.html')
102
- html = template.render({'post_url': '.'})
98
+ html = template.render({'post_url': '.', 'terms': terms})
103
99
  return html
104
100
 
105
101
 
@@ -1 +0,0 @@
1
- 3.0.13
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes