Compare commits

...

11 Commits

Author SHA1 Message Date
pedro a67fda6b51 better printing of DOMAIN var
when in settings, any command prints again DOMAIN which is boring and
inefficient
2024-11-05 04:01:44 +01:00
pedro 79a34c9b55 logger: always do traceback when DEBUG var is True
related to #13
2024-11-05 03:43:18 +01:00
Cayo Puigdefabregas e4124fb20b fix duplicate logs 2024-10-31 15:25:38 +01:00
pedro 517c3eb0c0 DEBUG false -> true
we are not ready to deploy without DEBUG

- collect static is not configured
- current demo in debug helps to find problems easily
2024-10-31 15:04:28 +01:00
pedro d4f50961bc improve logging text for certain messages 2024-10-31 14:24:16 +01:00
Cayo Puigdefabregas 65bd88a2a2 table in evidence page details 2024-10-31 13:15:56 +01:00
Cayo Puigdefabregas 7926943947 remove dashboard.js in login template 2024-10-31 12:51:49 +01:00
pedro 9de7dc6647 docker: disable debug by default 2024-10-31 11:57:33 +01:00
Cayo Puigdefabregas 16ba03bd0a fix 2024-10-31 10:40:53 +01:00
Cayo Puigdefabregas 4b3471d24e fix bug 2024-10-31 10:24:15 +01:00
Cayo Puigdefabregas e74ddc47a7 extract logs with colors and depending of DEBUG var 2024-10-31 10:14:02 +01:00
13 changed files with 158 additions and 67 deletions

View File

@ -1,11 +1,12 @@
DOMAIN=localhost DOMAIN=localhost
DEMO=false # note that with DEBUG=true, logs are more verbose (include tracebacks)
DEBUG=true
DEMO=true
STATIC_ROOT=/tmp/static/ STATIC_ROOT=/tmp/static/
MEDIA_ROOT=/tmp/media/ MEDIA_ROOT=/tmp/media/
ALLOWED_HOSTS=localhost,localhost:8000,127.0.0.1, ALLOWED_HOSTS=localhost,localhost:8000,127.0.0.1,
DOMAIN=localhost DOMAIN=localhost
DEBUG=True
EMAIL_HOST="mail.example.org" EMAIL_HOST="mail.example.org"
EMAIL_HOST_USER="fillme_noreply" EMAIL_HOST_USER="fillme_noreply"
EMAIL_HOST_PASSWORD="fillme_passwd" EMAIL_HOST_PASSWORD="fillme_passwd"

View File

@ -5,6 +5,7 @@ import logging
from uuid import uuid4 from uuid import uuid4
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.conf import settings
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -41,20 +42,20 @@ class ApiMixing(View):
# Authentication # Authentication
auth_header = self.request.headers.get('Authorization') auth_header = self.request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '): if not auth_header or not auth_header.startswith('Bearer '):
logger.exception("Invalid or missing token {}".format(auth_header)) logger.error("Invalid or missing token %s", auth_header)
return JsonResponse({'error': 'Invalid or missing token'}, status=401) return JsonResponse({'error': 'Invalid or missing token'}, status=401)
token = auth_header.split(' ')[1].strip("'").strip('"') token = auth_header.split(' ')[1].strip("'").strip('"')
try: try:
uuid.UUID(token) uuid.UUID(token)
except Exception: except Exception:
logger.exception("Invalid token {}".format(token)) logger.error("Invalid or missing token %s", token)
return JsonResponse({'error': 'Invalid or missing token'}, status=401) return JsonResponse({'error': 'Invalid or missing token'}, status=401)
self.tk = Token.objects.filter(token=token).first() self.tk = Token.objects.filter(token=token).first()
if not self.tk: if not self.tk:
logger.exception("Invalid or missing token {}".format(token)) logger.error("Invalid or missing token %s", token)
return JsonResponse({'error': 'Invalid or missing token'}, status=401) return JsonResponse({'error': 'Invalid or missing token'}, status=401)
@ -72,7 +73,8 @@ class NewSnapshotView(ApiMixing):
try: try:
data = json.loads(request.body) data = json.loads(request.body)
except json.JSONDecodeError: except json.JSONDecodeError:
logger.exception("Invalid Snapshot of user {}".format(self.tk.owner)) txt = "error: the snapshot is not a json"
logger.error("%s", txt)
return JsonResponse({'error': 'Invalid JSON'}, status=500) return JsonResponse({'error': 'Invalid JSON'}, status=500)
# Process snapshot # Process snapshot
@ -85,7 +87,7 @@ class NewSnapshotView(ApiMixing):
if not data.get("uuid"): if not data.get("uuid"):
txt = "error: the snapshot not have uuid" txt = "error: the snapshot not have uuid"
logger.exception(txt) logger.error("%s", txt)
return JsonResponse({'status': txt}, status=500) return JsonResponse({'status': txt}, status=500)
exist_annotation = Annotation.objects.filter( exist_annotation = Annotation.objects.filter(
@ -94,15 +96,20 @@ class NewSnapshotView(ApiMixing):
if exist_annotation: if exist_annotation:
txt = "error: the snapshot {} exist".format(data['uuid']) txt = "error: the snapshot {} exist".format(data['uuid'])
logger.exception(txt) logger.warning("%s", txt)
return JsonResponse({'status': txt}, status=500) return JsonResponse({'status': txt}, status=500)
try: try:
Build(data, self.tk.owner) Build(data, self.tk.owner)
except Exception as err: except Exception as err:
logger.exception(err) if settings.DEBUG:
return JsonResponse({'status': f"fail: {err}"}, status=500) logger.exception("%s", err)
snapshot_id = data.get("uuid", "")
txt = "It is not possible to parse snapshot: %s."
logger.error(txt, snapshot_id)
text = "fail: It is not possible to parse snapshot"
return JsonResponse({'status': text}, status=500)
annotation = Annotation.objects.filter( annotation = Annotation.objects.filter(
uuid=data['uuid'], uuid=data['uuid'],
@ -114,7 +121,7 @@ class NewSnapshotView(ApiMixing):
if not annotation: if not annotation:
logger.exception("Error: No annotation for uuid: {}".format(data["uuid"])) logger.error("Error: No annotation for uuid: %s", data["uuid"])
return JsonResponse({'status': 'fail'}, status=500) return JsonResponse({'status': 'fail'}, status=500)
url_args = reverse_lazy("device:details", args=(annotation.value,)) url_args = reverse_lazy("device:details", args=(annotation.value,))
@ -286,7 +293,7 @@ class AddAnnotationView(ApiMixing):
key = data["key"] key = data["key"]
value = data["value"] value = data["value"]
except Exception: except Exception:
logger.exception("Invalid Snapshot of user {}".format(self.tk.owner)) logger.error("Invalid Snapshot of user %s", self.tk.owner)
return JsonResponse({'error': 'Invalid JSON'}, status=500) return JsonResponse({'error': 'Invalid JSON'}, status=500)
Annotation.objects.create( Annotation.objects.create(

View File

@ -17,6 +17,8 @@ from pathlib import Path
from django.contrib.messages import constants as messages from django.contrib.messages import constants as messages
from decouple import config, Csv from decouple import config, Csv
from utils.logger import CustomFormatter
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
@ -32,8 +34,6 @@ DEBUG = config('DEBUG', default=False, cast=bool)
DOMAIN = config("DOMAIN") DOMAIN = config("DOMAIN")
assert DOMAIN not in [None, ''], "DOMAIN var is MANDATORY" assert DOMAIN not in [None, ''], "DOMAIN var is MANDATORY"
# this var is very important, we print it
print("DOMAIN: " + DOMAIN)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=DOMAIN, cast=Csv()) ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=DOMAIN, cast=Csv())
assert DOMAIN in ALLOWED_HOSTS, f"DOMAIN {DOMAIN} is not in ALLOWED_HOSTS {ALLOWED_HOSTS}" assert DOMAIN in ALLOWED_HOSTS, f"DOMAIN {DOMAIN} is not in ALLOWED_HOSTS {ALLOWED_HOSTS}"
@ -205,12 +205,34 @@ LOGOUT_REDIRECT_URL = '/'
LOGGING = { LOGGING = {
"version": 1, "version": 1,
"disable_existing_loggers": False, "disable_existing_loggers": False,
'formatters': {
'colored': {
'()': CustomFormatter,
'format': '%(levelname)s %(asctime)s %(message)s'
},
},
"handlers": { "handlers": {
"console": {"level": "DEBUG", "class": "logging.StreamHandler"}, "console": {
"level": "DEBUG",
"class": "logging.StreamHandler",
"formatter": "colored"
},
}, },
"root": { "root": {
"handlers": ["console"], "handlers": ["console"],
"level": "DEBUG", "level": "DEBUG",
},
"loggers": {
"django": {
"handlers": ["console"],
"level": "INFO",
"propagate": False, # Asegura que no se reenvíen a los manejadores raíz
},
"django.request": {
"handlers": ["console"],
"level": "ERROR",
"propagate": False,
}
} }
} }

View File

@ -4,7 +4,7 @@ services:
build: build:
dockerfile: docker/devicehub-django.Dockerfile dockerfile: docker/devicehub-django.Dockerfile
environment: environment:
- DEBUG=true - DEBUG=${DEBUG:-false}
- DOMAIN=${DOMAIN:-localhost} - DOMAIN=${DOMAIN:-localhost}
- ALLOWED_HOSTS=${ALLOWED_HOSTS:-$DOMAIN} - ALLOWED_HOSTS=${ALLOWED_HOSTS:-$DOMAIN}
- DEMO=${DEMO:-false} - DEMO=${DEMO:-false}

View File

@ -18,6 +18,8 @@ deploy() {
if [ "${DEBUG:-}" = 'true' ]; then if [ "${DEBUG:-}" = 'true' ]; then
./manage.py print_settings ./manage.py print_settings
else
echo "DOMAIN: ${DOMAIN}"
fi fi
# detect if existing deployment (TODO only works with sqlite) # detect if existing deployment (TODO only works with sqlite)

View File

@ -36,10 +36,8 @@ class Command(BaseCommand):
continue continue
user = institution.user_set.filter(is_admin=True).first() user = institution.user_set.filter(is_admin=True).first()
if not user: if not user:
txt = "Error No there are Admins for the institution: {}".format( txt = "No there are Admins for the institution: %s"
institution.name logger.warning(txt, institution.name)
)
logger.exception(txt)
continue continue
snapshots_path = os.path.join(filepath, "snapshots") snapshots_path = os.path.join(filepath, "snapshots")
@ -74,13 +72,12 @@ class Command(BaseCommand):
create_index(s, user) create_index(s, user)
create_annotation(s, user, commit=True) create_annotation(s, user, commit=True)
except Exception as err: except Exception as err:
txt = "Error: in placeholder {} \n{}".format(f_path, err) txt = "In placeholder %s \n%s"
logger.exception(txt) logger.warning(txt, f_path, err)
def build_snapshot(self, s, user, f_path): def build_snapshot(self, s, user, f_path):
try: try:
Build(s, user) Build(s, user)
except Exception as err: except Exception:
txt = "Error: in Snapshot {} \n{}".format(f_path, err) txt = "Error: in Snapshot {}".format(f_path)
logger.exception(txt) logger.error(txt)

View File

@ -4,6 +4,9 @@ import logging
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.conf import settings
from utils.save_snapshots import move_json, save_in_disk
from evidence.parse import Build from evidence.parse import Build
@ -46,12 +49,15 @@ class Command(BaseCommand):
def open(self, filepath): def open(self, filepath):
with open(filepath, 'r') as file: with open(filepath, 'r') as file:
content = json.loads(file.read()) content = json.loads(file.read())
self.snapshots.append(content) path_name = save_in_disk(content, self.user.institution.name)
self.snapshots.append((content, path_name))
def parsing(self): def parsing(self):
for s in self.snapshots: for s, p in self.snapshots:
try: try:
self.devices.append(Build(s, self.user)) self.devices.append(Build(s, self.user))
move_json(p, self.user.institution.name)
except Exception as err: except Exception as err:
logger.exception(err) snapshot_id = s.get("uuid", "")
txt = "Could not parse snapshot: %s"
logger.error(txt, snapshot_id)

View File

@ -33,7 +33,7 @@ def get_mac(lshw):
try: try:
get_network_cards(hw, nets) get_network_cards(hw, nets)
except Exception as ss: except Exception as ss:
print("WARNING!! {}".format(ss)) logger.warning("%s", ss)
return return
nets_sorted = sorted(nets, key=lambda x: x['businfo']) nets_sorted = sorted(nets, key=lambda x: x['businfo'])
@ -90,8 +90,8 @@ class Build:
) )
if annotation: if annotation:
txt = "Warning: Snapshot {} exist as annotation !!".format(self.uuid) txt = "Warning: Snapshot %s already registered (annotation exists)"
logger.exception(txt) logger.warning(txt, self.uuid)
return return
for k, v in self.algorithms.items(): for k, v in self.algorithms.items():
@ -135,9 +135,7 @@ class Build:
# mac = get_mac2(hwinfo_raw) or "" # mac = get_mac2(hwinfo_raw) or ""
mac = get_mac(lshw) or "" mac = get_mac(lshw) or ""
if not mac: if not mac:
print(f"WARNING: Could not retrieve MAC address in snapshot {snapshot['uuid']}" ) txt = "Could not retrieve MAC address in snapshot %s"
# TODO generate system annotation for that snapshot logger.warning(txt, snapshot['uuid'])
else:
print(f"{manufacturer}{model}{chassis}{serial_number}{sku}{mac}")
return f"{manufacturer}{model}{chassis}{serial_number}{sku}{mac}" return f"{manufacturer}{model}{chassis}{serial_number}{sku}{mac}"

View File

@ -1,4 +1,5 @@
import json import json
import logging
import numpy as np import numpy as np
from datetime import datetime from datetime import datetime
@ -8,6 +9,9 @@ from json_repair import repair_json
from utils.constants import CHASSIS_DH, DATASTORAGEINTERFACE from utils.constants import CHASSIS_DH, DATASTORAGEINTERFACE
logger = logging.getLogger('django')
def get_lshw_child(child, nets, component): def get_lshw_child(child, nets, component):
if child.get('id') == component: if child.get('id') == component:
nets.append(child) nets.append(child)
@ -483,12 +487,12 @@ class ParseSnapshot:
if isinstance(x, str): if isinstance(x, str):
try: try:
try: try:
hw = json.loads(lshw) hw = json.loads(x)
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
hw = json.loads(repair_json(lshw)) hw = json.loads(repair_json(x))
return hw return hw
except Exception as ss: except Exception as ss:
print("WARNING!! {}".format(ss)) logger.warning("%s", ss)
return {} return {}
return x return x
@ -497,5 +501,5 @@ class ParseSnapshot:
return self._errors return self._errors
logger.error(txt) logger.error(txt)
self._errors.append(txt) self._errors.append("%s", txt)

View File

@ -29,26 +29,44 @@
<div class="tab-content pt-2"> <div class="tab-content pt-2">
<div class="tab-pane fade show active" id="device"> <div class="tab-pane fade show active" id="device">
<h5 class="card-title">List of chids</h5> <h5 class="card-title"></h5>
<div class="list-group col-6"> <div class="list-group col-6">
{% for snap in object.annotations %} <table class="table">
{% if snap.type == 0 %} <thead>
<div class="list-group-item"> <tr>
<div class="d-flex w-100 justify-content-between"> <th scope="col" data-sortable="">
<h5 class="mb-1"></h5> {% trans "Type" %}
<small class="text-muted"> </th>
{{ snap.created }} <th scope="col" data-sortable="">
</small> {% trans "Identificator" %}
</div> </th>
<p class="mb-1"> <th scope="col" data-sortable="">
{{ snap.key }}<br /> {% trans "Data" %}
</p> </th>
<small class="text-muted"> </tr>
<a href="{% url 'device:details' snap.value %}">{{ snap.value }}</a> </thead>
</small> {% for snap in object.annotations %}
</div> <tbody>
{% endif %} {% if snap.type == 0 %}
{% endfor %} <tr>
<td>
{{ snap.key }}
</td>
<td>
<small class="text-muted">
<a href="{% url 'device:details' snap.value %}">{{ snap.value }}</a>
</small>
</td>
<td>
<small class="text-muted">
{{ snap.created }}
</small>
</td>
</tr>
{% endif %}
</tbody>
{% endfor %}
</table>
</div> </div>
</div> </div>
<div class="tab-pane fade" id="tag"> <div class="tab-pane fade" id="tag">

View File

@ -110,7 +110,6 @@
<script src="/static/js/bootstrap.bundle.min.js"></script> <script src="/static/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script>
<script src="/static/js/dashboard.js"></script>
<script> <script>
const togglePassword = document.querySelector('#togglePassword'); const togglePassword = document.querySelector('#togglePassword');
const password = document.querySelector('#id_password'); const password = document.querySelector('#id_password');

View File

@ -88,8 +88,8 @@ def create_annotation(doc, user, commit=False):
) )
if annotation: if annotation:
txt = "Warning: Snapshot {} exist as annotation !!".format(doc["uuid"]) txt = "Warning: Snapshot %s already registered (annotation exists)"
logger.exception(txt) logger.warning(txt, doc["uuid"])
return annotation return annotation
return Annotation.objects.create(**data) return Annotation.objects.create(**data)

37
utils/logger.py Normal file
View File

@ -0,0 +1,37 @@
import logging
from django.conf import settings
# Colors
RED = "\033[91m"
PURPLE = "\033[95m"
YELLOW = "\033[93m"
RESET = "\033[0m"
class CustomFormatter(logging.Formatter):
def format(self, record):
if record.levelname == "ERROR":
color = RED
elif record.levelname == "WARNING":
color = PURPLE
elif record.levelname in ["INFO", "DEBUG"]:
color = YELLOW
else:
color = RESET
record.levelname = f"{color}{record.levelname}{RESET}"
if record.args:
record.msg = self.highlight_args(record.msg, record.args, color)
record.args = ()
# provide trace when DEBUG config
if settings.DEBUG:
import traceback
print(traceback.format_exc())
return super().format(record)
def highlight_args(self, message, args, color):
highlighted_args = tuple(f"{color}{arg}{RESET}" for arg in args)
return message % highlighted_args