summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/toastermain
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/toaster/toastermain')
-rw-r--r--bitbake/lib/toaster/toastermain/logs.py158
-rw-r--r--bitbake/lib/toaster/toastermain/management/commands/buildimport.py4
-rw-r--r--bitbake/lib/toaster/toastermain/management/commands/checksocket.py4
-rw-r--r--bitbake/lib/toaster/toastermain/settings.py85
-rw-r--r--bitbake/lib/toaster/toastermain/settings_test.py4
-rw-r--r--bitbake/lib/toaster/toastermain/urls.py4
6 files changed, 206 insertions, 53 deletions
diff --git a/bitbake/lib/toaster/toastermain/logs.py b/bitbake/lib/toaster/toastermain/logs.py
new file mode 100644
index 0000000000..62d871963a
--- /dev/null
+++ b/bitbake/lib/toaster/toastermain/logs.py
@@ -0,0 +1,158 @@
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4import os
5import logging
6import json
7from pathlib import Path
8from django.http import HttpRequest
9
10BUILDDIR = Path(os.environ.get('BUILDDIR', '/tmp'))
11
12def log_api_request(request, response, view, logger_name='api'):
13 """Helper function for LogAPIMixin"""
14
15 repjson = {
16 'view': view,
17 'path': request.path,
18 'method': request.method,
19 'status': response.status_code
20 }
21
22 logger = logging.getLogger(logger_name)
23 logger.info(
24 json.dumps(repjson, indent=4, separators=(", ", " : "))
25 )
26
27
28def log_view_mixin(view):
29 def log_view_request(*args, **kwargs):
30 # get request from args else kwargs
31 request = None
32 if len(args) > 0:
33 for req in args:
34 if isinstance(req, HttpRequest):
35 request = req
36 break
37 elif request is None:
38 request = kwargs.get('request')
39
40 response = view(*args, **kwargs)
41 view_name = 'unknown'
42 if hasattr(request, 'resolver_match'):
43 if hasattr(request.resolver_match, 'view_name'):
44 view_name = request.resolver_match.view_name
45
46 log_api_request(
47 request, response, view_name, 'toaster')
48 return response
49 return log_view_request
50
51
52
53class LogAPIMixin:
54 """Logs API requests
55
56 tested with:
57 - APIView
58 - ModelViewSet
59 - ReadOnlyModelViewSet
60 - GenericAPIView
61
62 Note: you can set `view_name` attribute in View to override get_view_name()
63 """
64
65 def get_view_name(self):
66 if hasattr(self, 'view_name'):
67 return self.view_name
68 return super().get_view_name()
69
70 def finalize_response(self, request, response, *args, **kwargs):
71 log_api_request(request, response, self.get_view_name())
72 return super().finalize_response(request, response, *args, **kwargs)
73
74
75LOGGING_SETTINGS = {
76 'version': 1,
77 'disable_existing_loggers': False,
78 'filters': {
79 'require_debug_false': {
80 '()': 'django.utils.log.RequireDebugFalse'
81 }
82 },
83 'formatters': {
84 'datetime': {
85 'format': '%(asctime)s %(levelname)s %(message)s'
86 },
87 'verbose': {
88 'format': '{levelname} {asctime} {module} {name}.{funcName} {process:d} {thread:d} {message}',
89 'datefmt': "%d/%b/%Y %H:%M:%S",
90 'style': '{',
91 },
92 'api': {
93 'format': '\n{levelname} {asctime} {name}.{funcName}:\n{message}',
94 'style': '{'
95 }
96 },
97 'handlers': {
98 'mail_admins': {
99 'level': 'ERROR',
100 'filters': ['require_debug_false'],
101 'class': 'django.utils.log.AdminEmailHandler'
102 },
103 'console': {
104 'level': 'DEBUG',
105 'class': 'logging.StreamHandler',
106 'formatter': 'datetime',
107 },
108 'file_django': {
109 'level': 'INFO',
110 'class': 'logging.handlers.TimedRotatingFileHandler',
111 'filename': BUILDDIR / 'toaster_logs/django.log',
112 'when': 'D', # interval type
113 'interval': 1, # defaults to 1
114 'backupCount': 10, # how many files to keep
115 'formatter': 'verbose',
116 },
117 'file_api': {
118 'level': 'INFO',
119 'class': 'logging.handlers.TimedRotatingFileHandler',
120 'filename': BUILDDIR / 'toaster_logs/api.log',
121 'when': 'D',
122 'interval': 1,
123 'backupCount': 10,
124 'formatter': 'verbose',
125 },
126 'file_toaster': {
127 'level': 'INFO',
128 'class': 'logging.handlers.TimedRotatingFileHandler',
129 'filename': BUILDDIR / 'toaster_logs/web.log',
130 'when': 'D',
131 'interval': 1,
132 'backupCount': 10,
133 'formatter': 'verbose',
134 },
135 },
136 'loggers': {
137 'django.request': {
138 'handlers': ['file_django', 'console'],
139 'level': 'WARN',
140 'propagate': True,
141 },
142 'django': {
143 'handlers': ['file_django', 'console'],
144 'level': 'WARNING',
145 'propogate': True,
146 },
147 'toaster': {
148 'handlers': ['file_toaster'],
149 'level': 'INFO',
150 'propagate': False,
151 },
152 'api': {
153 'handlers': ['file_api'],
154 'level': 'INFO',
155 'propagate': False,
156 }
157 }
158}
diff --git a/bitbake/lib/toaster/toastermain/management/commands/buildimport.py b/bitbake/lib/toaster/toastermain/management/commands/buildimport.py
index 59da6ff7ac..f7139aa041 100644
--- a/bitbake/lib/toaster/toastermain/management/commands/buildimport.py
+++ b/bitbake/lib/toaster/toastermain/management/commands/buildimport.py
@@ -451,7 +451,7 @@ class Command(BaseCommand):
451 # Catch vars relevant to Toaster (in case no Toaster section) 451 # Catch vars relevant to Toaster (in case no Toaster section)
452 self.update_project_vars(project,'DISTRO') 452 self.update_project_vars(project,'DISTRO')
453 self.update_project_vars(project,'MACHINE') 453 self.update_project_vars(project,'MACHINE')
454 self.update_project_vars(project,'IMAGE_INSTALL_append') 454 self.update_project_vars(project,'IMAGE_INSTALL:append')
455 self.update_project_vars(project,'IMAGE_FSTYPES') 455 self.update_project_vars(project,'IMAGE_FSTYPES')
456 self.update_project_vars(project,'PACKAGE_CLASSES') 456 self.update_project_vars(project,'PACKAGE_CLASSES')
457 # These vars are typically only assigned by Toaster 457 # These vars are typically only assigned by Toaster
@@ -545,7 +545,7 @@ class Command(BaseCommand):
545 # Find the directory's release, and promote to default_release if local paths 545 # Find the directory's release, and promote to default_release if local paths
546 release = self.find_import_release(layers_list,lv_dict,default_release) 546 release = self.find_import_release(layers_list,lv_dict,default_release)
547 # create project, SANITY: reuse any project of same name 547 # create project, SANITY: reuse any project of same name
548 project = Project.objects.create_project(project_name,release,project) 548 project = Project.objects.create_project(project_name,release,project, imported=True)
549 # Apply any new layers or variables 549 # Apply any new layers or variables
550 self.apply_conf_variables(project,layers_list,lv_dict,release) 550 self.apply_conf_variables(project,layers_list,lv_dict,release)
551 # WORKAROUND: since we now derive the release, redirect 'newproject_specific' to 'project_specific' 551 # WORKAROUND: since we now derive the release, redirect 'newproject_specific' to 'project_specific'
diff --git a/bitbake/lib/toaster/toastermain/management/commands/checksocket.py b/bitbake/lib/toaster/toastermain/management/commands/checksocket.py
index 811fd5d516..b2c002da7a 100644
--- a/bitbake/lib/toaster/toastermain/management/commands/checksocket.py
+++ b/bitbake/lib/toaster/toastermain/management/commands/checksocket.py
@@ -13,7 +13,7 @@ import errno
13import socket 13import socket
14 14
15from django.core.management.base import BaseCommand, CommandError 15from django.core.management.base import BaseCommand, CommandError
16from django.utils.encoding import force_text 16from django.utils.encoding import force_str
17 17
18DEFAULT_ADDRPORT = "0.0.0.0:8000" 18DEFAULT_ADDRPORT = "0.0.0.0:8000"
19 19
@@ -51,7 +51,7 @@ class Command(BaseCommand):
51 if hasattr(err, 'errno') and err.errno in errors: 51 if hasattr(err, 'errno') and err.errno in errors:
52 errtext = errors[err.errno] 52 errtext = errors[err.errno]
53 else: 53 else:
54 errtext = force_text(err) 54 errtext = force_str(err)
55 raise CommandError(errtext) 55 raise CommandError(errtext)
56 56
57 self.stdout.write("OK") 57 self.stdout.write("OK")
diff --git a/bitbake/lib/toaster/toastermain/settings.py b/bitbake/lib/toaster/toastermain/settings.py
index a4b370c8d4..e06adc5a93 100644
--- a/bitbake/lib/toaster/toastermain/settings.py
+++ b/bitbake/lib/toaster/toastermain/settings.py
@@ -9,6 +9,8 @@
9# Django settings for Toaster project. 9# Django settings for Toaster project.
10 10
11import os 11import os
12from pathlib import Path
13from toastermain.logs import LOGGING_SETTINGS
12 14
13DEBUG = True 15DEBUG = True
14 16
@@ -39,6 +41,9 @@ DATABASES = {
39 } 41 }
40} 42}
41 43
44# New in Django 3.2
45DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
46
42# Needed when Using sqlite especially to add a longer timeout for waiting 47# Needed when Using sqlite especially to add a longer timeout for waiting
43# for the database lock to be released 48# for the database lock to be released
44# https://docs.djangoproject.com/en/1.6/ref/databases/#database-is-locked-errors 49# https://docs.djangoproject.com/en/1.6/ref/databases/#database-is-locked-errors
@@ -84,14 +89,17 @@ else:
84 from pytz.exceptions import UnknownTimeZoneError 89 from pytz.exceptions import UnknownTimeZoneError
85 try: 90 try:
86 if pytz.timezone(zonename) is not None: 91 if pytz.timezone(zonename) is not None:
87 zonefilelist[hashlib.md5(open(filepath, 'rb').read()).hexdigest()] = zonename 92 with open(filepath, 'rb') as f:
93 zonefilelist[hashlib.md5(f.read()).hexdigest()] = zonename
88 except UnknownTimeZoneError as ValueError: 94 except UnknownTimeZoneError as ValueError:
89 # we expect timezone failures here, just move over 95 # we expect timezone failures here, just move over
90 pass 96 pass
91 except ImportError: 97 except ImportError:
92 zonefilelist[hashlib.md5(open(filepath, 'rb').read()).hexdigest()] = zonename 98 with open(filepath, 'rb') as f:
99 zonefilelist[hashlib.md5(f.read()).hexdigest()] = zonename
93 100
94 TIME_ZONE = zonefilelist[hashlib.md5(open('/etc/localtime', 'rb').read()).hexdigest()] 101 with open('/etc/localtime', 'rb') as f:
102 TIME_ZONE = zonefilelist[hashlib.md5(f.read()).hexdigest()]
95 103
96# Language code for this installation. All choices can be found here: 104# Language code for this installation. All choices can be found here:
97# http://www.i18nguy.com/unicode/language-identifiers.html 105# http://www.i18nguy.com/unicode/language-identifiers.html
@@ -103,10 +111,6 @@ SITE_ID = 1
103# to load the internationalization machinery. 111# to load the internationalization machinery.
104USE_I18N = True 112USE_I18N = True
105 113
106# If you set this to False, Django will not format dates, numbers and
107# calendars according to the current locale.
108USE_L10N = True
109
110# If you set this to False, Django will not use timezone-aware datetimes. 114# If you set this to False, Django will not use timezone-aware datetimes.
111USE_TZ = True 115USE_TZ = True
112 116
@@ -147,6 +151,8 @@ STATICFILES_FINDERS = (
147# Make this unique, and don't share it with anybody. 151# Make this unique, and don't share it with anybody.
148SECRET_KEY = 'NOT_SUITABLE_FOR_HOSTED_DEPLOYMENT' 152SECRET_KEY = 'NOT_SUITABLE_FOR_HOSTED_DEPLOYMENT'
149 153
154TMPDIR = os.environ.get('TOASTER_DJANGO_TMPDIR', '/tmp')
155
150class InvalidString(str): 156class InvalidString(str):
151 def __mod__(self, other): 157 def __mod__(self, other):
152 from django.template.base import TemplateSyntaxError 158 from django.template.base import TemplateSyntaxError
@@ -183,7 +189,13 @@ TEMPLATES = [
183 'django.template.loaders.app_directories.Loader', 189 'django.template.loaders.app_directories.Loader',
184 #'django.template.loaders.eggs.Loader', 190 #'django.template.loaders.eggs.Loader',
185 ], 191 ],
186 'string_if_invalid': InvalidString("%s"), 192 # https://docs.djangoproject.com/en/4.2/ref/templates/api/#how-invalid-variables-are-handled
193 # Generally, string_if_invalid should only be enabled in order to debug
194 # a specific template problem, then cleared once debugging is complete.
195 # If you assign a value other than '' to string_if_invalid,
196 # you will experience rendering problems with these templates and sites.
197 # 'string_if_invalid': InvalidString("%s"),
198 'string_if_invalid': "",
187 'debug': DEBUG, 199 'debug': DEBUG,
188 }, 200 },
189 }, 201 },
@@ -207,7 +219,7 @@ CACHES = {
207 # }, 219 # },
208 'default': { 220 'default': {
209 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 221 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
210 'LOCATION': '/tmp/toaster_cache_%d' % os.getuid(), 222 'LOCATION': '%s/toaster_cache_%d' % (TMPDIR, os.getuid()),
211 'TIMEOUT': 1, 223 'TIMEOUT': 1,
212 } 224 }
213 } 225 }
@@ -239,6 +251,9 @@ INSTALLED_APPS = (
239 'django.contrib.humanize', 251 'django.contrib.humanize',
240 'bldcollector', 252 'bldcollector',
241 'toastermain', 253 'toastermain',
254
255 # 3rd-lib
256 "log_viewer",
242) 257)
243 258
244 259
@@ -299,43 +314,21 @@ for t in os.walk(os.path.dirname(currentdir)):
299# the site admins on every HTTP 500 error when DEBUG=False. 314# the site admins on every HTTP 500 error when DEBUG=False.
300# See http://docs.djangoproject.com/en/dev/topics/logging for 315# See http://docs.djangoproject.com/en/dev/topics/logging for
301# more details on how to customize your logging configuration. 316# more details on how to customize your logging configuration.
302LOGGING = { 317LOGGING = LOGGING_SETTINGS
303 'version': 1, 318
304 'disable_existing_loggers': False, 319# Build paths inside the project like this: BASE_DIR / 'subdir'.
305 'filters': { 320BUILDDIR = os.environ.get("BUILDDIR", TMPDIR)
306 'require_debug_false': { 321
307 '()': 'django.utils.log.RequireDebugFalse' 322# LOG VIEWER
308 } 323# https://pypi.org/project/django-log-viewer/
309 }, 324LOG_VIEWER_FILES_PATTERN = '*.log*'
310 'formatters': { 325LOG_VIEWER_FILES_DIR = os.path.join(BUILDDIR, "toaster_logs/")
311 'datetime': { 326LOG_VIEWER_PAGE_LENGTH = 25 # total log lines per-page
312 'format': '%(asctime)s %(levelname)s %(message)s' 327LOG_VIEWER_MAX_READ_LINES = 100000 # total log lines will be read
313 } 328LOG_VIEWER_PATTERNS = ['INFO', 'DEBUG', 'WARNING', 'ERROR', 'CRITICAL']
314 }, 329
315 'handlers': { 330# Optionally you can set the next variables in order to customize the admin:
316 'mail_admins': { 331LOG_VIEWER_FILE_LIST_TITLE = "Logs list"
317 'level': 'ERROR',
318 'filters': ['require_debug_false'],
319 'class': 'django.utils.log.AdminEmailHandler'
320 },
321 'console': {
322 'level': 'DEBUG',
323 'class': 'logging.StreamHandler',
324 'formatter': 'datetime',
325 }
326 },
327 'loggers': {
328 'toaster' : {
329 'handlers': ['console'],
330 'level': 'DEBUG',
331 },
332 'django.request': {
333 'handlers': ['console'],
334 'level': 'WARN',
335 'propagate': True,
336 },
337 }
338}
339 332
340if DEBUG and SQL_DEBUG: 333if DEBUG and SQL_DEBUG:
341 LOGGING['loggers']['django.db.backends'] = { 334 LOGGING['loggers']['django.db.backends'] = {
diff --git a/bitbake/lib/toaster/toastermain/settings_test.py b/bitbake/lib/toaster/toastermain/settings_test.py
index 6538d9e453..74def2d240 100644
--- a/bitbake/lib/toaster/toastermain/settings_test.py
+++ b/bitbake/lib/toaster/toastermain/settings_test.py
@@ -19,10 +19,10 @@ TEMPLATE_DEBUG = DEBUG
19DATABASES = { 19DATABASES = {
20 'default': { 20 'default': {
21 'ENGINE': 'django.db.backends.sqlite3', 21 'ENGINE': 'django.db.backends.sqlite3',
22 'NAME': '/tmp/toaster-test-db.sqlite', 22 'NAME': '%s/toaster-test-db.sqlite' % TMPDIR,
23 'TEST': { 23 'TEST': {
24 'ENGINE': 'django.db.backends.sqlite3', 24 'ENGINE': 'django.db.backends.sqlite3',
25 'NAME': '/tmp/toaster-test-db.sqlite', 25 'NAME': '%s/toaster-test-db.sqlite' % TMPDIR,
26 } 26 }
27 } 27 }
28} 28}
diff --git a/bitbake/lib/toaster/toastermain/urls.py b/bitbake/lib/toaster/toastermain/urls.py
index 5fb520b384..3be46fcf0c 100644
--- a/bitbake/lib/toaster/toastermain/urls.py
+++ b/bitbake/lib/toaster/toastermain/urls.py
@@ -6,7 +6,7 @@
6# SPDX-License-Identifier: GPL-2.0-only 6# SPDX-License-Identifier: GPL-2.0-only
7# 7#
8 8
9from django.conf.urls import include, url 9from django.urls import re_path as url, include
10from django.views.generic import RedirectView, TemplateView 10from django.views.generic import RedirectView, TemplateView
11from django.views.decorators.cache import never_cache 11from django.views.decorators.cache import never_cache
12import bldcollector.views 12import bldcollector.views
@@ -28,6 +28,8 @@ urlpatterns = [
28 # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 28 # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
29 29
30 30
31 url(r'^logs/', include('log_viewer.urls')),
32
31 # This is here to maintain backward compatibility and will be deprecated 33 # This is here to maintain backward compatibility and will be deprecated
32 # in the future. 34 # in the future.
33 url(r'^orm/eventfile$', bldcollector.views.eventfile), 35 url(r'^orm/eventfile$', bldcollector.views.eventfile),