Compare commits

...

16 commits

Author SHA1 Message Date
pedro 65788b36af up_snapshots: simplify and clarify errors
specially, do not halt in case of errors, just print them in logs
2024-11-12 14:48:35 +01:00
pedro a748b900ca recover old up_snapshot functionality
- We don't need the if settings.DEBUG anymore, we have a logger
configuration that when DEBUG is there, a trace is always generated
- restore old error message that is shorter and clearer than the new one
2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki ac48a77a2f added success messages 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki ac70a4ae25 catching pandas exception 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki 8dade92f7e changed form validation onto field 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki b4c18535fb grammar fix and todo added 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki 90f057288d upload form now displays error messages once 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki 80083caf99 better error messages 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki 3763124440 minor fix in mac parsing 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki cd0a8217c8 fixed mac not recovered properly 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki 83dd044325 better error handling and logging 2024-11-12 14:48:35 +01:00
Thomas Nahuel Rusiecki ccb0be1b4c check for values on websnapshot 2024-11-12 14:48:35 +01:00
pedro eb81b65e5b logger: swap colors between INFO and WARNING
yellow is more a warning color than purple

purple is more relaxing, so better for info
2024-11-10 03:54:29 +01:00
pedro 9378621877 Merge pull request 'Add improved readme' (#26) from improve-readme into real_main
Reviewed-on: #26
2024-11-09 20:33:29 +00:00
pedro d136ae3aa3 README: small changes 2024-11-09 21:32:52 +01:00
sergio_gimenez 2653b0eeee Add improved redame 2024-11-09 21:32:52 +01:00
8 changed files with 172 additions and 58 deletions

121
README.md
View file

@ -1,20 +1,123 @@
# INSTALACIÓN: # Device Hub
La instalación es muy estándar DeviceHub is an IT Asset Management System focused on reusing devices, created under the [eReuse.org](https://www.ereuse.org) project.
## Overview
DeviceHub aims to:
- Provide a common IT Asset Management platform for donors, receivers, and IT professionals.
- Automatically collect, analyze, and share device metadata while ensuring privacy and traceability.
- Integrate with existing IT Asset Management Systems.
- Operate in a decentralized manner.
DeviceHub primarily works with three types of objects:
1. **Devices**: Including computers, smartphones, and their components.
2. **Events**: Actions performed on devices (e.g., Repair, Allocate).
3. **Accounts**: Users who perform events on devices.
## Installation
Assuming a host with debian stable
### Quickstart
For a quick start with dummy data in localhost, DeviceHub can be run directly with docker. To do so, from the root of the project run:
```bash
./docker-reset.sh
``` ```
Note that everytime you perform the `docker-reset.sh` script, all data is lost.
Also there is a demo running in http://demo.ereuse.org/. The token for accessing the instance will be always: `token=5018dd65-9abd-4a62-8896-80f34ac66150`, but the instance will be reset every day at 4 am.
For production needs, review and change .env file properly
## Running from baremetal
### Prerequisites
- Python 3.10
- pip
- virtualenv
Specially when developing, is quite convenient to run DeviceHub from a virtual environment. To start with this deployment, create a virtual environment to isolate our project dependencies:
```bash
python -m venv env python -m venv env
source env/bin/actevate source env/bin/activate
python install -r requirements.txt pip install -r requirements.txt
``` ```
## IMPORTANT EXTERNAL DEPENDENCIES ### System Dependencies
Para arrancarlo es necesario tener el paquete `xapian-bindings` en tu ordenador. No se instala mediante `pip`, así que depende de cada [sistema operativo](https://xapian.org/download). #### Xapian
Luego solo necesitas: Now, install the xapian dependencies (xapian library and python bindings)
```bash
sudo apt-get install python3-xapian libxapian-dev
``` ```
./manage.py migrate
./manage.py runserver Allow the virtual environment to use system-installed packages:
```bash
export PYTHONPATH="${PYTHONPATH}:/usr/lib/python3/dist-packages"
``` ```
#### Environment Variables
Now, configure the environment variables. For this, we will expand a `.env` file. You can use the following content as an example:
```source
STATIC_ROOT=/tmp/static/
MEDIA_ROOT=/tmp/media/
ALLOWED_HOSTS=localhost,localhost:8000,127.0.0.1,
DOMAIN=localhost
DEBUG=True
```
Now, expand the environment variables:
```bash
source .env
```
### Migrations
Now, apply migrations
```bash
python manage.py makemigrations
python manage.py migrate
```
Also, we can add some dummy data into the database to play along:
```bash
python manage.py add_institution Pangea
python manage.py add_user Pangea user@example.org 1234
python manage.py up_snapshots example/snapshots/ user@example.org
```
### Run DeviceHub
Finally, we can run the DeviceHub service by running:
```bash
python manage.py runserver
```
### Clean up
To clean up the deployment and start fresh, just delete Django's database:
```bash
rm db/*
```
## License
DeviceHub is released under the [GNU Affero General Public License v3.0](LICENSE).

View file

@ -58,7 +58,7 @@
<div class="col-lg-9 col-md-8">{{ object.type }}</div> <div class="col-lg-9 col-md-8">{{ object.type }}</div>
</div> </div>
{% if object.is_websnapshot %} {% if object.is_websnapshot and object.last_user_evidence %}
{% for k, v in object.last_user_evidence %} {% for k, v in object.last_user_evidence %}
<div class="row mb-3"> <div class="row mb-3">
<div class="col-lg-3 col-md-4 label">{{ k }}</div> <div class="col-lg-3 col-md-4 label">{{ k }}</div>

View file

@ -15,7 +15,7 @@ from utils.save_snapshots import move_json, save_in_disk
class UploadForm(forms.Form): class UploadForm(forms.Form):
evidence_file = MultipleFileField(label=_("File")) evidence_file = MultipleFileField(label=_("File"))
def clean(self): def clean_evidence_file(self):
self.evidences = [] self.evidences = []
data = self.cleaned_data.get('evidence_file') data = self.cleaned_data.get('evidence_file')
if not data: if not data:
@ -35,11 +35,18 @@ class UploadForm(forms.Form):
).first() ).first()
if exist_annotation: if exist_annotation:
raise ValidationError("error: {} exist".format(file_name)) raise ValidationError(
_("The snapshot already exists"),
except Exception: code="duplicate_snapshot",
raise ValidationError("error in: {}".format(file_name)) )
#Catch any error and display it as Validation Error so the Form handles it
except Exception as e:
raise ValidationError(
_("Error on '%(file_name)s': %(error)s"),
code="error",
params={"file_name": file_name, "error": getattr(e, 'message', str(e))},
)
self.evidences.append((file_name, file_json)) self.evidences.append((file_name, file_json))
return True return True
@ -123,7 +130,15 @@ class ImportForm(forms.Form):
data = self.cleaned_data["file_import"] data = self.cleaned_data["file_import"]
self.file_name = data.name self.file_name = data.name
try:
df = pd.read_excel(data) df = pd.read_excel(data)
except Exception as e:
raise ValidationError(
_("Error on '%(file_name)s': Invalid File"),
params={"file_name": self.file_name}
)
df.fillna('', inplace=True) df.fillna('', inplace=True)
data_pd = df.to_dict(orient='index') data_pd = df.to_dict(orient='index')

View file

@ -47,17 +47,22 @@ class Command(BaseCommand):
self.open(filepath) self.open(filepath)
def open(self, filepath): def open(self, filepath):
try:
with open(filepath, 'r') as file: with open(filepath, 'r') as file:
content = json.loads(file.read()) content = json.loads(file.read())
path_name = save_in_disk(content, self.user.institution.name) path_name = save_in_disk(content, self.user.institution.name)
self.snapshots.append((content, path_name)) self.snapshots.append((content, path_name))
except Exception as e:
logger.error("Could not open file %s: %s", filepath, e)
def parsing(self): def parsing(self):
for s, p 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) move_json(p, self.user.institution.name)
except Exception as err: except Exception as e:
snapshot_id = s.get("uuid", "") snapshot_id = s.get("uuid", "")
txt = "Could not parse snapshot: %s" txt = "Could not parse snapshot %s: %s"
logger.error(txt, snapshot_id) logger.error(txt, snapshot_id, e)

View file

@ -4,6 +4,7 @@ import logging
from dmidecode import DMIParse from dmidecode import DMIParse
from json_repair import repair_json from json_repair import repair_json
from evidence.parse_details import get_lshw_child
from evidence.models import Annotation from evidence.models import Annotation
from evidence.xapian import index from evidence.xapian import index
@ -12,16 +13,7 @@ from utils.constants import CHASSIS_DH
logger = logging.getLogger('django') logger = logging.getLogger('django')
def get_network_cards(child, nets):
if child['id'] == 'network' and "PCI:" in child.get("businfo"):
nets.append(child)
if child.get('children'):
[get_network_cards(x, nets) for x in child['children']]
def get_mac(lshw): def get_mac(lshw):
nets = []
try: try:
if type(lshw) is dict: if type(lshw) is dict:
hw = lshw hw = lshw
@ -30,18 +22,16 @@ def get_mac(lshw):
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
hw = json.loads(repair_json(lshw)) hw = json.loads(repair_json(lshw))
try: nets = []
get_network_cards(hw, nets) get_lshw_child(hw, nets, 'network')
except Exception as ss:
logger.warning("%s", ss)
return
nets_sorted = sorted(nets, key=lambda x: x['businfo']) nets_sorted = sorted(nets, key=lambda x: x['businfo'])
# This funcion get the network card integrated in motherboard
# integrate = [x for x in nets if "pci@0000:00:" in x.get('businfo', '')]
if nets_sorted: if nets_sorted:
return nets_sorted[0]['serial'] mac = nets_sorted[0]['serial']
logger.debug("The snapshot has the following MAC: %s" , mac)
return mac
class Build: class Build:

View file

@ -8,22 +8,20 @@
</div> </div>
</div> </div>
<!-- override invalid-feedback class -->
<style>
.invalid-feedback {
color: #670000;
font-size: 1rem;
}
</style>
{% load django_bootstrap5 %} {% load django_bootstrap5 %}
<form role="form" method="post" enctype="multipart/form-data"> <form role="form" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert"> {% bootstrap_form form alert_error_type="none" error_css_class="alert alert-danger alert-icon alert-icon-border" %}
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div> <div class="form-actions-no-box">
<div class="message">
{% for field, error in form.errors.items %}
{{ error }}<br />
{% endfor %}
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
</div>
</div>
{% endif %}
{% bootstrap_form form %}
<div class="form-actions-no-box">
<a class="btn btn-grey" href="{% url 'dashboard:unassigned_devices' %}">{% translate "Cancel" %}</a> <a class="btn btn-grey" href="{% url 'dashboard:unassigned_devices' %}">{% translate "Cancel" %}</a>
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" /> <input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
</div> </div>

View file

@ -1,5 +1,6 @@
import json import json
from django.contrib import messages
from urllib.parse import urlparse from urllib.parse import urlparse
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -47,6 +48,7 @@ class UploadView(DashboardView, FormView):
def form_valid(self, form): def form_valid(self, form):
form.save(self.request.user) form.save(self.request.user)
messages.success(self.request, _("Evidence uploaded successfully."))
response = super().form_valid(form) response = super().form_valid(form)
return response return response
@ -70,6 +72,7 @@ class ImportView(DashboardView, FormView):
def form_valid(self, form): def form_valid(self, form):
form.save() form.save()
messages.success(self.request, _("Evidence imported successfully."))
response = super().form_valid(form) response = super().form_valid(form)
return response return response

View file

@ -13,9 +13,9 @@ class CustomFormatter(logging.Formatter):
if record.levelname == "ERROR": if record.levelname == "ERROR":
color = RED color = RED
elif record.levelname == "WARNING": elif record.levelname == "WARNING":
color = PURPLE
elif record.levelname in ["INFO", "DEBUG"]:
color = YELLOW color = YELLOW
elif record.levelname in ["INFO", "DEBUG"]:
color = PURPLE
else: else:
color = RESET color = RESET