patchman 2.0.17__tar.gz → 3.0.0__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-2.0.17 → patchman-3.0.0}/INSTALL.md +29 -17
- {patchman-2.0.17 → patchman-3.0.0}/MANIFEST.in +1 -0
- {patchman-2.0.17 → patchman-3.0.0}/PKG-INFO +9 -8
- patchman-3.0.0/VERSION.txt +1 -0
- {patchman-2.0.17 → patchman-3.0.0}/etc/patchman/local_settings.py +4 -1
- {patchman-2.0.17 → patchman-3.0.0}/hosts/models.py +30 -4
- {patchman-2.0.17 → patchman-3.0.0}/hosts/templates/hosts/host_detail.html +9 -0
- patchman-3.0.0/modules/admin.py +20 -0
- patchman-3.0.0/modules/apps.py +21 -0
- patchman-3.0.0/modules/managers.py +22 -0
- patchman-3.0.0/modules/models.py +44 -0
- patchman-3.0.0/modules/serializers.py +25 -0
- patchman-3.0.0/modules/templates/modules/module_detail.html +69 -0
- patchman-3.0.0/modules/templates/modules/module_list.html +7 -0
- patchman-3.0.0/modules/templates/modules/module_table.html +23 -0
- patchman-3.0.0/modules/urls.py +26 -0
- patchman-3.0.0/modules/views.py +74 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/utils.py +23 -1
- {patchman-2.0.17 → patchman-3.0.0}/patchman/settings.py +3 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/urls.py +1 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman.egg-info/PKG-INFO +9 -8
- {patchman-2.0.17 → patchman-3.0.0}/patchman.egg-info/SOURCES.txt +11 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman.egg-info/requires.txt +8 -7
- {patchman-2.0.17 → patchman-3.0.0}/patchman.egg-info/top_level.txt +1 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/models.py +16 -3
- patchman-3.0.0/reports/templates/reports/report.txt +29 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/templates/reports/report_detail.html +17 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/utils.py +101 -3
- {patchman-2.0.17 → patchman-3.0.0}/reports/views.py +8 -4
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/repo_detail.html +9 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/utils.py +124 -18
- {patchman-2.0.17 → patchman-3.0.0}/requirements.txt +8 -7
- {patchman-2.0.17 → patchman-3.0.0}/sbin/patchman +20 -1
- {patchman-2.0.17 → patchman-3.0.0}/scripts/rpm-post-install.sh +4 -3
- {patchman-2.0.17 → patchman-3.0.0}/setup.cfg +4 -3
- patchman-3.0.0/util/templatetags/__init__.py +0 -0
- patchman-2.0.17/VERSION.txt +0 -1
- patchman-2.0.17/reports/templates/reports/report.txt +0 -16
- {patchman-2.0.17 → patchman-3.0.0}/AUTHORS +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/COPYING +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/README.md +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/arch/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/arch/admin.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/arch/apps.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/arch/models.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/arch/serializers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/arch/views.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/domains/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/domains/admin.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/domains/apps.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/domains/models.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/domains/serializers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/domains/views.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/etc/patchman/apache.conf.example +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/admin.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/apps.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/forms.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/managers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/serializers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/templates/hosts/host_delete.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/templates/hosts/host_edit.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/templates/hosts/host_list.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/templates/hosts/host_table.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/templatetags/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/templatetags/report_alert.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/urls.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/utils.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/hosts/views.py +0 -0
- {patchman-2.0.17/operatingsystems → patchman-3.0.0/modules}/__init__.py +0 -0
- {patchman-2.0.17/packages → patchman-3.0.0/operatingsystems}/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/admin.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/apps.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/forms.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/models.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/serializers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/templates/operatingsystems/operatingsystem_table.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/templates/operatingsystems/operatingsystemgroup_table.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/templates/operatingsystems/os_delete.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/templates/operatingsystems/os_detail.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/templates/operatingsystems/os_list.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/templates/operatingsystems/osgroup_delete.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/templates/operatingsystems/osgroup_detail.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/templates/operatingsystems/osgroup_list.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/urls.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/operatingsystems/views.py +0 -0
- {patchman-2.0.17/reports → patchman-3.0.0/packages}/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/admin.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/apps.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/managers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/models.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/serializers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/templates/packages/package_detail.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/templates/packages/package_list.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/templates/packages/package_table.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/urls.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/packages/views.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/celery.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/receivers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/signals.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/static/css/base.css +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/static/img/icon-alert.gif +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/static/img/icon-no.gif +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/static/img/icon-yes.gif +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/static/js/ajax-jquery.js +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/static/js/button-post.js +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman/wsgi.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/patchman.egg-info/dependency_links.txt +0 -0
- {patchman-2.0.17/repos → patchman-3.0.0/reports}/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/admin.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/apps.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/tasks.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/templates/reports/report_delete.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/templates/reports/report_list.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/templates/reports/report_table.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/reports/urls.py +0 -0
- {patchman-2.0.17/repos/templatetags → patchman-3.0.0/repos}/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/admin.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/apps.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/forms.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/managers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/models.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/serializers.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/mirror_delete.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/mirror_detail.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/mirror_edit.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/mirror_edit_repo.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/mirror_list.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/mirror_table.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/mirror_with_repo_list.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/repo_delete.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/repo_edit.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/repo_list.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templates/repos/repository_table.html +0 -0
- {patchman-2.0.17/util → patchman-3.0.0/repos}/templatetags/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/templatetags/repo_buttons.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/urls.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/repos/views.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/sbin/patchman-manage +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/sbin/patchman-set-secret-key +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/scripts/clear-django-logs.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/scripts/create_graph.sh +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/scripts/rpm-install.sh +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/setup.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/__init__.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/apps.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/filterspecs.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templates/404.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templates/500.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templates/base.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templates/dashboard.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templates/navbar.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templates/objectlist.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templates/registration/login.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templates/searchbar.html +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/templatetags/common.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/urls.py +0 -0
- {patchman-2.0.17 → patchman-3.0.0}/util/views.py +0 -0
|
@@ -5,43 +5,48 @@ mysql or postgresql instead, see the database configuration section.
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
## Supported Install Options
|
|
8
|
-
- [Ubuntu
|
|
9
|
-
- [Debian
|
|
10
|
-
- [CentOS
|
|
8
|
+
- [Ubuntu 22.04](#ubuntu-2204-jammy)
|
|
9
|
+
- [Debian 12](#debian-12-bookworm)
|
|
10
|
+
- [CentOS 9](#centos-9)
|
|
11
11
|
- [virtualenv + pip](#virtualenv--pip)
|
|
12
12
|
- [Source](#source)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
### Ubuntu
|
|
15
|
+
### Ubuntu 22.04 (jammy)
|
|
16
16
|
|
|
17
17
|
```shell
|
|
18
18
|
curl -sS https://repo.openbytes.ie/openbytes.gpg > /usr/share/keyrings/openbytes.gpg
|
|
19
|
-
echo "deb [signed-by=/usr/share/keyrings/openbytes.gpg] https://repo.openbytes.ie/patchman/ubuntu
|
|
19
|
+
echo "deb [signed-by=/usr/share/keyrings/openbytes.gpg] https://repo.openbytes.ie/patchman/ubuntu jammy main" > /etc/apt/sources.list.d/patchman.list
|
|
20
20
|
apt update
|
|
21
21
|
apt -y install python3-patchman patchman-client
|
|
22
22
|
patchman-manage createsuperuser
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
### Debian
|
|
25
|
+
### Debian 12 (bookworm)
|
|
26
26
|
|
|
27
27
|
```shell
|
|
28
28
|
curl -sS https://repo.openbytes.ie/openbytes.gpg > /usr/share/keyrings/openbytes.gpg
|
|
29
|
-
echo "deb [signed-by=/usr/share/keyrings/openbytes.gpg] https://repo.openbytes.ie/patchman/debian
|
|
29
|
+
echo "deb [signed-by=/usr/share/keyrings/openbytes.gpg] https://repo.openbytes.ie/patchman/debian bookworm main" > /etc/apt/sources.list.d/patchman.list
|
|
30
30
|
apt update
|
|
31
31
|
apt -y install python3-patchman patchman-client
|
|
32
32
|
patchman-manage createsuperuser
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
### CentOS
|
|
35
|
+
### CentOS 9
|
|
36
|
+
|
|
37
|
+
This also applies to Rocky/Alma/RHEL
|
|
36
38
|
|
|
37
39
|
```shell
|
|
40
|
+
curl -sS https://repo.openbytes.ie/openbytes.gpg > /etc/pki/rpm-gpg/RPM-GPG-KEY-openbytes
|
|
38
41
|
cat <<EOF >> /etc/yum.repos.d/openbytes.repo
|
|
39
42
|
[openbytes]
|
|
40
43
|
name=openbytes
|
|
41
|
-
baseurl=https://repo.openbytes.ie/patchman/
|
|
44
|
+
baseurl=https://repo.openbytes.ie/patchman/el9
|
|
42
45
|
enabled=1
|
|
43
|
-
gpgcheck=
|
|
46
|
+
gpgcheck=1
|
|
47
|
+
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-openbytes
|
|
44
48
|
EOF
|
|
49
|
+
update-crypto-policies --set DEFAULT:SHA1
|
|
45
50
|
dnf -y install epel-release
|
|
46
51
|
dnf makecache
|
|
47
52
|
dnf -y install patchman patchman-client
|
|
@@ -55,7 +60,7 @@ TBD - not working yet
|
|
|
55
60
|
|
|
56
61
|
```shell
|
|
57
62
|
apt -y install gcc libxml2-dev libxslt1-dev virtualenv python3-dev zlib1g-dev # (debian/ubuntu)
|
|
58
|
-
dnf -y install gcc libxml2-devel libxslt-devel python3-virtualenv # (centos/
|
|
63
|
+
dnf -y install gcc libxml2-devel libxslt-devel python3-virtualenv # (centos/rocky/alma)
|
|
59
64
|
mkdir /srv/patchman
|
|
60
65
|
cd /srv/patchman
|
|
61
66
|
virtualenv .
|
|
@@ -69,7 +74,7 @@ gunicorn patchman.wsgi -b 0.0.0.0:80
|
|
|
69
74
|
|
|
70
75
|
### Source
|
|
71
76
|
|
|
72
|
-
#### Ubuntu
|
|
77
|
+
#### Ubuntu 22.04 (jammy)
|
|
73
78
|
|
|
74
79
|
1. Install dependencies
|
|
75
80
|
|
|
@@ -111,7 +116,7 @@ be configured:
|
|
|
111
116
|
|
|
112
117
|
* ADMINS - set up an admin email address
|
|
113
118
|
* SECRET_KEY - create a random secret key
|
|
114
|
-
*
|
|
119
|
+
* STATIC_ROOT - should point to `/srv/patchman/run/static` if installing from
|
|
115
120
|
source
|
|
116
121
|
|
|
117
122
|
|
|
@@ -212,6 +217,8 @@ postgres=# ALTER ROLE patchman SET timezone TO 'UTC';
|
|
|
212
217
|
ALTER ROLE
|
|
213
218
|
postgres=# GRANT ALL PRIVILEGES ON DATABASE patchman to patchman;
|
|
214
219
|
GRANT
|
|
220
|
+
postgres=# GRANT ALL ON SCHEMA public TO patchman;
|
|
221
|
+
GRANT
|
|
215
222
|
```
|
|
216
223
|
|
|
217
224
|
3. Modify `/etc/patchman/local_settings.py` as follows:
|
|
@@ -314,7 +321,9 @@ apt -y install python3-celery redis python3-redis python-celery-common
|
|
|
314
321
|
C_FORCE_ROOT=1 celery -b redis://127.0.0.1:6379/0 -A patchman worker -l INFO -E
|
|
315
322
|
```
|
|
316
323
|
|
|
317
|
-
#### CentOS /
|
|
324
|
+
#### CentOS / Rocky / Alma
|
|
325
|
+
|
|
326
|
+
Currently waiting on https://bugzilla.redhat.com/show_bug.cgi?id=2032543
|
|
318
327
|
|
|
319
328
|
```shell
|
|
320
329
|
dnf -y install python3-celery redis python3-redis
|
|
@@ -334,8 +343,8 @@ Enable celery by adding `USE_ASYNC_PROCESSING = True` to `/etc/patchman/local_se
|
|
|
334
343
|
Memcached can optionally be run to reduce the load on the server.
|
|
335
344
|
|
|
336
345
|
```shell
|
|
337
|
-
apt -y install memcached python3-
|
|
338
|
-
dnf -y install memcached python3-
|
|
346
|
+
apt -y install memcached python3-pymemcache # (debian/ubuntu)
|
|
347
|
+
dnf -y install memcached python3-pymemcache # (centos/rocky/alma)
|
|
339
348
|
systemctl restart memcached
|
|
340
349
|
```
|
|
341
350
|
|
|
@@ -344,8 +353,11 @@ and add the following to `/etc/patchman/local_settings.py`
|
|
|
344
353
|
```
|
|
345
354
|
CACHES = {
|
|
346
355
|
'default': {
|
|
347
|
-
'BACKEND': 'django.core.cache.backends.memcached.
|
|
356
|
+
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
|
|
348
357
|
'LOCATION': '127.0.0.1:11211',
|
|
358
|
+
'OPTIONS': {
|
|
359
|
+
'ignore_exc': True,
|
|
360
|
+
},
|
|
349
361
|
}
|
|
350
362
|
}
|
|
351
363
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: patchman
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
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
|
|
@@ -15,23 +15,24 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: COPYING
|
|
17
17
|
License-File: AUTHORS
|
|
18
|
-
Requires-Dist: Django==
|
|
18
|
+
Requires-Dist: Django==3.2.23
|
|
19
19
|
Requires-Dist: django-tagging==0.5.0
|
|
20
|
-
Requires-Dist: django-extensions==3.1
|
|
21
|
-
Requires-Dist: django-bootstrap3==
|
|
20
|
+
Requires-Dist: django-extensions==3.2.1
|
|
21
|
+
Requires-Dist: django-bootstrap3==23.1
|
|
22
22
|
Requires-Dist: progressbar==2.5
|
|
23
|
-
Requires-Dist: python-debian==0.1.
|
|
24
|
-
Requires-Dist: lxml==4.
|
|
23
|
+
Requires-Dist: python-debian==0.1.49
|
|
24
|
+
Requires-Dist: lxml==4.9.2
|
|
25
25
|
Requires-Dist: defusedxml==0.7.1
|
|
26
|
+
Requires-Dist: PyYAML==6.0
|
|
26
27
|
Requires-Dist: chardet==4.0.0
|
|
27
|
-
Requires-Dist: requests==2.
|
|
28
|
+
Requires-Dist: requests==2.31.0
|
|
28
29
|
Requires-Dist: colorama==0.4.4
|
|
29
30
|
Requires-Dist: djangorestframework==3.13.1
|
|
30
31
|
Requires-Dist: django-filter==21.1
|
|
31
32
|
Requires-Dist: humanize==3.13.1
|
|
32
33
|
Requires-Dist: version-utils==0.3.0
|
|
33
34
|
Requires-Dist: python-magic==0.4.25
|
|
34
|
-
Requires-Dist:
|
|
35
|
+
Requires-Dist: pymemcache==4.0.0
|
|
35
36
|
|
|
36
37
|
# Patchman
|
|
37
38
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.0.0
|
|
@@ -40,7 +40,10 @@ RUN_GUNICORN = False
|
|
|
40
40
|
# Enable memcached
|
|
41
41
|
CACHES = {
|
|
42
42
|
'default': {
|
|
43
|
-
'BACKEND': 'django.core.cache.backends.memcached.
|
|
43
|
+
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
|
|
44
44
|
'LOCATION': '127.0.0.1:11211',
|
|
45
|
+
'OPTIONS': {
|
|
46
|
+
'ignore_exc': True,
|
|
47
|
+
},
|
|
45
48
|
}
|
|
46
49
|
}
|
|
@@ -31,6 +31,7 @@ from domains.models import Domain
|
|
|
31
31
|
from repos.models import Repository
|
|
32
32
|
from operatingsystems.models import OS
|
|
33
33
|
from arch.models import MachineArchitecture
|
|
34
|
+
from modules.models import Module
|
|
34
35
|
from patchman.signals import info_message, error_message
|
|
35
36
|
from packages.utils import get_or_create_package_update
|
|
36
37
|
from repos.utils import find_best_repo
|
|
@@ -50,6 +51,7 @@ class Host(models.Model):
|
|
|
50
51
|
lastreport = models.DateTimeField()
|
|
51
52
|
packages = models.ManyToManyField(Package, blank=True)
|
|
52
53
|
repos = models.ManyToManyField(Repository, blank=True, through='HostRepo')
|
|
54
|
+
modules = models.ManyToManyField(Module, blank=True)
|
|
53
55
|
updates = models.ManyToManyField(PackageUpdate, blank=True)
|
|
54
56
|
reboot_required = models.BooleanField(default=False)
|
|
55
57
|
host_repos_only = models.BooleanField(default=True)
|
|
@@ -122,15 +124,18 @@ class Host(models.Model):
|
|
|
122
124
|
def get_host_repo_packages(self):
|
|
123
125
|
if self.host_repos_only:
|
|
124
126
|
hostrepos_q = Q(mirror__repo__in=self.repos.all(),
|
|
125
|
-
mirror__enabled=True,
|
|
127
|
+
mirror__enabled=True,
|
|
128
|
+
mirror__repo__enabled=True,
|
|
126
129
|
mirror__repo__hostrepo__enabled=True)
|
|
127
130
|
else:
|
|
128
131
|
hostrepos_q = \
|
|
129
132
|
Q(mirror__repo__osgroup__os__host=self,
|
|
130
|
-
mirror__repo__arch=self.arch,
|
|
133
|
+
mirror__repo__arch=self.arch,
|
|
134
|
+
mirror__enabled=True,
|
|
131
135
|
mirror__repo__enabled=True) | \
|
|
132
136
|
Q(mirror__repo__in=self.repos.all(),
|
|
133
|
-
mirror__enabled=True,
|
|
137
|
+
mirror__enabled=True,
|
|
138
|
+
mirror__repo__enabled=True)
|
|
134
139
|
return Package.objects.select_related().filter(hostrepos_q).distinct()
|
|
135
140
|
|
|
136
141
|
def process_update(self, package, highest_package):
|
|
@@ -221,10 +226,21 @@ class Host(models.Model):
|
|
|
221
226
|
priority = best_repo.priority
|
|
222
227
|
|
|
223
228
|
# find the packages that are potential updates
|
|
224
|
-
pu_q = Q(name=package.name,
|
|
229
|
+
pu_q = Q(name=package.name,
|
|
230
|
+
arch=package.arch,
|
|
225
231
|
packagetype=package.packagetype)
|
|
226
232
|
potential_updates = repo_packages.filter(pu_q)
|
|
227
233
|
for pu in potential_updates:
|
|
234
|
+
pu_is_module_package = False
|
|
235
|
+
pu_in_enabled_modules = False
|
|
236
|
+
if pu.module_set.exists():
|
|
237
|
+
pu_is_module_package = True
|
|
238
|
+
for module in pu.module_set.all():
|
|
239
|
+
if module in self.modules.all():
|
|
240
|
+
pu_in_enabled_modules = True
|
|
241
|
+
if pu_is_module_package:
|
|
242
|
+
if not pu_in_enabled_modules:
|
|
243
|
+
continue
|
|
228
244
|
if highest_package.compare_version(pu) == -1 \
|
|
229
245
|
and package.compare_version(pu) == -1:
|
|
230
246
|
|
|
@@ -257,6 +273,16 @@ class Host(models.Model):
|
|
|
257
273
|
packagetype=package.packagetype)
|
|
258
274
|
potential_updates = repo_packages.filter(pu_q)
|
|
259
275
|
for pu in potential_updates:
|
|
276
|
+
pu_is_module_package = False
|
|
277
|
+
pu_in_enabled_modules = False
|
|
278
|
+
if pu.module_set.exists():
|
|
279
|
+
pu_is_module_package = True
|
|
280
|
+
for module in pu.module_set.all():
|
|
281
|
+
if module in self.modules.all():
|
|
282
|
+
pu_in_enabled_modules = True
|
|
283
|
+
if pu_is_module_package:
|
|
284
|
+
if not pu_in_enabled_modules:
|
|
285
|
+
continue
|
|
260
286
|
if highest_package.compare_version(pu) == -1 \
|
|
261
287
|
and package.compare_version(pu) == -1:
|
|
262
288
|
highest_package = pu
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<li class="active"><a data-toggle="tab" href="#host_details">Details</a></li>
|
|
15
15
|
<li><a data-toggle="tab" href="#host_updates">Updates Available</a></li>
|
|
16
16
|
<li><a data-toggle="tab" href="#host_repos">Repos</a></li>
|
|
17
|
+
<li><a data-toggle="tab" href="#host_modules">Modules</a></li>
|
|
17
18
|
<li><a data-toggle="tab" href="#host_packages">Packages</a></li>
|
|
18
19
|
</ul>
|
|
19
20
|
|
|
@@ -135,6 +136,14 @@
|
|
|
135
136
|
</div>
|
|
136
137
|
</div>
|
|
137
138
|
|
|
139
|
+
<div class="tab-pane fade in" id="host_modules">
|
|
140
|
+
<div class="well well-sm">
|
|
141
|
+
<div class="well">
|
|
142
|
+
{% gen_table host.modules.all %}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
138
147
|
<div class="tab-pane fade in" id="host_packages">
|
|
139
148
|
<div class="well well-sm">
|
|
140
149
|
<button class="btn btn-primary btn-sm" data-toggle="collapse" data-target="#packages">Show/Hide Installed Packages</button>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Copyright 2023 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.contrib import admin
|
|
18
|
+
from modules.models import Module
|
|
19
|
+
|
|
20
|
+
admin.site.register(Module)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2023 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.apps import AppConfig
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ModulesConfig(AppConfig):
|
|
21
|
+
name = 'modules'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright 2023 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 models
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ModuleManager(models.Manager):
|
|
21
|
+
def get_queryset(self):
|
|
22
|
+
return super().get_queryset().select_related()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Copyright 2023 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 models
|
|
18
|
+
from django.urls import reverse
|
|
19
|
+
|
|
20
|
+
from arch.models import PackageArchitecture
|
|
21
|
+
from packages.models import Package
|
|
22
|
+
from repos.models import Repository
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Module(models.Model):
|
|
26
|
+
|
|
27
|
+
name = models.CharField(unique=True, max_length=255)
|
|
28
|
+
stream = models.CharField(unique=True, max_length=255)
|
|
29
|
+
version = models.CharField(max_length=255)
|
|
30
|
+
context = models.CharField(unique=True, max_length=255)
|
|
31
|
+
arch = models.ForeignKey(PackageArchitecture, on_delete=models.CASCADE)
|
|
32
|
+
repo = models.ForeignKey(Repository, on_delete=models.CASCADE)
|
|
33
|
+
packages = models.ManyToManyField(Package, blank=True)
|
|
34
|
+
|
|
35
|
+
class Meta:
|
|
36
|
+
verbose_name = 'Module'
|
|
37
|
+
verbose_name_plural = 'Modules'
|
|
38
|
+
ordering = ('name',)
|
|
39
|
+
|
|
40
|
+
def __str__(self):
|
|
41
|
+
return self.name
|
|
42
|
+
|
|
43
|
+
def get_absolute_url(self):
|
|
44
|
+
return reverse('modules:module_detail', args=[str(self.id)])
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Copyright 2023 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 rest_framework import serializers
|
|
18
|
+
|
|
19
|
+
from modules.models import Module
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ModuleSerializer(serializers.HyperlinkedModelSerializer):
|
|
23
|
+
class Meta:
|
|
24
|
+
model = Module
|
|
25
|
+
fields = ('id', 'name', 'stream', 'version', 'context', 'arch', 'repo', 'packages')
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% load common bootstrap3 %}
|
|
4
|
+
|
|
5
|
+
{% block page_title %}Module - {{ module }} {% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block breadcrumbs %} {{ block.super }} <li><a href="{% url 'modules:module_list' %}">Modules</a></li><li class="active">{{ module }}</li>{% endblock %}
|
|
8
|
+
|
|
9
|
+
{% block content_title %}Module - {{ module }} {% endblock %}
|
|
10
|
+
|
|
11
|
+
{% block content %}
|
|
12
|
+
|
|
13
|
+
<ul class="nav nav-tabs">
|
|
14
|
+
<li class="active"><a data-toggle="tab" href="#module_details">Details</a></li>
|
|
15
|
+
<li><a data-toggle="tab" href="#module_packages">Packages</a></li>
|
|
16
|
+
<li><a data-toggle="tab" href="#module_hosts">Hosts with this Module Enabled</a></li>
|
|
17
|
+
</ul>
|
|
18
|
+
|
|
19
|
+
<div class="tab-content">
|
|
20
|
+
<div class="tab-pane fade in active" id="module_details">
|
|
21
|
+
<div class="well well-sm">
|
|
22
|
+
<table class="table table-striped table-bordered table-hover table-condensed table-responsive">
|
|
23
|
+
<tr><th>Name</th><td> {{ module.name }} </td></tr>
|
|
24
|
+
<tr><th>ID</th><td> {{ module.id }} </td></tr>
|
|
25
|
+
<tr><th>Stream</th><td> {{ module.stream }} </td></tr>
|
|
26
|
+
<tr><th>Version</th><td> {{ module.version }} </td></tr>
|
|
27
|
+
<tr><th>Context</th><td> {{ module.context }} </td></tr>
|
|
28
|
+
<tr><th>Architecture</th><td> {{ module.arch }} </td></tr>
|
|
29
|
+
<tr><th>Repo</th><td><a href="{{ module.repo.get_absolute_url }}">{{ module.repo }}</a></td></tr>
|
|
30
|
+
</table>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="tab-pane fade in" id="module_packages">
|
|
35
|
+
<div class="well well-sm">
|
|
36
|
+
<table class="table table-striped table-bordered table-hover table-condensed table-responsive">
|
|
37
|
+
<thead>
|
|
38
|
+
<tr>
|
|
39
|
+
<th>Name</th>
|
|
40
|
+
<th>Epoch</th>
|
|
41
|
+
<th>Version</th>
|
|
42
|
+
<th>Release</th>
|
|
43
|
+
<th>Arch</th>
|
|
44
|
+
<th>Type</th>
|
|
45
|
+
</tr>
|
|
46
|
+
</thead>
|
|
47
|
+
<tbody>
|
|
48
|
+
{% for package in module.packages.all %}
|
|
49
|
+
<tr>
|
|
50
|
+
<td> {{ package.name }} </td>
|
|
51
|
+
<td> {{ package.epoch }} </td>
|
|
52
|
+
<td> {{ package.version }} </td>
|
|
53
|
+
<td> {{ package.release }} </td>
|
|
54
|
+
<td> {{ package.arch }} </td>
|
|
55
|
+
<td> {{ package.get_packagetype_display }} </td>
|
|
56
|
+
</tr>
|
|
57
|
+
{% endfor %}
|
|
58
|
+
</tbody>
|
|
59
|
+
</table>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="tab-pane fade in" id="module_hosts">
|
|
64
|
+
<div class="well well-sm">
|
|
65
|
+
{% gen_table module.host_set.all.distinct %}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
{% endblock %}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{% extends "objectlist.html" %}
|
|
2
|
+
|
|
3
|
+
{% block page_title %}Modules{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block breadcrumbs %} {{ block.super }} <li class="active"><a href="{% url 'modules:module_list' %}">Modules</a></li>{% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block content_title %} Modules {% endblock %}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{% load common %}
|
|
2
|
+
<table class="table table-striped table-bordered table-hover table-condensed table-responsive">
|
|
3
|
+
<thead>
|
|
4
|
+
<tr>
|
|
5
|
+
<th class="col-sm-auto">Module</th>
|
|
6
|
+
<th class="col-sm-auto">ID</th>
|
|
7
|
+
<th class="col-sm-auto">Repo</th>
|
|
8
|
+
<th class="col-sm-auto">Packages</th>
|
|
9
|
+
<th class="col-md-auto">Enabled on Hosts</th>
|
|
10
|
+
</tr>
|
|
11
|
+
</thead>
|
|
12
|
+
<tbody>
|
|
13
|
+
{% for module in object_list %}
|
|
14
|
+
<tr>
|
|
15
|
+
<td><a href="{{ module.get_absolute_url }}">{{ module }}</a></td>
|
|
16
|
+
<td>{{ module.id }}</td>
|
|
17
|
+
<td><a href="{{ module.repo.get_absolute_url }}">{{ module.repo }}</a></td>
|
|
18
|
+
<td>{{ module.packages.count }}</td>
|
|
19
|
+
<td>{{ module.host_set.count }}</td>
|
|
20
|
+
</tr>
|
|
21
|
+
{% endfor %}
|
|
22
|
+
</tbody>
|
|
23
|
+
</table>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copyright 2023 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.urls import path
|
|
18
|
+
|
|
19
|
+
from modules import views
|
|
20
|
+
|
|
21
|
+
app_name = 'modules'
|
|
22
|
+
|
|
23
|
+
urlpatterns = [
|
|
24
|
+
path('', views.module_list, name='module_list'),
|
|
25
|
+
path('<int:module_id>/', views.module_detail, name='module_detail'),
|
|
26
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Copyright 2023 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.shortcuts import get_object_or_404, render
|
|
18
|
+
from django.contrib.auth.decorators import login_required
|
|
19
|
+
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
20
|
+
from django.db.models import Q
|
|
21
|
+
|
|
22
|
+
from rest_framework import viewsets, permissions
|
|
23
|
+
|
|
24
|
+
from modules.models import Module
|
|
25
|
+
from modules.serializers import ModuleSerializer
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@login_required
|
|
29
|
+
def module_list(request):
|
|
30
|
+
|
|
31
|
+
modules = Module.objects.select_related()
|
|
32
|
+
|
|
33
|
+
if 'search' in request.GET:
|
|
34
|
+
terms = request.GET['search'].lower()
|
|
35
|
+
query = Q()
|
|
36
|
+
for term in terms.split(' '):
|
|
37
|
+
q = Q(name__icontains=term)
|
|
38
|
+
query = query & q
|
|
39
|
+
modules = modules.filter(query)
|
|
40
|
+
else:
|
|
41
|
+
terms = ''
|
|
42
|
+
|
|
43
|
+
page_no = request.GET.get('page')
|
|
44
|
+
paginator = Paginator(modules, 50)
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
page = paginator.page(page_no)
|
|
48
|
+
except PageNotAnInteger:
|
|
49
|
+
page = paginator.page(1)
|
|
50
|
+
except EmptyPage:
|
|
51
|
+
page = paginator.page(paginator.num_pages)
|
|
52
|
+
|
|
53
|
+
return render(request,
|
|
54
|
+
'modules/module_list.html',
|
|
55
|
+
{'page': page,
|
|
56
|
+
'terms': terms}, )
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@login_required
|
|
60
|
+
def module_detail(request, module_id):
|
|
61
|
+
|
|
62
|
+
module = get_object_or_404(Module, id=module_id)
|
|
63
|
+
return render(request,
|
|
64
|
+
'modules/module_detail.html',
|
|
65
|
+
{'module': module}, )
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ModuleViewSet(viewsets.ModelViewSet):
|
|
69
|
+
"""
|
|
70
|
+
API endpoint that allows modules to be viewed or edited.
|
|
71
|
+
"""
|
|
72
|
+
queryset = Module.objects.all()
|
|
73
|
+
serializer_class = ModuleSerializer
|
|
74
|
+
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|