Merge branch 'testing'
This commit is contained in:
commit
6350bcd6a4
|
@ -16,10 +16,10 @@ on:
|
||||||
branches: [master, testing]
|
branches: [master, testing]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '16'
|
||||||
|
|
|
@ -32,12 +32,12 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
max-parallel: 4
|
max-parallel: 4
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.7]
|
python-version: [3.9]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
name: Selenium
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master, testing]
|
||||||
|
pull_request:
|
||||||
|
branches: [master, testing]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Service containers to run with `container-job`
|
||||||
|
services:
|
||||||
|
# Label used to access the service container
|
||||||
|
postgres:
|
||||||
|
# Docker Hub image
|
||||||
|
image: postgres:11
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
# Set health checks to wait until postgres has started
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: dh_test
|
||||||
|
POSTGRES_USER: dhub
|
||||||
|
POSTGRES_PASSWORD: ereuse
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
max-parallel: 4
|
||||||
|
matrix:
|
||||||
|
python-version: [3.9]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
cache: 'pip'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qy
|
||||||
|
sudo apt-get -y install postgresql-client --no-install-recommends
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install flake8 pytest coverage
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install -e .
|
||||||
|
mkdir bin
|
||||||
|
wget https://github.com/mozilla/geckodriver/releases/download/v0.30.0/geckodriver-v0.30.0-linux64.tar.gz
|
||||||
|
tar xf geckodriver-v0.30.0-linux64.tar.gz -C bin/
|
||||||
|
|
||||||
|
- name: Prepare database
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: dh_test
|
||||||
|
POSTGRES_USER: dhub
|
||||||
|
POSTGRES_PASSWORD: ereuse
|
||||||
|
run: |
|
||||||
|
export PGPASSWORD=$POSTGRES_PASSWORD
|
||||||
|
psql -h "localhost" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION pgcrypto SCHEMA public;"
|
||||||
|
psql -h "localhost" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION ltree SCHEMA public;"
|
||||||
|
psql -h "localhost" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION citext SCHEMA public;"
|
||||||
|
psql -h "localhost" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "CREATE EXTENSION pg_trgm SCHEMA public;"
|
||||||
|
|
||||||
|
- name: Selenium tests
|
||||||
|
env:
|
||||||
|
SECRET_KEY: 'f00046306835001b55c230092e3a7990485beda0bc3bf732088d1ba1b5b74110e22e3f9ec3a24890272554b37d4'
|
||||||
|
DB_DATABASE: dh_test
|
||||||
|
FLASK_APP: examples/app.py
|
||||||
|
dhi: dbtest
|
||||||
|
run: |
|
||||||
|
alembic -x inventory=dbtest upgrade head
|
||||||
|
dh dummy --yes
|
||||||
|
flask run & pytest tests/test_selenium.py
|
23
CHANGELOG.md
23
CHANGELOG.md
|
@ -5,15 +5,32 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.ht
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.ht
|
||||||
ml).
|
ml).
|
||||||
|
|
||||||
## master
|
|
||||||
|
|
||||||
## testing
|
## testing
|
||||||
|
|
||||||
|
## [2.3.0] - 2022-07-12
|
||||||
|
- [added] #281 Add selenium test.
|
||||||
|
- [added] #305 Add button to download ISO Workbench.
|
||||||
|
- [added] #306 Add link to download JSON snapshot.
|
||||||
|
- [added] #308 Add sentry.
|
||||||
|
- [changed] #302 Add system uuid to check the identity of one device.
|
||||||
|
- [fixed] #309 Column lifecycle status is always empty.
|
||||||
|
|
||||||
|
**IMPORTANT**: PR #302 involves some changes in the deployment process:
|
||||||
|
```bash
|
||||||
|
# First, run script `extract_uuids.sh` before applying alembic migrations (e.g. with schema `dbtest`)
|
||||||
|
sh scripts/extract_uuids.sh
|
||||||
|
|
||||||
|
# Then, apply alembic migrations
|
||||||
|
alembic -x inventory=dbtest upgrade head
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE**: If you forget (or don't need) to run this script before applying new migration it will work but any device will be updated.
|
||||||
|
|
||||||
## [2.2.0] - 2022-06-24
|
## [2.2.0] - 2022-06-24
|
||||||
- [changed] #304 change anchor of link devices lots.
|
- [changed] #304 change anchor of link devices lots.
|
||||||
|
|
||||||
## [2.2.0 rc2] - 2022-06-22
|
## [2.2.0 rc2] - 2022-06-22
|
||||||
- [added] #299 Multy select with Shift.
|
- [added] #299 Multiselect with Shift.
|
||||||
- [added] #300 Add Sid in label.
|
- [added] #300 Add Sid in label.
|
||||||
- [added] #301 Add logo in label.
|
- [added] #301 Add logo in label.
|
||||||
- [added] #303 Add export Lots.
|
- [added] #303 Add export Lots.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "2.2.0"
|
__version__ = "2.3.0"
|
||||||
|
|
|
@ -29,7 +29,6 @@ from wtforms.fields import FormField
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.inventory.models import DeliveryNote, ReceiverNote, Transfer
|
from ereuse_devicehub.inventory.models import DeliveryNote, ReceiverNote, Transfer
|
||||||
from ereuse_devicehub.parser.models import SnapshotsLog
|
|
||||||
from ereuse_devicehub.parser.parser import ParseSnapshotLsHw
|
from ereuse_devicehub.parser.parser import ParseSnapshotLsHw
|
||||||
from ereuse_devicehub.parser.schemas import Snapshot_lite
|
from ereuse_devicehub.parser.schemas import Snapshot_lite
|
||||||
from ereuse_devicehub.resources.action.models import Snapshot, Trade
|
from ereuse_devicehub.resources.action.models import Snapshot, Trade
|
||||||
|
@ -260,45 +259,35 @@ class UploadSnapshotForm(SnapshotMixin, FlaskForm):
|
||||||
self.tmp_snapshots = app.config['TMP_SNAPSHOTS']
|
self.tmp_snapshots = app.config['TMP_SNAPSHOTS']
|
||||||
for filename, snapshot_json in self.snapshots:
|
for filename, snapshot_json in self.snapshots:
|
||||||
path_snapshot = save_json(snapshot_json, self.tmp_snapshots, g.user.email)
|
path_snapshot = save_json(snapshot_json, self.tmp_snapshots, g.user.email)
|
||||||
snapshot_json.pop('debug', None)
|
debug = snapshot_json.pop('debug', None)
|
||||||
version = snapshot_json.get('schema_api')
|
self.version = snapshot_json.get('schema_api')
|
||||||
uuid = snapshot_json.get('uuid')
|
self.uuid = snapshot_json.get('uuid')
|
||||||
sid = snapshot_json.get('sid')
|
self.sid = snapshot_json.get('sid')
|
||||||
software_version = snapshot_json.get('version')
|
|
||||||
if self.is_wb_lite_snapshot(version):
|
if self.is_wb_lite_snapshot(self.version):
|
||||||
self.snapshot_json = schema_lite.load(snapshot_json)
|
self.snapshot_json = schema_lite.load(snapshot_json)
|
||||||
snapshot_json = ParseSnapshotLsHw(self.snapshot_json).snapshot_json
|
snapshot_json = ParseSnapshotLsHw(self.snapshot_json).snapshot_json
|
||||||
|
else:
|
||||||
|
self.version = snapshot_json.get('version')
|
||||||
|
system_uuid = self.get_uuid(debug)
|
||||||
|
if system_uuid:
|
||||||
|
snapshot_json['device']['system_uuid'] = system_uuid
|
||||||
|
|
||||||
try:
|
try:
|
||||||
snapshot_json = schema.load(snapshot_json)
|
snapshot_json = schema.load(snapshot_json)
|
||||||
except ValidationError as err:
|
except ValidationError as err:
|
||||||
txt = "{}".format(err)
|
txt = "{}".format(err)
|
||||||
error = SnapshotsLog(
|
self.errors(txt=txt)
|
||||||
description=txt,
|
|
||||||
snapshot_uuid=uuid,
|
|
||||||
severity=Severity.Error,
|
|
||||||
sid=sid,
|
|
||||||
version=software_version,
|
|
||||||
)
|
|
||||||
error.save(commit=True)
|
|
||||||
self.result[filename] = 'Error'
|
self.result[filename] = 'Error'
|
||||||
continue
|
continue
|
||||||
|
|
||||||
response = self.build(snapshot_json)
|
response = self.build(snapshot_json)
|
||||||
db.session.add(response)
|
db.session.add(response)
|
||||||
devices.append(response.device)
|
devices.append(response.device)
|
||||||
snap_log = SnapshotsLog(
|
|
||||||
description='Ok',
|
|
||||||
snapshot_uuid=uuid,
|
|
||||||
severity=Severity.Info,
|
|
||||||
sid=sid,
|
|
||||||
version=software_version,
|
|
||||||
snapshot=response,
|
|
||||||
)
|
|
||||||
snap_log.save()
|
|
||||||
|
|
||||||
if hasattr(response, 'type'):
|
if hasattr(response, 'type'):
|
||||||
self.result[filename] = 'Ok'
|
self.result[filename] = 'Ok'
|
||||||
|
self.errors(txt="Ok", severity=Severity.Info, snapshot=response)
|
||||||
else:
|
else:
|
||||||
self.result[filename] = 'Error'
|
self.result[filename] = 'Error'
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import csv
|
import csv
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
from distutils.util import strtobool
|
from distutils.util import strtobool
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
import flask_weasyprint
|
import flask_weasyprint
|
||||||
from flask import Blueprint, g, make_response, request, url_for
|
from flask import Blueprint
|
||||||
|
from flask import current_app as app
|
||||||
|
from flask import g, make_response, request, url_for
|
||||||
from flask.views import View
|
from flask.views import View
|
||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
from werkzeug.exceptions import NotFound
|
from werkzeug.exceptions import NotFound
|
||||||
|
@ -479,6 +483,7 @@ class ExportsView(View):
|
||||||
'certificates': self.erasure,
|
'certificates': self.erasure,
|
||||||
'lots': self.lots_export,
|
'lots': self.lots_export,
|
||||||
'devices_lots': self.devices_lots_export,
|
'devices_lots': self.devices_lots_export,
|
||||||
|
'snapshot': self.snapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
if export_id not in export_ids:
|
if export_id not in export_ids:
|
||||||
|
@ -685,6 +690,33 @@ class ExportsView(View):
|
||||||
data, "Devices_Incoming_and_Outgoing_Lots_Spreadsheet.csv"
|
data, "Devices_Incoming_and_Outgoing_Lots_Spreadsheet.csv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def snapshot(self):
|
||||||
|
uuid = request.args.get('id')
|
||||||
|
if not uuid:
|
||||||
|
messages.error('Snapshot not exist!')
|
||||||
|
return flask.redirect(request.referrer)
|
||||||
|
|
||||||
|
user = g.user.email
|
||||||
|
name_file = f"*_{user}_{uuid}.json"
|
||||||
|
tmp_snapshots = app.config['TMP_SNAPSHOTS']
|
||||||
|
path_dir_base = os.path.join(tmp_snapshots, user)
|
||||||
|
|
||||||
|
for _file in Path(path_dir_base).glob(name_file):
|
||||||
|
with open(_file) as file_snapshot:
|
||||||
|
snapshot = file_snapshot.read()
|
||||||
|
data = StringIO()
|
||||||
|
data.write(snapshot)
|
||||||
|
bfile = data.getvalue().encode('utf-8')
|
||||||
|
output = make_response(bfile)
|
||||||
|
output.headers['Content-Disposition'] = 'attachment; filename={}'.format(
|
||||||
|
name_file
|
||||||
|
)
|
||||||
|
output.headers['Content-type'] = 'text/json'
|
||||||
|
return output
|
||||||
|
|
||||||
|
messages.error('Snapshot not exist!')
|
||||||
|
return flask.redirect(request.referrer)
|
||||||
|
|
||||||
|
|
||||||
class SnapshotListView(GenericMixin):
|
class SnapshotListView(GenericMixin):
|
||||||
template_name = 'inventory/snapshots_list.html'
|
template_name = 'inventory/snapshots_list.html'
|
||||||
|
@ -702,12 +734,18 @@ class SnapshotListView(GenericMixin):
|
||||||
).order_by(SnapshotsLog.created.desc())
|
).order_by(SnapshotsLog.created.desc())
|
||||||
logs = {}
|
logs = {}
|
||||||
for snap in snapshots_log:
|
for snap in snapshots_log:
|
||||||
|
try:
|
||||||
|
system_uuid = snap.snapshot.device.system_uuid or ''
|
||||||
|
except AttributeError:
|
||||||
|
system_uuid = ''
|
||||||
|
|
||||||
if snap.snapshot_uuid not in logs:
|
if snap.snapshot_uuid not in logs:
|
||||||
logs[snap.snapshot_uuid] = {
|
logs[snap.snapshot_uuid] = {
|
||||||
'sid': snap.sid,
|
'sid': snap.sid,
|
||||||
'snapshot_uuid': snap.snapshot_uuid,
|
'snapshot_uuid': snap.snapshot_uuid,
|
||||||
'version': snap.version,
|
'version': snap.version,
|
||||||
'device': snap.get_device(),
|
'device': snap.get_device(),
|
||||||
|
'system_uuid': system_uuid,
|
||||||
'status': snap.get_status(),
|
'status': snap.get_status(),
|
||||||
'severity': snap.severity,
|
'severity': snap.severity,
|
||||||
'created': snap.created,
|
'created': snap.created,
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
"""system_uuid instead of uuid
|
||||||
|
|
||||||
|
Revision ID: 73348969a583
|
||||||
|
Revises: dac62da1621a
|
||||||
|
Create Date: 2022-06-15 12:27:23.170313
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import context, op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '73348969a583'
|
||||||
|
down_revision = 'dac62da1621a'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_inv():
|
||||||
|
INV = context.get_x_argument(as_dictionary=True).get('inventory')
|
||||||
|
if not INV:
|
||||||
|
raise ValueError("Inventory value is not specified")
|
||||||
|
return INV
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.alter_column(
|
||||||
|
'computer', 'uuid', new_column_name="system_uuid", schema=f'{get_inv()}'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.alter_column(
|
||||||
|
'computer', 'system_uuid', new_column_name="uuid", schema=f'{get_inv()}'
|
||||||
|
)
|
|
@ -0,0 +1,67 @@
|
||||||
|
"""add system uuid to old registers
|
||||||
|
|
||||||
|
Revision ID: 8d4fe4b497b3
|
||||||
|
Revises: 73348969a583
|
||||||
|
Create Date: 2022-06-15 15:52:39.205192
|
||||||
|
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from alembic import context, op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '8d4fe4b497b3'
|
||||||
|
down_revision = '73348969a583'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_inv():
|
||||||
|
INV = context.get_x_argument(as_dictionary=True).get('inventory')
|
||||||
|
if not INV:
|
||||||
|
raise ValueError("Inventory value is not specified")
|
||||||
|
return INV
|
||||||
|
|
||||||
|
|
||||||
|
def update_db(con, system_uuid, snapshot_uuid):
|
||||||
|
sql_snapshot = f'select id from {get_inv()}.snapshot where uuid=\'{snapshot_uuid}\''
|
||||||
|
sql_device_id = f'select device_id from {get_inv()}.action_with_one_device where id in ({sql_snapshot})'
|
||||||
|
sql = f'select id, system_uuid from {get_inv()}.computer where id in ({sql_device_id})'
|
||||||
|
|
||||||
|
for device_id, db_system_uuid in con.execute(sql):
|
||||||
|
if db_system_uuid:
|
||||||
|
return
|
||||||
|
|
||||||
|
sql = f'update {get_inv()}.computer set system_uuid=\'{system_uuid}\' where id=\'{device_id}\''
|
||||||
|
con.execute(sql)
|
||||||
|
|
||||||
|
|
||||||
|
def update_to_little_endian(uuid):
|
||||||
|
uuid = UUID(uuid)
|
||||||
|
return UUID(bytes_le=uuid.bytes)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
uuids = []
|
||||||
|
system_uuids_file = 'system_uuids.csv'
|
||||||
|
if os.path.exists(system_uuids_file):
|
||||||
|
with open(system_uuids_file) as f:
|
||||||
|
for x in f.read().split('\n'):
|
||||||
|
z = x.split(';')
|
||||||
|
if len(z) != 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
x, y = z
|
||||||
|
uuids.append([x.strip(), y.strip()])
|
||||||
|
|
||||||
|
con = op.get_bind()
|
||||||
|
for u in uuids[1:]:
|
||||||
|
if u[0] == '':
|
||||||
|
continue
|
||||||
|
u[0] = update_to_little_endian(u[0])
|
||||||
|
update_db(con, u[0], u[1])
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
pass
|
|
@ -94,7 +94,7 @@ class Processor(Component):
|
||||||
assert not hasattr(self, 'cores') or 1 <= self.cores <= 16
|
assert not hasattr(self, 'cores') or 1 <= self.cores <= 16
|
||||||
|
|
||||||
@staticmethod # noqa: C901
|
@staticmethod # noqa: C901
|
||||||
def processor_brand_generation(model: str):
|
def processor_brand_generation(model: str): # noqa: C901
|
||||||
"""Generates the ``brand`` and ``generation`` fields for the given model.
|
"""Generates the ``brand`` and ``generation`` fields for the given model.
|
||||||
|
|
||||||
This returns a tuple with:
|
This returns a tuple with:
|
||||||
|
|
|
@ -52,7 +52,7 @@ class ParseSnapshot:
|
||||||
self.device['type'] = self.get_type()
|
self.device['type'] = self.get_type()
|
||||||
self.device['sku'] = self.get_sku()
|
self.device['sku'] = self.get_sku()
|
||||||
self.device['version'] = self.get_version()
|
self.device['version'] = self.get_version()
|
||||||
self.device['uuid'] = self.get_uuid()
|
self.device['system_uuid'] = self.get_uuid()
|
||||||
|
|
||||||
def set_components(self):
|
def set_components(self):
|
||||||
self.get_cpu()
|
self.get_cpu()
|
||||||
|
@ -379,7 +379,7 @@ class ParseSnapshotLsHw:
|
||||||
raise ValidationError(txt)
|
raise ValidationError(txt)
|
||||||
|
|
||||||
self.device = pc
|
self.device = pc
|
||||||
self.device['uuid'] = self.get_uuid()
|
self.device['system_uuid'] = self.get_uuid()
|
||||||
|
|
||||||
def set_components(self):
|
def set_components(self):
|
||||||
memory = None
|
memory = None
|
||||||
|
|
|
@ -4,10 +4,10 @@ import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
from flask import g
|
from flask import g
|
||||||
from marshmallow import ValidationError
|
|
||||||
from sqlalchemy.util import OrderedSet
|
from sqlalchemy.util import OrderedSet
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
|
@ -117,6 +117,49 @@ class SnapshotMixin:
|
||||||
|
|
||||||
return snapshot
|
return snapshot
|
||||||
|
|
||||||
|
def get_old_smbios_version(self, debug):
|
||||||
|
capabilities = debug.get('lshw', {}).get('capabilities', {})
|
||||||
|
for x in capabilities.values():
|
||||||
|
if "SMBIOS version" in x:
|
||||||
|
e = x.split("SMBIOS version ")[1].split(".")
|
||||||
|
if int(e[0]) < 3 and int(e[1]) < 6:
|
||||||
|
self.errors(txt=x)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_uuid(self, debug):
|
||||||
|
if not debug or not isinstance(debug, dict):
|
||||||
|
self.errors(txt="There is not uuid")
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.get_old_smbios_version(debug):
|
||||||
|
return
|
||||||
|
|
||||||
|
hw_uuid = debug.get('lshw', {}).get('configuration', {}).get('uuid')
|
||||||
|
|
||||||
|
if not hw_uuid:
|
||||||
|
self.errors(txt="There is not uuid")
|
||||||
|
return
|
||||||
|
|
||||||
|
uuid = UUID(hw_uuid)
|
||||||
|
return UUID(bytes_le=uuid.bytes)
|
||||||
|
|
||||||
|
def errors(self, txt=None, severity=Severity.Error, snapshot=None, commit=False):
|
||||||
|
if not txt:
|
||||||
|
return
|
||||||
|
|
||||||
|
from ereuse_devicehub.parser.models import SnapshotsLog
|
||||||
|
|
||||||
|
error = SnapshotsLog(
|
||||||
|
description=txt,
|
||||||
|
snapshot_uuid=self.uuid,
|
||||||
|
severity=severity,
|
||||||
|
sid=self.sid,
|
||||||
|
version=self.version,
|
||||||
|
snapshot=snapshot,
|
||||||
|
)
|
||||||
|
error.save(commit=commit)
|
||||||
|
|
||||||
|
|
||||||
class SnapshotView(SnapshotMixin):
|
class SnapshotView(SnapshotMixin):
|
||||||
"""Performs a Snapshot.
|
"""Performs a Snapshot.
|
||||||
|
@ -129,38 +172,29 @@ class SnapshotView(SnapshotMixin):
|
||||||
# snapshot, and we want to wait to flush snapshot at the end
|
# snapshot, and we want to wait to flush snapshot at the end
|
||||||
|
|
||||||
def __init__(self, snapshot_json: dict, resource_def, schema):
|
def __init__(self, snapshot_json: dict, resource_def, schema):
|
||||||
from ereuse_devicehub.parser.models import SnapshotsLog
|
|
||||||
|
|
||||||
self.schema = schema
|
self.schema = schema
|
||||||
self.resource_def = resource_def
|
self.resource_def = resource_def
|
||||||
self.tmp_snapshots = app.config['TMP_SNAPSHOTS']
|
self.tmp_snapshots = app.config['TMP_SNAPSHOTS']
|
||||||
self.path_snapshot = save_json(snapshot_json, self.tmp_snapshots, g.user.email)
|
self.path_snapshot = save_json(snapshot_json, self.tmp_snapshots, g.user.email)
|
||||||
snapshot_json.pop('debug', None)
|
self.version = snapshot_json.get('version')
|
||||||
|
self.uuid = snapshot_json.get('uuid')
|
||||||
|
self.sid = None
|
||||||
|
system_uuid = self.get_uuid(snapshot_json.pop('debug', None))
|
||||||
|
if system_uuid:
|
||||||
|
snapshot_json['device']['system_uuid'] = system_uuid
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.snapshot_json = resource_def.schema.load(snapshot_json)
|
self.snapshot_json = resource_def.schema.load(snapshot_json)
|
||||||
snapshot = self.build()
|
snapshot = self.build()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
txt = "{}".format(err)
|
txt = "{}".format(err)
|
||||||
uuid = snapshot_json.get('uuid')
|
self.errors(txt=txt, commit=True)
|
||||||
version = snapshot_json.get('version')
|
|
||||||
error = SnapshotsLog(
|
|
||||||
description=txt,
|
|
||||||
snapshot_uuid=uuid,
|
|
||||||
severity=Severity.Error,
|
|
||||||
version=str(version),
|
|
||||||
)
|
|
||||||
error.save(commit=True)
|
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
db.session.add(snapshot)
|
db.session.add(snapshot)
|
||||||
snap_log = SnapshotsLog(
|
self.errors(txt="Ok", severity=Severity.Info, snapshot=snapshot, commit=False)
|
||||||
description='Ok',
|
|
||||||
snapshot_uuid=snapshot.uuid,
|
|
||||||
severity=Severity.Info,
|
|
||||||
version=str(snapshot.version),
|
|
||||||
snapshot=snapshot,
|
|
||||||
)
|
|
||||||
snap_log.save()
|
|
||||||
db.session().final_flush()
|
db.session().final_flush()
|
||||||
self.response = self.schema.jsonify(snapshot) # transform it back
|
self.response = self.schema.jsonify(snapshot) # transform it back
|
||||||
self.response.status_code = 201
|
self.response.status_code = 201
|
||||||
|
|
|
@ -191,6 +191,7 @@ class Device(Thing):
|
||||||
'image',
|
'image',
|
||||||
'allocated',
|
'allocated',
|
||||||
'devicehub_id',
|
'devicehub_id',
|
||||||
|
'system_uuid',
|
||||||
'active',
|
'active',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,7 +819,7 @@ class Computer(Device):
|
||||||
transfer_state.comment = TransferState.__doc__
|
transfer_state.comment = TransferState.__doc__
|
||||||
receiver_id = db.Column(UUID(as_uuid=True), db.ForeignKey(User.id), nullable=True)
|
receiver_id = db.Column(UUID(as_uuid=True), db.ForeignKey(User.id), nullable=True)
|
||||||
receiver = db.relationship(User, primaryjoin=receiver_id == User.id)
|
receiver = db.relationship(User, primaryjoin=receiver_id == User.id)
|
||||||
uuid = db.Column(UUID(as_uuid=True), nullable=True)
|
system_uuid = db.Column(UUID(as_uuid=True), nullable=True)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
if args:
|
if args:
|
||||||
|
|
|
@ -1,17 +1,30 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from marshmallow import post_load, pre_load, fields as f
|
from marshmallow import fields as f
|
||||||
from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String, UUID, Dict
|
from marshmallow import post_load, pre_load
|
||||||
|
from marshmallow.fields import (
|
||||||
|
UUID,
|
||||||
|
Boolean,
|
||||||
|
Date,
|
||||||
|
DateTime,
|
||||||
|
Dict,
|
||||||
|
Float,
|
||||||
|
Integer,
|
||||||
|
List,
|
||||||
|
Str,
|
||||||
|
String,
|
||||||
|
)
|
||||||
from marshmallow.validate import Length, OneOf, Range
|
from marshmallow.validate import Length, OneOf, Range
|
||||||
from sqlalchemy.util import OrderedSet
|
from sqlalchemy.util import OrderedSet
|
||||||
from stdnum import imei, meid
|
from stdnum import imei, meid
|
||||||
from teal.enums import Layouts
|
from teal.enums import Layouts
|
||||||
from teal.marshmallow import EnumField, SanitizedStr, URL, ValidationError
|
from teal.marshmallow import URL, EnumField, SanitizedStr, ValidationError
|
||||||
from teal.resource import Schema
|
from teal.resource import Schema
|
||||||
|
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
from ereuse_devicehub.resources import enums
|
from ereuse_devicehub.resources import enums
|
||||||
from ereuse_devicehub.resources.device import models as m, states
|
from ereuse_devicehub.resources.device import models as m
|
||||||
|
from ereuse_devicehub.resources.device import states
|
||||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
||||||
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
||||||
|
|
||||||
|
@ -20,58 +33,90 @@ class Device(Thing):
|
||||||
__doc__ = m.Device.__doc__
|
__doc__ = m.Device.__doc__
|
||||||
id = Integer(description=m.Device.id.comment, dump_only=True)
|
id = Integer(description=m.Device.id.comment, dump_only=True)
|
||||||
hid = SanitizedStr(lower=True, description=m.Device.hid.comment)
|
hid = SanitizedStr(lower=True, description=m.Device.hid.comment)
|
||||||
tags = NestedOn('Tag',
|
tags = NestedOn(
|
||||||
many=True,
|
'Tag',
|
||||||
collection_class=OrderedSet,
|
many=True,
|
||||||
description='A set of tags that identify the device.')
|
collection_class=OrderedSet,
|
||||||
model = SanitizedStr(lower=True,
|
description='A set of tags that identify the device.',
|
||||||
validate=Length(max=STR_BIG_SIZE),
|
)
|
||||||
description=m.Device.model.comment)
|
model = SanitizedStr(
|
||||||
manufacturer = SanitizedStr(lower=True,
|
lower=True,
|
||||||
validate=Length(max=STR_SIZE),
|
validate=Length(max=STR_BIG_SIZE),
|
||||||
description=m.Device.manufacturer.comment)
|
description=m.Device.model.comment,
|
||||||
serial_number = SanitizedStr(lower=True,
|
)
|
||||||
validate=Length(max=STR_BIG_SIZE),
|
manufacturer = SanitizedStr(
|
||||||
data_key='serialNumber')
|
lower=True,
|
||||||
brand = SanitizedStr(validate=Length(max=STR_BIG_SIZE), description=m.Device.brand.comment)
|
validate=Length(max=STR_SIZE),
|
||||||
generation = Integer(validate=Range(1, 100), description=m.Device.generation.comment)
|
description=m.Device.manufacturer.comment,
|
||||||
|
)
|
||||||
|
serial_number = SanitizedStr(
|
||||||
|
lower=True, validate=Length(max=STR_BIG_SIZE), data_key='serialNumber'
|
||||||
|
)
|
||||||
|
brand = SanitizedStr(
|
||||||
|
validate=Length(max=STR_BIG_SIZE), description=m.Device.brand.comment
|
||||||
|
)
|
||||||
|
generation = Integer(
|
||||||
|
validate=Range(1, 100), description=m.Device.generation.comment
|
||||||
|
)
|
||||||
version = SanitizedStr(description=m.Device.version)
|
version = SanitizedStr(description=m.Device.version)
|
||||||
weight = Float(validate=Range(0.1, 5), unit=UnitCodes.kgm, description=m.Device.weight.comment)
|
weight = Float(
|
||||||
width = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment)
|
validate=Range(0.1, 5), unit=UnitCodes.kgm, description=m.Device.weight.comment
|
||||||
height = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment)
|
)
|
||||||
depth = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment)
|
width = Float(
|
||||||
|
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment
|
||||||
|
)
|
||||||
|
height = Float(
|
||||||
|
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment
|
||||||
|
)
|
||||||
|
depth = Float(
|
||||||
|
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment
|
||||||
|
)
|
||||||
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
||||||
actions = NestedOn('Action', many=True, dump_only=True, description=m.Device.actions.__doc__)
|
actions = NestedOn(
|
||||||
|
'Action', many=True, dump_only=True, description=m.Device.actions.__doc__
|
||||||
|
)
|
||||||
# TODO TimeOut 2. Comment actions_one and lots if there are time out.
|
# TODO TimeOut 2. Comment actions_one and lots if there are time out.
|
||||||
actions_one = NestedOn('Action', many=True, load_only=True, collection_class=OrderedSet)
|
actions_one = NestedOn(
|
||||||
problems = NestedOn('Action', many=True, dump_only=True, description=m.Device.problems.__doc__)
|
'Action', many=True, load_only=True, collection_class=OrderedSet
|
||||||
|
)
|
||||||
|
problems = NestedOn(
|
||||||
|
'Action', many=True, dump_only=True, description=m.Device.problems.__doc__
|
||||||
|
)
|
||||||
url = URL(dump_only=True, description=m.Device.url.__doc__)
|
url = URL(dump_only=True, description=m.Device.url.__doc__)
|
||||||
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
||||||
lots = NestedOn('Lot',
|
lots = NestedOn(
|
||||||
many=True,
|
'Lot',
|
||||||
dump_only=True,
|
many=True,
|
||||||
description='The lots where this device is directly under.')
|
dump_only=True,
|
||||||
|
description='The lots where this device is directly under.',
|
||||||
|
)
|
||||||
rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__)
|
rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__)
|
||||||
price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__)
|
price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__)
|
||||||
tradings = Dict(dump_only=True, description='')
|
tradings = Dict(dump_only=True, description='')
|
||||||
physical = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__)
|
physical = EnumField(
|
||||||
traking = EnumField(states.Traking, dump_only=True, description=m.Device.physical.__doc__)
|
states.Physical, dump_only=True, description=m.Device.physical.__doc__
|
||||||
usage = EnumField(states.Usage, dump_only=True, description=m.Device.physical.__doc__)
|
)
|
||||||
|
traking = EnumField(
|
||||||
|
states.Traking, dump_only=True, description=m.Device.physical.__doc__
|
||||||
|
)
|
||||||
|
usage = EnumField(
|
||||||
|
states.Usage, dump_only=True, description=m.Device.physical.__doc__
|
||||||
|
)
|
||||||
revoke = UUID(dump_only=True)
|
revoke = UUID(dump_only=True)
|
||||||
physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor')
|
physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor')
|
||||||
production_date = DateTime('iso',
|
production_date = DateTime(
|
||||||
description=m.Device.updated.comment,
|
'iso', description=m.Device.updated.comment, data_key='productionDate'
|
||||||
data_key='productionDate')
|
)
|
||||||
working = NestedOn('Action',
|
working = NestedOn(
|
||||||
many=True,
|
'Action', many=True, dump_only=True, description=m.Device.working.__doc__
|
||||||
dump_only=True,
|
)
|
||||||
description=m.Device.working.__doc__)
|
|
||||||
variant = SanitizedStr(description=m.Device.variant.comment)
|
variant = SanitizedStr(description=m.Device.variant.comment)
|
||||||
sku = SanitizedStr(description=m.Device.sku.comment)
|
sku = SanitizedStr(description=m.Device.sku.comment)
|
||||||
image = URL(description=m.Device.image.comment)
|
image = URL(description=m.Device.image.comment)
|
||||||
allocated = Boolean(description=m.Device.allocated.comment)
|
allocated = Boolean(description=m.Device.allocated.comment)
|
||||||
devicehub_id = SanitizedStr(data_key='devicehubID',
|
devicehub_id = SanitizedStr(
|
||||||
description=m.Device.devicehub_id.comment)
|
data_key='devicehubID', description=m.Device.devicehub_id.comment
|
||||||
|
)
|
||||||
|
|
||||||
@pre_load
|
@pre_load
|
||||||
def from_actions_to_actions_one(self, data: dict):
|
def from_actions_to_actions_one(self, data: dict):
|
||||||
|
@ -91,52 +136,73 @@ class Device(Thing):
|
||||||
@post_load
|
@post_load
|
||||||
def validate_snapshot_actions(self, data):
|
def validate_snapshot_actions(self, data):
|
||||||
"""Validates that only snapshot-related actions can be uploaded."""
|
"""Validates that only snapshot-related actions can be uploaded."""
|
||||||
from ereuse_devicehub.resources.action.models import EraseBasic, Test, Rate, Install, \
|
from ereuse_devicehub.resources.action.models import (
|
||||||
Benchmark
|
Benchmark,
|
||||||
|
EraseBasic,
|
||||||
|
Install,
|
||||||
|
Rate,
|
||||||
|
Test,
|
||||||
|
)
|
||||||
|
|
||||||
for action in data['actions_one']:
|
for action in data['actions_one']:
|
||||||
if not isinstance(action, (Install, EraseBasic, Rate, Test, Benchmark)):
|
if not isinstance(action, (Install, EraseBasic, Rate, Test, Benchmark)):
|
||||||
raise ValidationError('You cannot upload {}'.format(action),
|
raise ValidationError(
|
||||||
field_names=['actions'])
|
'You cannot upload {}'.format(action), field_names=['actions']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Computer(Device):
|
class Computer(Device):
|
||||||
__doc__ = m.Computer.__doc__
|
__doc__ = m.Computer.__doc__
|
||||||
# TODO TimeOut 1. Comment components if there are time out.
|
# TODO TimeOut 1. Comment components if there are time out.
|
||||||
components = NestedOn('Component',
|
components = NestedOn(
|
||||||
many=True,
|
'Component',
|
||||||
dump_only=True,
|
many=True,
|
||||||
collection_class=OrderedSet,
|
dump_only=True,
|
||||||
description='The components that are inside this computer.')
|
collection_class=OrderedSet,
|
||||||
chassis = EnumField(enums.ComputerChassis,
|
description='The components that are inside this computer.',
|
||||||
description=m.Computer.chassis.comment)
|
)
|
||||||
ram_size = Integer(dump_only=True,
|
chassis = EnumField(enums.ComputerChassis, description=m.Computer.chassis.comment)
|
||||||
data_key='ramSize',
|
ram_size = Integer(
|
||||||
description=m.Computer.ram_size.__doc__)
|
dump_only=True, data_key='ramSize', description=m.Computer.ram_size.__doc__
|
||||||
data_storage_size = Integer(dump_only=True,
|
)
|
||||||
data_key='dataStorageSize',
|
data_storage_size = Integer(
|
||||||
description=m.Computer.data_storage_size.__doc__)
|
dump_only=True,
|
||||||
processor_model = Str(dump_only=True,
|
data_key='dataStorageSize',
|
||||||
data_key='processorModel',
|
description=m.Computer.data_storage_size.__doc__,
|
||||||
description=m.Computer.processor_model.__doc__)
|
)
|
||||||
graphic_card_model = Str(dump_only=True,
|
processor_model = Str(
|
||||||
data_key='graphicCardModel',
|
dump_only=True,
|
||||||
description=m.Computer.graphic_card_model.__doc__)
|
data_key='processorModel',
|
||||||
network_speeds = List(Integer(dump_only=True),
|
description=m.Computer.processor_model.__doc__,
|
||||||
dump_only=True,
|
)
|
||||||
data_key='networkSpeeds',
|
graphic_card_model = Str(
|
||||||
description=m.Computer.network_speeds.__doc__)
|
dump_only=True,
|
||||||
privacy = NestedOn('Action',
|
data_key='graphicCardModel',
|
||||||
many=True,
|
description=m.Computer.graphic_card_model.__doc__,
|
||||||
dump_only=True,
|
)
|
||||||
collection_class=set,
|
network_speeds = List(
|
||||||
description=m.Computer.privacy.__doc__)
|
Integer(dump_only=True),
|
||||||
amount = Integer(validate=f.validate.Range(min=0, max=100),
|
dump_only=True,
|
||||||
description=m.Computer.amount.__doc__)
|
data_key='networkSpeeds',
|
||||||
|
description=m.Computer.network_speeds.__doc__,
|
||||||
|
)
|
||||||
|
privacy = NestedOn(
|
||||||
|
'Action',
|
||||||
|
many=True,
|
||||||
|
dump_only=True,
|
||||||
|
collection_class=set,
|
||||||
|
description=m.Computer.privacy.__doc__,
|
||||||
|
)
|
||||||
|
amount = Integer(
|
||||||
|
validate=f.validate.Range(min=0, max=100), description=m.Computer.amount.__doc__
|
||||||
|
)
|
||||||
# author_id = NestedOn(s_user.User, only_query='author_id')
|
# author_id = NestedOn(s_user.User, only_query='author_id')
|
||||||
owner_id = UUID(data_key='ownerID')
|
owner_id = UUID(data_key='ownerID')
|
||||||
transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment)
|
transfer_state = EnumField(
|
||||||
|
enums.TransferState, description=m.Computer.transfer_state.comment
|
||||||
|
)
|
||||||
receiver_id = UUID(data_key='receiverID')
|
receiver_id = UUID(data_key='receiverID')
|
||||||
uuid = UUID(required=False)
|
system_uuid = UUID(required=False)
|
||||||
|
|
||||||
|
|
||||||
class Desktop(Computer):
|
class Desktop(Computer):
|
||||||
|
@ -155,29 +221,36 @@ class Server(Computer):
|
||||||
class DisplayMixin:
|
class DisplayMixin:
|
||||||
__doc__ = m.DisplayMixin.__doc__
|
__doc__ = m.DisplayMixin.__doc__
|
||||||
size = Float(description=m.DisplayMixin.size.comment, validate=Range(2, 150))
|
size = Float(description=m.DisplayMixin.size.comment, validate=Range(2, 150))
|
||||||
technology = EnumField(enums.DisplayTech,
|
technology = EnumField(
|
||||||
description=m.DisplayMixin.technology.comment)
|
enums.DisplayTech, description=m.DisplayMixin.technology.comment
|
||||||
resolution_width = Integer(data_key='resolutionWidth',
|
)
|
||||||
validate=Range(10, 20000),
|
resolution_width = Integer(
|
||||||
description=m.DisplayMixin.resolution_width.comment,
|
data_key='resolutionWidth',
|
||||||
)
|
validate=Range(10, 20000),
|
||||||
resolution_height = Integer(data_key='resolutionHeight',
|
description=m.DisplayMixin.resolution_width.comment,
|
||||||
validate=Range(10, 20000),
|
)
|
||||||
description=m.DisplayMixin.resolution_height.comment,
|
resolution_height = Integer(
|
||||||
)
|
data_key='resolutionHeight',
|
||||||
|
validate=Range(10, 20000),
|
||||||
|
description=m.DisplayMixin.resolution_height.comment,
|
||||||
|
)
|
||||||
refresh_rate = Integer(data_key='refreshRate', validate=Range(10, 1000))
|
refresh_rate = Integer(data_key='refreshRate', validate=Range(10, 1000))
|
||||||
contrast_ratio = Integer(data_key='contrastRatio', validate=Range(100, 100000))
|
contrast_ratio = Integer(data_key='contrastRatio', validate=Range(100, 100000))
|
||||||
touchable = Boolean(description=m.DisplayMixin.touchable.comment)
|
touchable = Boolean(description=m.DisplayMixin.touchable.comment)
|
||||||
aspect_ratio = String(dump_only=True, description=m.DisplayMixin.aspect_ratio.__doc__)
|
aspect_ratio = String(
|
||||||
|
dump_only=True, description=m.DisplayMixin.aspect_ratio.__doc__
|
||||||
|
)
|
||||||
widescreen = Boolean(dump_only=True, description=m.DisplayMixin.widescreen.__doc__)
|
widescreen = Boolean(dump_only=True, description=m.DisplayMixin.widescreen.__doc__)
|
||||||
|
|
||||||
|
|
||||||
class NetworkMixin:
|
class NetworkMixin:
|
||||||
__doc__ = m.NetworkMixin.__doc__
|
__doc__ = m.NetworkMixin.__doc__
|
||||||
|
|
||||||
speed = Integer(validate=Range(min=10, max=10000),
|
speed = Integer(
|
||||||
unit=UnitCodes.mbps,
|
validate=Range(min=10, max=10000),
|
||||||
description=m.NetworkAdapter.speed.comment)
|
unit=UnitCodes.mbps,
|
||||||
|
description=m.NetworkAdapter.speed.comment,
|
||||||
|
)
|
||||||
wireless = Boolean(required=True)
|
wireless = Boolean(required=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -198,16 +271,22 @@ class Mobile(Device):
|
||||||
|
|
||||||
imei = Integer(description=m.Mobile.imei.comment)
|
imei = Integer(description=m.Mobile.imei.comment)
|
||||||
meid = Str(description=m.Mobile.meid.comment)
|
meid = Str(description=m.Mobile.meid.comment)
|
||||||
ram_size = Integer(validate=Range(min=128, max=36000),
|
ram_size = Integer(
|
||||||
data_key='ramSize',
|
validate=Range(min=128, max=36000),
|
||||||
unit=UnitCodes.mbyte,
|
data_key='ramSize',
|
||||||
description=m.Mobile.ram_size.comment)
|
unit=UnitCodes.mbyte,
|
||||||
data_storage_size = Integer(validate=Range(0, 10 ** 8),
|
description=m.Mobile.ram_size.comment,
|
||||||
data_key='dataStorageSize',
|
)
|
||||||
description=m.Mobile.data_storage_size)
|
data_storage_size = Integer(
|
||||||
display_size = Float(validate=Range(min=0.1, max=30.0),
|
validate=Range(0, 10**8),
|
||||||
data_key='displaySize',
|
data_key='dataStorageSize',
|
||||||
description=m.Mobile.display_size.comment)
|
description=m.Mobile.data_storage_size,
|
||||||
|
)
|
||||||
|
display_size = Float(
|
||||||
|
validate=Range(min=0.1, max=30.0),
|
||||||
|
data_key='displaySize',
|
||||||
|
description=m.Mobile.display_size.comment,
|
||||||
|
)
|
||||||
|
|
||||||
@pre_load
|
@pre_load
|
||||||
def convert_check_imei(self, data):
|
def convert_check_imei(self, data):
|
||||||
|
@ -243,17 +322,21 @@ class Component(Device):
|
||||||
class GraphicCard(Component):
|
class GraphicCard(Component):
|
||||||
__doc__ = m.GraphicCard.__doc__
|
__doc__ = m.GraphicCard.__doc__
|
||||||
|
|
||||||
memory = Integer(validate=Range(0, 10000),
|
memory = Integer(
|
||||||
unit=UnitCodes.mbyte,
|
validate=Range(0, 10000),
|
||||||
description=m.GraphicCard.memory.comment)
|
unit=UnitCodes.mbyte,
|
||||||
|
description=m.GraphicCard.memory.comment,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DataStorage(Component):
|
class DataStorage(Component):
|
||||||
__doc__ = m.DataStorage.__doc__
|
__doc__ = m.DataStorage.__doc__
|
||||||
|
|
||||||
size = Integer(validate=Range(0, 10 ** 8),
|
size = Integer(
|
||||||
unit=UnitCodes.mbyte,
|
validate=Range(0, 10**8),
|
||||||
description=m.DataStorage.size.comment)
|
unit=UnitCodes.mbyte,
|
||||||
|
description=m.DataStorage.size.comment,
|
||||||
|
)
|
||||||
interface = EnumField(enums.DataStorageInterface)
|
interface = EnumField(enums.DataStorageInterface)
|
||||||
privacy = NestedOn('Action', dump_only=True)
|
privacy = NestedOn('Action', dump_only=True)
|
||||||
|
|
||||||
|
@ -269,16 +352,21 @@ class SolidStateDrive(DataStorage):
|
||||||
class Motherboard(Component):
|
class Motherboard(Component):
|
||||||
__doc__ = m.Motherboard.__doc__
|
__doc__ = m.Motherboard.__doc__
|
||||||
|
|
||||||
slots = Integer(validate=Range(0, 20),
|
slots = Integer(validate=Range(0, 20), description=m.Motherboard.slots.comment)
|
||||||
description=m.Motherboard.slots.comment)
|
|
||||||
usb = Integer(validate=Range(0, 20), description=m.Motherboard.usb.comment)
|
usb = Integer(validate=Range(0, 20), description=m.Motherboard.usb.comment)
|
||||||
firewire = Integer(validate=Range(0, 20), description=m.Motherboard.firewire.comment)
|
firewire = Integer(
|
||||||
|
validate=Range(0, 20), description=m.Motherboard.firewire.comment
|
||||||
|
)
|
||||||
serial = Integer(validate=Range(0, 20), description=m.Motherboard.serial.comment)
|
serial = Integer(validate=Range(0, 20), description=m.Motherboard.serial.comment)
|
||||||
pcmcia = Integer(validate=Range(0, 20), description=m.Motherboard.pcmcia.comment)
|
pcmcia = Integer(validate=Range(0, 20), description=m.Motherboard.pcmcia.comment)
|
||||||
bios_date = Date(validate=Range(datetime.date(year=1980, month=1, day=1),
|
bios_date = Date(
|
||||||
datetime.date(year=2030, month=1, day=1)),
|
validate=Range(
|
||||||
data_key='biosDate',
|
datetime.date(year=1980, month=1, day=1),
|
||||||
description=m.Motherboard.bios_date)
|
datetime.date(year=2030, month=1, day=1),
|
||||||
|
),
|
||||||
|
data_key='biosDate',
|
||||||
|
description=m.Motherboard.bios_date,
|
||||||
|
)
|
||||||
ram_slots = Integer(validate=Range(1), data_key='ramSlots')
|
ram_slots = Integer(validate=Range(1), data_key='ramSlots')
|
||||||
ram_max_size = Integer(validate=Range(1), data_key='ramMaxSize')
|
ram_max_size = Integer(validate=Range(1), data_key='ramMaxSize')
|
||||||
|
|
||||||
|
@ -290,22 +378,32 @@ class NetworkAdapter(NetworkMixin, Component):
|
||||||
class Processor(Component):
|
class Processor(Component):
|
||||||
__doc__ = m.Processor.__doc__
|
__doc__ = m.Processor.__doc__
|
||||||
|
|
||||||
speed = Float(validate=Range(min=0.1, max=15),
|
speed = Float(
|
||||||
unit=UnitCodes.ghz,
|
validate=Range(min=0.1, max=15),
|
||||||
description=m.Processor.speed.comment)
|
unit=UnitCodes.ghz,
|
||||||
cores = Integer(validate=Range(min=1, max=10), description=m.Processor.cores.comment)
|
description=m.Processor.speed.comment,
|
||||||
threads = Integer(validate=Range(min=1, max=20), description=m.Processor.threads.comment)
|
)
|
||||||
address = Integer(validate=OneOf({8, 16, 32, 64, 128, 256}),
|
cores = Integer(
|
||||||
description=m.Processor.address.comment)
|
validate=Range(min=1, max=10), description=m.Processor.cores.comment
|
||||||
|
)
|
||||||
|
threads = Integer(
|
||||||
|
validate=Range(min=1, max=20), description=m.Processor.threads.comment
|
||||||
|
)
|
||||||
|
address = Integer(
|
||||||
|
validate=OneOf({8, 16, 32, 64, 128, 256}),
|
||||||
|
description=m.Processor.address.comment,
|
||||||
|
)
|
||||||
abi = SanitizedStr(lower=True, description=m.Processor.abi.comment)
|
abi = SanitizedStr(lower=True, description=m.Processor.abi.comment)
|
||||||
|
|
||||||
|
|
||||||
class RamModule(Component):
|
class RamModule(Component):
|
||||||
__doc__ = m.RamModule.__doc__
|
__doc__ = m.RamModule.__doc__
|
||||||
|
|
||||||
size = Integer(validate=Range(min=128, max=17000),
|
size = Integer(
|
||||||
unit=UnitCodes.mbyte,
|
validate=Range(min=128, max=17000),
|
||||||
description=m.RamModule.size.comment)
|
unit=UnitCodes.mbyte,
|
||||||
|
description=m.RamModule.size.comment,
|
||||||
|
)
|
||||||
speed = Integer(validate=Range(min=100, max=10000), unit=UnitCodes.mhz)
|
speed = Integer(validate=Range(min=100, max=10000), unit=UnitCodes.mhz)
|
||||||
interface = EnumField(enums.RamInterface)
|
interface = EnumField(enums.RamInterface)
|
||||||
format = EnumField(enums.RamFormat)
|
format = EnumField(enums.RamFormat)
|
||||||
|
@ -323,7 +421,9 @@ class Battery(Component):
|
||||||
__doc__ = m.Battery.__doc__
|
__doc__ = m.Battery.__doc__
|
||||||
|
|
||||||
wireless = Boolean(description=m.Battery.wireless.comment)
|
wireless = Boolean(description=m.Battery.wireless.comment)
|
||||||
technology = EnumField(enums.BatteryTechnology, description=m.Battery.technology.comment)
|
technology = EnumField(
|
||||||
|
enums.BatteryTechnology, description=m.Battery.technology.comment
|
||||||
|
)
|
||||||
size = Integer(required=True, description=m.Battery.size.comment)
|
size = Integer(required=True, description=m.Battery.size.comment)
|
||||||
|
|
||||||
|
|
||||||
|
@ -393,12 +493,18 @@ class WirelessAccessPoint(Networking):
|
||||||
class Printer(Device):
|
class Printer(Device):
|
||||||
__doc__ = m.Printer.__doc__
|
__doc__ = m.Printer.__doc__
|
||||||
|
|
||||||
wireless = Boolean(required=True, missing=False, description=m.Printer.wireless.comment)
|
wireless = Boolean(
|
||||||
scanning = Boolean(required=True, missing=False, description=m.Printer.scanning.comment)
|
required=True, missing=False, description=m.Printer.wireless.comment
|
||||||
technology = EnumField(enums.PrinterTechnology,
|
)
|
||||||
required=True,
|
scanning = Boolean(
|
||||||
description=m.Printer.technology.comment)
|
required=True, missing=False, description=m.Printer.scanning.comment
|
||||||
monochrome = Boolean(required=True, missing=True, description=m.Printer.monochrome.comment)
|
)
|
||||||
|
technology = EnumField(
|
||||||
|
enums.PrinterTechnology, required=True, description=m.Printer.technology.comment
|
||||||
|
)
|
||||||
|
monochrome = Boolean(
|
||||||
|
required=True, missing=True, description=m.Printer.monochrome.comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LabelPrinter(Printer):
|
class LabelPrinter(Printer):
|
||||||
|
|
|
@ -13,10 +13,14 @@ from teal.marshmallow import ValidationError
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.action.models import Remove
|
from ereuse_devicehub.resources.action.models import Remove
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer, Device, DataStorage
|
from ereuse_devicehub.resources.device.models import (
|
||||||
|
Component,
|
||||||
|
Computer,
|
||||||
|
DataStorage,
|
||||||
|
Device,
|
||||||
|
)
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
|
|
||||||
|
|
||||||
DEVICES_ALLOW_DUPLICITY = [
|
DEVICES_ALLOW_DUPLICITY = [
|
||||||
'RamModule',
|
'RamModule',
|
||||||
'Display',
|
'Display',
|
||||||
|
@ -26,12 +30,13 @@ DEVICES_ALLOW_DUPLICITY = [
|
||||||
'GraphicCard',
|
'GraphicCard',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Sync:
|
class Sync:
|
||||||
"""Synchronizes the device and components with the database."""
|
"""Synchronizes the device and components with the database."""
|
||||||
|
|
||||||
def run(self,
|
def run(
|
||||||
device: Device,
|
self, device: Device, components: Iterable[Component] or None
|
||||||
components: Iterable[Component] or None) -> (Device, OrderedSet):
|
) -> (Device, OrderedSet):
|
||||||
"""Synchronizes the device and components with the database.
|
"""Synchronizes the device and components with the database.
|
||||||
|
|
||||||
Identifies if the device and components exist in the database
|
Identifies if the device and components exist in the database
|
||||||
|
@ -72,9 +77,9 @@ class Sync:
|
||||||
blacklist = set() # type: Set[int]
|
blacklist = set() # type: Set[int]
|
||||||
not_new_components = set()
|
not_new_components = set()
|
||||||
for component in components:
|
for component in components:
|
||||||
db_component, is_new = self.execute_register_component(component,
|
db_component, is_new = self.execute_register_component(
|
||||||
blacklist,
|
component, blacklist, parent=db_device
|
||||||
parent=db_device)
|
)
|
||||||
db_components.add(db_component)
|
db_components.add(db_component)
|
||||||
if not is_new:
|
if not is_new:
|
||||||
not_new_components.add(db_component)
|
not_new_components.add(db_component)
|
||||||
|
@ -83,10 +88,9 @@ class Sync:
|
||||||
db_device.components = db_components
|
db_device.components = db_components
|
||||||
return db_device, actions
|
return db_device, actions
|
||||||
|
|
||||||
def execute_register_component(self,
|
def execute_register_component(
|
||||||
component: Component,
|
self, component: Component, blacklist: Set[int], parent: Computer
|
||||||
blacklist: Set[int],
|
):
|
||||||
parent: Computer):
|
|
||||||
"""Synchronizes one component to the DB.
|
"""Synchronizes one component to the DB.
|
||||||
|
|
||||||
This method is a specialization of :meth:`.execute_register`
|
This method is a specialization of :meth:`.execute_register`
|
||||||
|
@ -118,9 +122,12 @@ class Sync:
|
||||||
# if not, then continue with the traditional behaviour
|
# if not, then continue with the traditional behaviour
|
||||||
try:
|
try:
|
||||||
if component.hid:
|
if component.hid:
|
||||||
db_component = Device.query.filter_by(hid=component.hid, owner_id=g.user.id).one()
|
db_component = Device.query.filter_by(
|
||||||
assert isinstance(db_component, Device), \
|
hid=component.hid, owner_id=g.user.id
|
||||||
'{} must be a component'.format(db_component)
|
).one()
|
||||||
|
assert isinstance(
|
||||||
|
db_component, Device
|
||||||
|
), '{} must be a component'.format(db_component)
|
||||||
else:
|
else:
|
||||||
# Is there a component similar to ours?
|
# Is there a component similar to ours?
|
||||||
db_component = component.similar_one(parent, blacklist)
|
db_component = component.similar_one(parent, blacklist)
|
||||||
|
@ -166,15 +173,35 @@ class Sync:
|
||||||
:return: The synced device from the db with the tags linked.
|
:return: The synced device from the db with the tags linked.
|
||||||
"""
|
"""
|
||||||
assert inspect(device).transient, 'Device cannot be already synced from DB'
|
assert inspect(device).transient, 'Device cannot be already synced from DB'
|
||||||
assert all(inspect(tag).transient for tag in device.tags), 'Tags cannot be synced from DB'
|
assert all(
|
||||||
|
inspect(tag).transient for tag in device.tags
|
||||||
|
), 'Tags cannot be synced from DB'
|
||||||
db_device = None
|
db_device = None
|
||||||
if device.hid:
|
if isinstance(device, Computer):
|
||||||
|
# first search by uuid
|
||||||
|
if device.system_uuid:
|
||||||
|
with suppress(ResourceNotFound):
|
||||||
|
db_device = Computer.query.filter_by(
|
||||||
|
system_uuid=device.system_uuid, owner_id=g.user.id, active=True
|
||||||
|
).one()
|
||||||
|
# if no there are any Computer by uuid search by hid
|
||||||
|
if not db_device and device.hid:
|
||||||
|
with suppress(ResourceNotFound):
|
||||||
|
db_device = Device.query.filter_by(
|
||||||
|
hid=device.hid, owner_id=g.user.id, active=True
|
||||||
|
).one()
|
||||||
|
elif device.hid:
|
||||||
with suppress(ResourceNotFound):
|
with suppress(ResourceNotFound):
|
||||||
db_device = Device.query.filter_by(hid=device.hid, owner_id=g.user.id, active=True).one()
|
db_device = Device.query.filter_by(
|
||||||
|
hid=device.hid, owner_id=g.user.id, active=True
|
||||||
|
).one()
|
||||||
if db_device and db_device.allocated:
|
if db_device and db_device.allocated:
|
||||||
raise ResourceNotFound('device is actually allocated {}'.format(device))
|
raise ResourceNotFound('device is actually allocated {}'.format(device))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tags = {Tag.from_an_id(tag.id).one() for tag in device.tags} # type: Set[Tag]
|
tags = {
|
||||||
|
Tag.from_an_id(tag.id).one() for tag in device.tags
|
||||||
|
} # type: Set[Tag]
|
||||||
except ResourceNotFound:
|
except ResourceNotFound:
|
||||||
raise ResourceNotFound('tag you are linking to device {}'.format(device))
|
raise ResourceNotFound('tag you are linking to device {}'.format(device))
|
||||||
linked_tags = {tag for tag in tags if tag.device_id} # type: Set[Tag]
|
linked_tags = {tag for tag in tags if tag.device_id} # type: Set[Tag]
|
||||||
|
@ -182,16 +209,22 @@ class Sync:
|
||||||
sample_tag = next(iter(linked_tags))
|
sample_tag = next(iter(linked_tags))
|
||||||
for tag in linked_tags:
|
for tag in linked_tags:
|
||||||
if tag.device_id != sample_tag.device_id:
|
if tag.device_id != sample_tag.device_id:
|
||||||
raise MismatchBetweenTags(tag, sample_tag) # Tags linked to different devices
|
raise MismatchBetweenTags(
|
||||||
|
tag, sample_tag
|
||||||
|
) # Tags linked to different devices
|
||||||
if db_device: # Device from hid
|
if db_device: # Device from hid
|
||||||
if sample_tag.device_id != db_device.id: # Device from hid != device from tags
|
if (
|
||||||
|
sample_tag.device_id != db_device.id
|
||||||
|
): # Device from hid != device from tags
|
||||||
raise MismatchBetweenTagsAndHid(db_device.id, db_device.hid)
|
raise MismatchBetweenTagsAndHid(db_device.id, db_device.hid)
|
||||||
else: # There was no device from hid
|
else: # There was no device from hid
|
||||||
if sample_tag.device.physical_properties != device.physical_properties:
|
if sample_tag.device.physical_properties != device.physical_properties:
|
||||||
# Incoming physical props of device != props from tag's device
|
# Incoming physical props of device != props from tag's device
|
||||||
# which means that the devices are not the same
|
# which means that the devices are not the same
|
||||||
raise MismatchBetweenProperties(sample_tag.device.physical_properties,
|
raise MismatchBetweenProperties(
|
||||||
device.physical_properties)
|
sample_tag.device.physical_properties,
|
||||||
|
device.physical_properties,
|
||||||
|
)
|
||||||
db_device = sample_tag.device
|
db_device = sample_tag.device
|
||||||
if db_device: # Device from hid or tags
|
if db_device: # Device from hid or tags
|
||||||
self.merge(device, db_device)
|
self.merge(device, db_device)
|
||||||
|
@ -199,17 +232,21 @@ class Sync:
|
||||||
device.tags.clear() # We don't want to add the transient dummy tags
|
device.tags.clear() # We don't want to add the transient dummy tags
|
||||||
db.session.add(device)
|
db.session.add(device)
|
||||||
db_device = device
|
db_device = device
|
||||||
db_device.tags |= tags # Union of tags the device had plus the (potentially) new ones
|
db_device.tags |= (
|
||||||
|
tags # Union of tags the device had plus the (potentially) new ones
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
# Manage 'one tag per organization' unique constraint
|
# Manage 'one tag per organization' unique constraint
|
||||||
if 'One tag per organization' in e.args[0]:
|
if 'One tag per organization' in e.args[0]:
|
||||||
# todo test for this
|
# todo test for this
|
||||||
id = int(e.args[0][135:e.args[0].index(',', 135)])
|
id = int(e.args[0][135 : e.args[0].index(',', 135)])
|
||||||
raise ValidationError('The device is already linked to tag {} '
|
raise ValidationError(
|
||||||
'from the same organization.'.format(id),
|
'The device is already linked to tag {} '
|
||||||
field_names=['device.tags'])
|
'from the same organization.'.format(id),
|
||||||
|
field_names=['device.tags'],
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
assert db_device is not None
|
assert db_device is not None
|
||||||
|
@ -222,15 +259,21 @@ class Sync:
|
||||||
This method mutates db_device.
|
This method mutates db_device.
|
||||||
"""
|
"""
|
||||||
if db_device.owner_id != g.user.id:
|
if db_device.owner_id != g.user.id:
|
||||||
return
|
return
|
||||||
|
|
||||||
for field_name, value in device.physical_properties.items():
|
for field_name, value in device.physical_properties.items():
|
||||||
if value is not None:
|
if value is not None:
|
||||||
setattr(db_device, field_name, value)
|
setattr(db_device, field_name, value)
|
||||||
|
|
||||||
|
# if device.system_uuid and db_device.system_uuid and device.system_uuid != db_device.system_uuid:
|
||||||
|
# TODO @cayop send error to sentry.io
|
||||||
|
# there are 2 computers duplicate get db_device for hid
|
||||||
|
|
||||||
|
if hasattr(device, 'system_uuid') and device.system_uuid:
|
||||||
|
db_device.system_uuid = device.system_uuid
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_remove(device: Computer,
|
def add_remove(device: Computer, components: Set[Component]) -> OrderedSet:
|
||||||
components: Set[Component]) -> OrderedSet:
|
|
||||||
"""Generates the Add and Remove actions (but doesn't add them to
|
"""Generates the Add and Remove actions (but doesn't add them to
|
||||||
session).
|
session).
|
||||||
|
|
||||||
|
@ -251,9 +294,13 @@ class Sync:
|
||||||
if adding:
|
if adding:
|
||||||
# For the components we are adding, let's remove them from their old parents
|
# For the components we are adding, let's remove them from their old parents
|
||||||
def g_parent(component: Component) -> Device:
|
def g_parent(component: Component) -> Device:
|
||||||
return component.parent or Device(id=0) # Computer with id 0 is our Identity
|
return component.parent or Device(
|
||||||
|
id=0
|
||||||
|
) # Computer with id 0 is our Identity
|
||||||
|
|
||||||
for parent, _components in groupby(sorted(adding, key=g_parent), key=g_parent):
|
for parent, _components in groupby(
|
||||||
|
sorted(adding, key=g_parent), key=g_parent
|
||||||
|
):
|
||||||
set_components = OrderedSet(_components)
|
set_components = OrderedSet(_components)
|
||||||
check_owners = (x.owner_id == g.user.id for x in set_components)
|
check_owners = (x.owner_id == g.user.id for x in set_components)
|
||||||
# Is not Computer Identity and all components have the correct owner
|
# Is not Computer Identity and all components have the correct owner
|
||||||
|
@ -263,21 +310,18 @@ class Sync:
|
||||||
|
|
||||||
|
|
||||||
class MismatchBetweenTags(ValidationError):
|
class MismatchBetweenTags(ValidationError):
|
||||||
def __init__(self,
|
def __init__(self, tag: Tag, other_tag: Tag, field_names={'device.tags'}):
|
||||||
tag: Tag,
|
message = '{!r} and {!r} are linked to different devices.'.format(
|
||||||
other_tag: Tag,
|
tag, other_tag
|
||||||
field_names={'device.tags'}):
|
)
|
||||||
message = '{!r} and {!r} are linked to different devices.'.format(tag, other_tag)
|
|
||||||
super().__init__(message, field_names)
|
super().__init__(message, field_names)
|
||||||
|
|
||||||
|
|
||||||
class MismatchBetweenTagsAndHid(ValidationError):
|
class MismatchBetweenTagsAndHid(ValidationError):
|
||||||
def __init__(self,
|
def __init__(self, device_id: int, hid: str, field_names={'device.hid'}):
|
||||||
device_id: int,
|
message = 'Tags are linked to device {} but hid refers to device {}.'.format(
|
||||||
hid: str,
|
device_id, hid
|
||||||
field_names={'device.hid'}):
|
)
|
||||||
message = 'Tags are linked to device {} but hid refers to device {}.'.format(device_id,
|
|
||||||
hid)
|
|
||||||
super().__init__(message, field_names)
|
super().__init__(message, field_names)
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,6 +329,8 @@ class MismatchBetweenProperties(ValidationError):
|
||||||
def __init__(self, props1, props2, field_names={'device'}):
|
def __init__(self, props1, props2, field_names={'device'}):
|
||||||
message = 'The device from the tag and the passed-in differ the following way:'
|
message = 'The device from the tag and the passed-in differ the following way:'
|
||||||
message += '\n'.join(
|
message += '\n'.join(
|
||||||
difflib.ndiff(yaml.dump(props1).splitlines(), yaml.dump(props2).splitlines())
|
difflib.ndiff(
|
||||||
|
yaml.dump(props1).splitlines(), yaml.dump(props2).splitlines()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
super().__init__(message, field_names)
|
super().__init__(message, field_names)
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
""" This file frame a correct row for csv report """
|
""" This file frame a correct row for csv report """
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
|
||||||
from ereuse_devicehub.resources.enums import Severity
|
|
||||||
from ereuse_devicehub.resources.device import models as d, states
|
|
||||||
from ereuse_devicehub.resources.action import models as da
|
from ereuse_devicehub.resources.action import models as da
|
||||||
from ereuse_devicehub.resources.action.models import (BenchmarkDataStorage, RateComputer,
|
from ereuse_devicehub.resources.action.models import (
|
||||||
TestDataStorage)
|
BenchmarkDataStorage,
|
||||||
|
RateComputer,
|
||||||
|
TestDataStorage,
|
||||||
|
)
|
||||||
|
from ereuse_devicehub.resources.device import models as d
|
||||||
|
from ereuse_devicehub.resources.device import states
|
||||||
|
from ereuse_devicehub.resources.enums import Severity
|
||||||
|
|
||||||
|
|
||||||
class DeviceRow(OrderedDict):
|
class DeviceRow(OrderedDict):
|
||||||
|
@ -40,20 +45,20 @@ class DeviceRow(OrderedDict):
|
||||||
software = ''
|
software = ''
|
||||||
if snapshot:
|
if snapshot:
|
||||||
software = "{software} {version}".format(
|
software = "{software} {version}".format(
|
||||||
software=snapshot.software.name, version=snapshot.version)
|
software=snapshot.software.name, version=snapshot.version
|
||||||
|
)
|
||||||
# General information about device
|
# General information about device
|
||||||
self['DHID'] = device.devicehub_id
|
self['DHID'] = device.devicehub_id
|
||||||
self['DocumentID'] = self.document_id
|
self['DocumentID'] = self.document_id
|
||||||
self['Public Link'] = '{url}{id}'.format(
|
self['Public Link'] = '{url}{id}'.format(
|
||||||
url=url_for('Device.main', _external=True),
|
url=url_for('Device.main', _external=True), id=device.devicehub_id
|
||||||
id=device.devicehub_id)
|
)
|
||||||
self['Lots'] = ', '.join([x.name for x in self.device.lots])
|
self['Lots'] = ', '.join([x.name for x in self.device.lots])
|
||||||
self['Tag 1 Type'] = self['Tag 1 ID'] = self['Tag 1 Organization'] = ''
|
self['Tag 1 Type'] = self['Tag 1 ID'] = self['Tag 1 Organization'] = ''
|
||||||
self['Tag 2 Type'] = self['Tag 2 ID'] = self['Tag 2 Organization'] = ''
|
self['Tag 2 Type'] = self['Tag 2 ID'] = self['Tag 2 Organization'] = ''
|
||||||
self['Tag 3 Type'] = self['Tag 3 ID'] = self['Tag 3 Organization'] = ''
|
self['Tag 3 Type'] = self['Tag 3 ID'] = self['Tag 3 Organization'] = ''
|
||||||
for i, tag in zip(range(1, 3), device.tags):
|
for i, tag in zip(range(1, 3), device.tags):
|
||||||
self['Tag {} Type'.format(
|
self['Tag {} Type'.format(i)] = 'unamed' if tag.provider else 'named'
|
||||||
i)] = 'unamed' if tag.provider else 'named'
|
|
||||||
self['Tag {} ID'.format(i)] = tag.id
|
self['Tag {} ID'.format(i)] = tag.id
|
||||||
self['Tag {} Organization'.format(i)] = tag.org.name
|
self['Tag {} Organization'.format(i)] = tag.org.name
|
||||||
|
|
||||||
|
@ -79,8 +84,7 @@ class DeviceRow(OrderedDict):
|
||||||
self['Allocate state'] = device.allocated_status.type
|
self['Allocate state'] = device.allocated_status.type
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self['Lifecycle state'] = device.last_action_of(
|
self['Lifecycle state'] = device.last_action_of(*states.Status.actions()).t
|
||||||
*states.Trading.actions()).t
|
|
||||||
except LookupError:
|
except LookupError:
|
||||||
self['Lifecycle state'] = ''
|
self['Lifecycle state'] = ''
|
||||||
if isinstance(device, d.Computer):
|
if isinstance(device, d.Computer):
|
||||||
|
@ -155,10 +159,12 @@ class DeviceRow(OrderedDict):
|
||||||
self['{} {} Serial Number'.format(ctype, i)] = ''
|
self['{} {} Serial Number'.format(ctype, i)] = ''
|
||||||
else:
|
else:
|
||||||
self['{} {} Manufacturer'.format(ctype, i)] = none2str(
|
self['{} {} Manufacturer'.format(ctype, i)] = none2str(
|
||||||
component.manufacturer)
|
component.manufacturer
|
||||||
|
)
|
||||||
self['{} {} Model'.format(ctype, i)] = none2str(component.model)
|
self['{} {} Model'.format(ctype, i)] = none2str(component.model)
|
||||||
self['{} {} Serial Number'.format(ctype, i)] = none2str(
|
self['{} {} Serial Number'.format(ctype, i)] = none2str(
|
||||||
component.serial_number)
|
component.serial_number
|
||||||
|
)
|
||||||
|
|
||||||
if ctype == d.Processor.t:
|
if ctype == d.Processor.t:
|
||||||
self.get_processor(ctype, i, component)
|
self.get_processor(ctype, i, component)
|
||||||
|
@ -178,12 +184,10 @@ class DeviceRow(OrderedDict):
|
||||||
self['{} {} Number of cores'.format(ctype, i)] = ''
|
self['{} {} Number of cores'.format(ctype, i)] = ''
|
||||||
self['{} {} Speed (GHz)'.format(ctype, i)] = ''
|
self['{} {} Speed (GHz)'.format(ctype, i)] = ''
|
||||||
self['Benchmark {} {} (points)'.format(ctype, i)] = ''
|
self['Benchmark {} {} (points)'.format(ctype, i)] = ''
|
||||||
self['Benchmark ProcessorSysbench {} {} (points)'.format(
|
self['Benchmark ProcessorSysbench {} {} (points)'.format(ctype, i)] = ''
|
||||||
ctype, i)] = ''
|
|
||||||
return
|
return
|
||||||
|
|
||||||
self['{} {} Number of cores'.format(
|
self['{} {} Number of cores'.format(ctype, i)] = none2str(component.cores)
|
||||||
ctype, i)] = none2str(component.cores)
|
|
||||||
self['{} {} Speed (GHz)'.format(ctype, i)] = none2str(component.speed)
|
self['{} {} Speed (GHz)'.format(ctype, i)] = none2str(component.speed)
|
||||||
|
|
||||||
benchmark = get_action(component, 'BenchmarkProcessor')
|
benchmark = get_action(component, 'BenchmarkProcessor')
|
||||||
|
@ -194,11 +198,11 @@ class DeviceRow(OrderedDict):
|
||||||
|
|
||||||
sysbench = get_action(component, 'BenchmarkProcessorSysbench')
|
sysbench = get_action(component, 'BenchmarkProcessorSysbench')
|
||||||
if not sysbench:
|
if not sysbench:
|
||||||
self['Benchmark ProcessorSysbench {} {} (points)'.format(
|
self['Benchmark ProcessorSysbench {} {} (points)'.format(ctype, i)] = ''
|
||||||
ctype, i)] = ''
|
|
||||||
return
|
return
|
||||||
self['Benchmark ProcessorSysbench {} {} (points)'.format(
|
self[
|
||||||
ctype, i)] = sysbench.rate
|
'Benchmark ProcessorSysbench {} {} (points)'.format(ctype, i)
|
||||||
|
] = sysbench.rate
|
||||||
|
|
||||||
def get_ram(self, ctype, i, component):
|
def get_ram(self, ctype, i, component):
|
||||||
"""Particular fields for component Ram Module."""
|
"""Particular fields for component Ram Module."""
|
||||||
|
@ -212,7 +216,7 @@ class DeviceRow(OrderedDict):
|
||||||
|
|
||||||
def get_datastorage(self, ctype, i, component):
|
def get_datastorage(self, ctype, i, component):
|
||||||
"""Particular fields for component DataStorage.
|
"""Particular fields for component DataStorage.
|
||||||
A DataStorage can be HardDrive or SolidStateDrive.
|
A DataStorage can be HardDrive or SolidStateDrive.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if component is None:
|
if component is None:
|
||||||
|
@ -244,21 +248,23 @@ class DeviceRow(OrderedDict):
|
||||||
software = ''
|
software = ''
|
||||||
if snapshot:
|
if snapshot:
|
||||||
software = "{software} {version}".format(
|
software = "{software} {version}".format(
|
||||||
software=snapshot.software.name, version=snapshot.version)
|
software=snapshot.software.name, version=snapshot.version
|
||||||
|
)
|
||||||
|
|
||||||
self['{} {} Size (MB)'.format(ctype, i)] = none2str(component.size)
|
self['{} {} Size (MB)'.format(ctype, i)] = none2str(component.size)
|
||||||
|
|
||||||
component_actions = sorted(component.actions, key=lambda x: x.created)
|
component_actions = sorted(component.actions, key=lambda x: x.created)
|
||||||
erasures = [a for a in component_actions if a.type in [
|
erasures = [
|
||||||
'EraseBasic', 'EraseSectors', 'DataWipe']]
|
a
|
||||||
|
for a in component_actions
|
||||||
|
if a.type in ['EraseBasic', 'EraseSectors', 'DataWipe']
|
||||||
|
]
|
||||||
erasure = erasures[-1] if erasures else None
|
erasure = erasures[-1] if erasures else None
|
||||||
if not erasure:
|
if not erasure:
|
||||||
self['Erasure {} {}'.format(ctype, i)] = none2str(component.hid)
|
self['Erasure {} {}'.format(ctype, i)] = none2str(component.hid)
|
||||||
serial_number = none2str(component.serial_number)
|
serial_number = none2str(component.serial_number)
|
||||||
self['Erasure {} {} Serial Number'.format(
|
self['Erasure {} {} Serial Number'.format(ctype, i)] = serial_number
|
||||||
ctype, i)] = serial_number
|
self['Erasure {} {} Size (MB)'.format(ctype, i)] = none2str(component.size)
|
||||||
self['Erasure {} {} Size (MB)'.format(
|
|
||||||
ctype, i)] = none2str(component.size)
|
|
||||||
self['Erasure {} {} Software'.format(ctype, i)] = ''
|
self['Erasure {} {} Software'.format(ctype, i)] = ''
|
||||||
self['Erasure {} {} Result'.format(ctype, i)] = ''
|
self['Erasure {} {} Result'.format(ctype, i)] = ''
|
||||||
self['Erasure {} {} Certificate URL'.format(ctype, i)] = ''
|
self['Erasure {} {} Certificate URL'.format(ctype, i)] = ''
|
||||||
|
@ -272,15 +278,13 @@ class DeviceRow(OrderedDict):
|
||||||
elif hasattr(erasure, 'type') and erasure.type == 'DataWipe':
|
elif hasattr(erasure, 'type') and erasure.type == 'DataWipe':
|
||||||
self['Erasure {} {}'.format(ctype, i)] = none2str(component.hid)
|
self['Erasure {} {}'.format(ctype, i)] = none2str(component.hid)
|
||||||
serial_number = none2str(component.serial_number)
|
serial_number = none2str(component.serial_number)
|
||||||
self['Erasure {} {} Serial Number'.format(
|
self['Erasure {} {} Serial Number'.format(ctype, i)] = serial_number
|
||||||
ctype, i)] = serial_number
|
self['Erasure {} {} Size (MB)'.format(ctype, i)] = none2str(component.size)
|
||||||
self['Erasure {} {} Size (MB)'.format(
|
self['Erasure {} {} Software'.format(ctype, i)] = erasure.document.software
|
||||||
ctype, i)] = none2str(component.size)
|
|
||||||
self['Erasure {} {} Software'.format(
|
|
||||||
ctype, i)] = erasure.document.software
|
|
||||||
self['Erasure {} {} Result'.format(ctype, i)] = get_result(erasure)
|
self['Erasure {} {} Result'.format(ctype, i)] = get_result(erasure)
|
||||||
self['Erasure {} {} Certificate URL'.format(
|
self['Erasure {} {} Certificate URL'.format(ctype, i)] = (
|
||||||
ctype, i)] = erasure.document.url and erasure.document.url.to_text() or ''
|
erasure.document.url and erasure.document.url.to_text() or ''
|
||||||
|
)
|
||||||
self['Erasure {} {} Type'.format(ctype, i)] = ''
|
self['Erasure {} {} Type'.format(ctype, i)] = ''
|
||||||
self['Erasure {} {} Method'.format(ctype, i)] = ''
|
self['Erasure {} {} Method'.format(ctype, i)] = ''
|
||||||
self['Erasure {} {} Elapsed (hours)'.format(ctype, i)] = ''
|
self['Erasure {} {} Elapsed (hours)'.format(ctype, i)] = ''
|
||||||
|
@ -291,10 +295,8 @@ class DeviceRow(OrderedDict):
|
||||||
else:
|
else:
|
||||||
self['Erasure {} {}'.format(ctype, i)] = none2str(component.hid)
|
self['Erasure {} {}'.format(ctype, i)] = none2str(component.hid)
|
||||||
serial_number = none2str(component.serial_number)
|
serial_number = none2str(component.serial_number)
|
||||||
self['Erasure {} {} Serial Number'.format(
|
self['Erasure {} {} Serial Number'.format(ctype, i)] = serial_number
|
||||||
ctype, i)] = serial_number
|
self['Erasure {} {} Size (MB)'.format(ctype, i)] = none2str(component.size)
|
||||||
self['Erasure {} {} Size (MB)'.format(
|
|
||||||
ctype, i)] = none2str(component.size)
|
|
||||||
self['Erasure {} {} Software'.format(ctype, i)] = software
|
self['Erasure {} {} Software'.format(ctype, i)] = software
|
||||||
|
|
||||||
result = get_result(erasure)
|
result = get_result(erasure)
|
||||||
|
@ -302,20 +304,16 @@ class DeviceRow(OrderedDict):
|
||||||
self['Erasure {} {} Certificate URL'.format(ctype, i)] = ''
|
self['Erasure {} {} Certificate URL'.format(ctype, i)] = ''
|
||||||
self['Erasure {} {} Type'.format(ctype, i)] = erasure.type
|
self['Erasure {} {} Type'.format(ctype, i)] = erasure.type
|
||||||
self['Erasure {} {} Method'.format(ctype, i)] = erasure.method
|
self['Erasure {} {} Method'.format(ctype, i)] = erasure.method
|
||||||
self['Erasure {} {} Elapsed (hours)'.format(
|
self['Erasure {} {} Elapsed (hours)'.format(ctype, i)] = format(
|
||||||
ctype, i)] = format(erasure.elapsed)
|
erasure.elapsed
|
||||||
self['Erasure {} {} Date'.format(
|
)
|
||||||
ctype, i)] = format(erasure.created)
|
self['Erasure {} {} Date'.format(ctype, i)] = format(erasure.created)
|
||||||
steps = ','.join((format(x) for x in erasure.steps))
|
steps = ','.join((format(x) for x in erasure.steps))
|
||||||
self['Erasure {} {} Steps'.format(ctype, i)] = steps
|
self['Erasure {} {} Steps'.format(ctype, i)] = steps
|
||||||
steps_start_time = ','.join(
|
steps_start_time = ','.join((format(x.start_time) for x in erasure.steps))
|
||||||
(format(x.start_time) for x in erasure.steps))
|
self['Erasure {} {} Steps Start Time'.format(ctype, i)] = steps_start_time
|
||||||
self['Erasure {} {} Steps Start Time'.format(
|
steps_end_time = ','.join((format(x.end_time) for x in erasure.steps))
|
||||||
ctype, i)] = steps_start_time
|
self['Erasure {} {} Steps End Time'.format(ctype, i)] = steps_end_time
|
||||||
steps_end_time = ','.join((format(x.end_time)
|
|
||||||
for x in erasure.steps))
|
|
||||||
self['Erasure {} {} Steps End Time'.format(
|
|
||||||
ctype, i)] = steps_end_time
|
|
||||||
|
|
||||||
benchmark = get_action(component, 'BenchmarkDataStorage')
|
benchmark = get_action(component, 'BenchmarkDataStorage')
|
||||||
if not benchmark:
|
if not benchmark:
|
||||||
|
@ -323,9 +321,11 @@ class DeviceRow(OrderedDict):
|
||||||
self['Benchmark {} {} Writing speed (MB/s)'.format(ctype, i)] = ''
|
self['Benchmark {} {} Writing speed (MB/s)'.format(ctype, i)] = ''
|
||||||
else:
|
else:
|
||||||
self['Benchmark {} {} Read Speed (MB/s)'.format(ctype, i)] = none2str(
|
self['Benchmark {} {} Read Speed (MB/s)'.format(ctype, i)] = none2str(
|
||||||
benchmark.read_speed)
|
benchmark.read_speed
|
||||||
|
)
|
||||||
self['Benchmark {} {} Writing speed (MB/s)'.format(ctype, i)] = none2str(
|
self['Benchmark {} {} Writing speed (MB/s)'.format(ctype, i)] = none2str(
|
||||||
benchmark.write_speed)
|
benchmark.write_speed
|
||||||
|
)
|
||||||
|
|
||||||
test_storage = get_action(component, 'TestDataStorage')
|
test_storage = get_action(component, 'TestDataStorage')
|
||||||
if not test_storage:
|
if not test_storage:
|
||||||
|
@ -339,14 +339,16 @@ class DeviceRow(OrderedDict):
|
||||||
|
|
||||||
self['Test {} {} Software'.format(ctype, i)] = software
|
self['Test {} {} Software'.format(ctype, i)] = software
|
||||||
self['Test {} {} Type'.format(ctype, i)] = test_storage.length.value
|
self['Test {} {} Type'.format(ctype, i)] = test_storage.length.value
|
||||||
self['Test {} {} Result'.format(ctype, i)] = get_result(
|
self['Test {} {} Result'.format(ctype, i)] = get_result(test_storage)
|
||||||
test_storage)
|
|
||||||
self['Test {} {} Power cycle count'.format(ctype, i)] = none2str(
|
self['Test {} {} Power cycle count'.format(ctype, i)] = none2str(
|
||||||
test_storage.power_cycle_count)
|
test_storage.power_cycle_count
|
||||||
|
)
|
||||||
self['Test {} {} Lifetime (days)'.format(ctype, i)] = none2str(
|
self['Test {} {} Lifetime (days)'.format(ctype, i)] = none2str(
|
||||||
test_storage.lifetime)
|
test_storage.lifetime
|
||||||
|
)
|
||||||
self['Test {} {} Power on hours'.format(ctype, i)] = none2str(
|
self['Test {} {} Power on hours'.format(ctype, i)] = none2str(
|
||||||
test_storage.power_on_hours)
|
test_storage.power_on_hours
|
||||||
|
)
|
||||||
|
|
||||||
def get_graphic_card(self, ctype, i, component):
|
def get_graphic_card(self, ctype, i, component):
|
||||||
"""Particular fields for component GraphicCard."""
|
"""Particular fields for component GraphicCard."""
|
||||||
|
@ -400,39 +402,36 @@ class StockRow(OrderedDict):
|
||||||
|
|
||||||
|
|
||||||
def get_result(erasure):
|
def get_result(erasure):
|
||||||
""" For the csv is necessary simplify the message of results """
|
"""For the csv is necessary simplify the message of results"""
|
||||||
if hasattr(erasure, 'type') and erasure.type == 'DataWipe':
|
if hasattr(erasure, 'type') and erasure.type == 'DataWipe':
|
||||||
if erasure.document.success:
|
if erasure.document.success:
|
||||||
return 'Success'
|
return 'Success'
|
||||||
return 'Failure'
|
return 'Failure'
|
||||||
|
|
||||||
|
|
||||||
type_of_results = {
|
type_of_results = {
|
||||||
Severity.Error: 'Failure',
|
Severity.Error: 'Failure',
|
||||||
Severity.Warning: 'Success with Warnings',
|
Severity.Warning: 'Success with Warnings',
|
||||||
Severity.Notice: 'Success',
|
Severity.Notice: 'Success',
|
||||||
Severity.Info: 'Success'
|
Severity.Info: 'Success',
|
||||||
}
|
}
|
||||||
return type_of_results[erasure.severity]
|
return type_of_results[erasure.severity]
|
||||||
|
|
||||||
|
|
||||||
def none2str(string):
|
def none2str(string):
|
||||||
""" convert none to empty str """
|
"""convert none to empty str"""
|
||||||
if string is None:
|
if string is None:
|
||||||
return ''
|
return ''
|
||||||
return format(string)
|
return format(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_action(component, action):
|
def get_action(component, action):
|
||||||
""" Filter one action from a component or return None """
|
"""Filter one action from a component or return None"""
|
||||||
result = [a for a in component.actions if a.type == action]
|
result = [a for a in component.actions if a.type == action]
|
||||||
return result[-1] if result else None
|
return result[-1] if result else None
|
||||||
|
|
||||||
|
|
||||||
class ActionRow(OrderedDict):
|
class ActionRow(OrderedDict):
|
||||||
|
def __init__(self, allocate):
|
||||||
def __init__(self, allocate):
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
# General information about allocates, deallocate and lives
|
# General information about allocates, deallocate and lives
|
||||||
self['DHID'] = allocate['devicehubID']
|
self['DHID'] = allocate['devicehubID']
|
||||||
|
@ -459,7 +458,6 @@ class ActionRow(OrderedDict):
|
||||||
|
|
||||||
|
|
||||||
class InternalStatsRow(OrderedDict):
|
class InternalStatsRow(OrderedDict):
|
||||||
|
|
||||||
def __init__(self, user, create, actions):
|
def __init__(self, user, create, actions):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
# General information about all internal stats
|
# General information about all internal stats
|
||||||
|
@ -488,13 +486,7 @@ class InternalStatsRow(OrderedDict):
|
||||||
|
|
||||||
def count_actions(self):
|
def count_actions(self):
|
||||||
for ac in self.actions:
|
for ac in self.actions:
|
||||||
self.is_snapshot(
|
self.is_snapshot(self.is_deallocate(self.is_live(self.is_allocate(ac))))
|
||||||
self.is_deallocate(
|
|
||||||
self.is_live(
|
|
||||||
self.is_allocate(ac)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def is_allocate(self, ac):
|
def is_allocate(self, ac):
|
||||||
if ac.type == 'Allocate':
|
if ac.type == 'Allocate':
|
||||||
|
@ -531,9 +523,18 @@ class InternalStatsRow(OrderedDict):
|
||||||
self['Snapshot (Registers)'] += 1
|
self['Snapshot (Registers)'] += 1
|
||||||
|
|
||||||
def quarter(self, month):
|
def quarter(self, month):
|
||||||
q = {1: 'Q1', 2: 'Q1', 3: 'Q1',
|
q = {
|
||||||
4: 'Q2', 5: 'Q2', 6: 'Q2',
|
1: 'Q1',
|
||||||
7: 'Q3', 8: 'Q3', 9: 'Q3',
|
2: 'Q1',
|
||||||
10: 'Q4', 11: 'Q4', 12: 'Q4',
|
3: 'Q1',
|
||||||
}
|
4: 'Q2',
|
||||||
|
5: 'Q2',
|
||||||
|
6: 'Q2',
|
||||||
|
7: 'Q3',
|
||||||
|
8: 'Q3',
|
||||||
|
9: 'Q3',
|
||||||
|
10: 'Q4',
|
||||||
|
11: 'Q4',
|
||||||
|
12: 'Q4',
|
||||||
|
}
|
||||||
return q[int(month)]
|
return q[int(month)]
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
868e59911be73a941938644143d81f21a2fdbe82ea0841493c2d9fc04701e058334af5fecd69c1a1525ebd5c8c17ac3f49d8ecc53bbfc8a018f169be48fe79d6 USODY_2022.5.2-beta.iso
|
|
@ -95,7 +95,7 @@
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item d-flex align-items-center" href="{{ url_for('workbench.settings') }}">
|
<a class="dropdown-item d-flex align-items-center" href="{{ url_for('workbench.settings') }}">
|
||||||
<i class="bi bi-tools"></i>
|
<i class="bi bi-tools"></i>
|
||||||
<span>Workbench Settings</span>
|
<span>Workbench</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -29,8 +29,10 @@
|
||||||
<th scope="col">Snapshot id</th>
|
<th scope="col">Snapshot id</th>
|
||||||
<th scope="col">Version</th>
|
<th scope="col">Version</th>
|
||||||
<th scope="col">DHID</th>
|
<th scope="col">DHID</th>
|
||||||
|
<th scope="col">System UUID</th>
|
||||||
<th scope="col">Status</th>
|
<th scope="col">Status</th>
|
||||||
<th scope="col" data-type="date" data-format="DD-MM-YYYY">Time</th>
|
<th scope="col" data-type="date" data-format="DD-MM-YYYY">Time</th>
|
||||||
|
<th scope="col"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -60,10 +62,20 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ snap.system_uuid }}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ snap.status }}
|
{{ snap.status }}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ snap.created.strftime('%H:%M %d-%m-%Y') }}</td>
|
<td>{{ snap.created.strftime('%H:%M %d-%m-%Y') }}</td>
|
||||||
|
<td>
|
||||||
|
{% if snap.snapshot_uuid %}
|
||||||
|
<a href="{{ url_for('inventory.export', export_id='snapshot') }}?id={{ snap.snapshot_uuid }}" target="_blank">
|
||||||
|
<i class="bi bi-box-arrow-up-right"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -28,6 +28,20 @@
|
||||||
<p class="small">Download the settings only for register devices.</p>
|
<p class="small">Download the settings only for register devices.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if iso %}
|
||||||
|
<div class="row pt-3">
|
||||||
|
<div class="col-5">
|
||||||
|
<a href="/static/iso/{{ iso }}" class="btn btn-primary">{{ iso }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<p class="small">Download ISO workbench and burn it one one usb stick.</p>
|
||||||
|
<p class="small">
|
||||||
|
Download Checksum: <a style="color: #993365;" href="/static/iso/SHA512SUMS">SHA512SUMS</a> |
|
||||||
|
<a style="color: #993365;" href="/static/iso/SHA512SUMS.sign">Signature</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
|
@ -22,6 +24,7 @@ class SettingsView(GenericMixin):
|
||||||
|
|
||||||
def dispatch_request(self):
|
def dispatch_request(self):
|
||||||
self.get_context()
|
self.get_context()
|
||||||
|
self.get_iso()
|
||||||
self.context.update(
|
self.context.update(
|
||||||
{
|
{
|
||||||
'page_title': self.page_title,
|
'page_title': self.page_title,
|
||||||
|
@ -34,6 +37,19 @@ class SettingsView(GenericMixin):
|
||||||
|
|
||||||
return flask.render_template(self.template_name, **self.context)
|
return flask.render_template(self.template_name, **self.context)
|
||||||
|
|
||||||
|
def get_iso(self):
|
||||||
|
path = Path(__file__).parent.parent
|
||||||
|
files = [
|
||||||
|
f for f in os.listdir(f'{path}/static/iso/') if f[-3:].lower() == 'iso'
|
||||||
|
]
|
||||||
|
|
||||||
|
self.context['iso'] = ''
|
||||||
|
self.context['iso_sha'] = ''
|
||||||
|
|
||||||
|
if files:
|
||||||
|
self.context['iso'] = files[0]
|
||||||
|
self.context['iso_sha'] = 'aaa'
|
||||||
|
|
||||||
def download(self):
|
def download(self):
|
||||||
url = "https://{}/api/inventory/".format(app.config['HOST'])
|
url = "https://{}/api/inventory/".format(app.config['HOST'])
|
||||||
self.wbContext = {
|
self.wbContext = {
|
||||||
|
|
|
@ -42,6 +42,7 @@ sortedcontainers==2.1.0
|
||||||
tqdm==4.32.2
|
tqdm==4.32.2
|
||||||
python-decouple==3.3
|
python-decouple==3.3
|
||||||
python-dotenv==0.14.0
|
python-dotenv==0.14.0
|
||||||
|
selenium==4.1.5
|
||||||
pyjwt==2.4.0
|
pyjwt==2.4.0
|
||||||
pint==0.9
|
pint==0.9
|
||||||
py-dmidecode==0.1.0
|
py-dmidecode==0.1.0
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def get_old_smbios_version(snapshot):
|
||||||
|
capabilities = snapshot.get('debug', {}).get('lshw', {}).get('capabilities', {})
|
||||||
|
for x in capabilities.values():
|
||||||
|
if "SMBIOS version" in x:
|
||||||
|
e = x.split("SMBIOS version ")[1].split(".")
|
||||||
|
if int(e[0]) < 3 and int(e[1]) < 6:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_uuid(snapshot):
|
||||||
|
|
||||||
|
return (
|
||||||
|
snapshot.get('debug', {}).get('lshw', {}).get('configuration', {}).get('uuid')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
_file = sys.argv[1]
|
||||||
|
with open(_file) as file_snapshot:
|
||||||
|
snapshot = json.loads(file_snapshot.read())
|
||||||
|
|
||||||
|
if get_old_smbios_version(snapshot):
|
||||||
|
return
|
||||||
|
|
||||||
|
system_uuid = get_uuid(snapshot)
|
||||||
|
if system_uuid:
|
||||||
|
print("{};{}".format(system_uuid, snapshot['uuid']))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1 @@
|
||||||
|
for i in `ls ../snapshots/*/*.json`; do python examples/extract_uuid.py $i; done > system_uuids.csv
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,172 @@
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"size": 10.030411318500475,
|
||||||
|
"technology": "LCD",
|
||||||
|
"resolutionWidth": 1024,
|
||||||
|
"model": "AUO LCD Monitor",
|
||||||
|
"actions": [],
|
||||||
|
"type": "Display",
|
||||||
|
"refreshRate": 60,
|
||||||
|
"productionDate": "2009-01-04T00:00:00",
|
||||||
|
"manufacturer": "AUO \"AUO\"",
|
||||||
|
"serialNumber": null,
|
||||||
|
"resolutionHeight": 600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"generation": null,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"rate": 164.4981,
|
||||||
|
"type": "BenchmarkProcessorSysbench",
|
||||||
|
"elapsed": 165
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rate": 6650.48,
|
||||||
|
"type": "BenchmarkProcessor",
|
||||||
|
"elapsed": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"speed": 1.0,
|
||||||
|
"cores": 1,
|
||||||
|
"model": "Intel Atom CPU N450 @ 1.66GHz",
|
||||||
|
"address": 64,
|
||||||
|
"type": "Processor",
|
||||||
|
"threads": 2,
|
||||||
|
"manufacturer": "Intel Corp.",
|
||||||
|
"serialNumber": null,
|
||||||
|
"brand": "Atom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"memory": null,
|
||||||
|
"model": "Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller",
|
||||||
|
"actions": [],
|
||||||
|
"type": "GraphicCard",
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"serialNumber": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SoundCard",
|
||||||
|
"actions": [],
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"serialNumber": null,
|
||||||
|
"model": "NM10/ICH7 Family High Definition Audio Controller"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SoundCard",
|
||||||
|
"actions": [],
|
||||||
|
"manufacturer": "XPA970VW0",
|
||||||
|
"serialNumber": null,
|
||||||
|
"model": "1.3M WebCam"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 1024.0,
|
||||||
|
"actions": [],
|
||||||
|
"format": "SODIMM",
|
||||||
|
"model": "48594D503131325336344350362D53362020",
|
||||||
|
"interface": "DDR2",
|
||||||
|
"type": "RamModule",
|
||||||
|
"manufacturer": "Hynix Semiconductor",
|
||||||
|
"serialNumber": "4F43487B",
|
||||||
|
"speed": 667.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 160041.88569599998,
|
||||||
|
"variant": "1A01",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "EraseBasic",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "StepRandom",
|
||||||
|
"endTime": "2019-10-23T08:35:31.400587+00:00",
|
||||||
|
"severity": "Info",
|
||||||
|
"startTime": "2019-10-23T07:49:54.410830+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"endTime": "2019-10-23T08:35:31.400988+00:00",
|
||||||
|
"severity": "Info",
|
||||||
|
"startTime": "2019-10-23T07:49:54.410193+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 22,
|
||||||
|
"writeSpeed": 17.3,
|
||||||
|
"readSpeed": 41.6,
|
||||||
|
"type": "BenchmarkDataStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Completed without error",
|
||||||
|
"reallocatedSectorCount": 0,
|
||||||
|
"currentPendingSectorCount": 0,
|
||||||
|
"assessment": true,
|
||||||
|
"severity": "Info",
|
||||||
|
"offlineUncorrectable": 0,
|
||||||
|
"lifetime": 4692,
|
||||||
|
"type": "TestDataStorage",
|
||||||
|
"length": "Short",
|
||||||
|
"elapsed": 118,
|
||||||
|
"powerCycleCount": 5293
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"model": "WDC WD1600BEVT-2",
|
||||||
|
"interface": "ATA",
|
||||||
|
"type": "HardDrive",
|
||||||
|
"manufacturer": "Western Digital",
|
||||||
|
"serialNumber": "WD-WX11A80W7430"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"variant": "c1",
|
||||||
|
"actions": [],
|
||||||
|
"speed": 100.0,
|
||||||
|
"model": "AR8152 v1.1 Fast Ethernet",
|
||||||
|
"wireless": false,
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"serialNumber": "88:ae:1d:a6:f3:d0",
|
||||||
|
"manufacturer": "Qualcomm Atheros"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ramMaxSize": 4,
|
||||||
|
"slots": 1,
|
||||||
|
"model": "AOHAPPY",
|
||||||
|
"pcmcia": 0,
|
||||||
|
"type": "Motherboard",
|
||||||
|
"version": "V3.05(DDR2)",
|
||||||
|
"ramSlots": 2,
|
||||||
|
"serialNumber": "Base Board Serial Number",
|
||||||
|
"manufacturer": "Acer",
|
||||||
|
"serial": 1,
|
||||||
|
"actions": [],
|
||||||
|
"biosDate": "2010-08-12T00:00:00",
|
||||||
|
"firewire": 0,
|
||||||
|
"usb": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"software": "Workbench",
|
||||||
|
"device": {
|
||||||
|
"sku": null,
|
||||||
|
"chassis": "Netbook",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "StressTest",
|
||||||
|
"elapsed": 60,
|
||||||
|
"severity": "Info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rate": 19.2726,
|
||||||
|
"type": "BenchmarkRamSysbench",
|
||||||
|
"elapsed": 19
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"model": "AOHAPPY",
|
||||||
|
"type": "Laptop",
|
||||||
|
"version": "V3.05",
|
||||||
|
"manufacturer": "Acer",
|
||||||
|
"serialNumber": "LUSEA0D010038879A01601"
|
||||||
|
},
|
||||||
|
"uuid": "0973fda0-589a-11eb-ae93-0242ac130002",
|
||||||
|
"type": "Snapshot",
|
||||||
|
"version": "11.0b9",
|
||||||
|
"endTime": "2019-10-23T07:43:13.625104+00:00",
|
||||||
|
"elapsed": 3138,
|
||||||
|
"closed": true
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"size": 10.030411318500475,
|
||||||
|
"technology": "LCD",
|
||||||
|
"resolutionWidth": 1024,
|
||||||
|
"model": "AUO LCD Monitor",
|
||||||
|
"actions": [],
|
||||||
|
"type": "Display",
|
||||||
|
"refreshRate": 60,
|
||||||
|
"productionDate": "2009-01-04T00:00:00",
|
||||||
|
"manufacturer": "AUO \"AUO\"",
|
||||||
|
"serialNumber": null,
|
||||||
|
"resolutionHeight": 600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"generation": null,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"rate": 164.4981,
|
||||||
|
"type": "BenchmarkProcessorSysbench",
|
||||||
|
"elapsed": 165
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rate": 6650.48,
|
||||||
|
"type": "BenchmarkProcessor",
|
||||||
|
"elapsed": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"speed": 1.0,
|
||||||
|
"cores": 1,
|
||||||
|
"model": "Intel Atom CPU N450 @ 1.66GHz",
|
||||||
|
"address": 64,
|
||||||
|
"type": "Processor",
|
||||||
|
"threads": 2,
|
||||||
|
"manufacturer": "Intel Corp.",
|
||||||
|
"serialNumber": null,
|
||||||
|
"brand": "Atom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"memory": null,
|
||||||
|
"model": "Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller",
|
||||||
|
"actions": [],
|
||||||
|
"type": "GraphicCard",
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"serialNumber": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SoundCard",
|
||||||
|
"actions": [],
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"serialNumber": null,
|
||||||
|
"model": "NM10/ICH7 Family High Definition Audio Controller"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SoundCard",
|
||||||
|
"actions": [],
|
||||||
|
"manufacturer": "XPA970VW0",
|
||||||
|
"serialNumber": null,
|
||||||
|
"model": "1.3M WebCam"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 1024.0,
|
||||||
|
"actions": [],
|
||||||
|
"format": "SODIMM",
|
||||||
|
"model": "48594D503131325336344350362D53362020",
|
||||||
|
"interface": "DDR2",
|
||||||
|
"type": "RamModule",
|
||||||
|
"manufacturer": "Hynix Semiconductor",
|
||||||
|
"serialNumber": "4F43487B",
|
||||||
|
"speed": 667.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 160041.88569599998,
|
||||||
|
"variant": "1A01",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "EraseBasic",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "StepRandom",
|
||||||
|
"endTime": "2019-10-23T08:35:31.400587+00:00",
|
||||||
|
"severity": "Info",
|
||||||
|
"startTime": "2019-10-23T07:49:54.410830+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"endTime": "2019-10-23T08:35:31.400988+00:00",
|
||||||
|
"severity": "Info",
|
||||||
|
"startTime": "2019-10-23T07:49:54.410193+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 22,
|
||||||
|
"writeSpeed": 17.3,
|
||||||
|
"readSpeed": 41.6,
|
||||||
|
"type": "BenchmarkDataStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Completed without error",
|
||||||
|
"reallocatedSectorCount": 0,
|
||||||
|
"currentPendingSectorCount": 0,
|
||||||
|
"assessment": true,
|
||||||
|
"severity": "Info",
|
||||||
|
"offlineUncorrectable": 0,
|
||||||
|
"lifetime": 4692,
|
||||||
|
"type": "TestDataStorage",
|
||||||
|
"length": "Short",
|
||||||
|
"elapsed": 118,
|
||||||
|
"powerCycleCount": 5293
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"model": "WDC WD1600BEVT-2",
|
||||||
|
"interface": "ATA",
|
||||||
|
"type": "HardDrive",
|
||||||
|
"manufacturer": "Western Digital",
|
||||||
|
"serialNumber": "WD-WX11A80W7430"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"variant": "c1",
|
||||||
|
"actions": [],
|
||||||
|
"speed": 100.0,
|
||||||
|
"model": "AR8152 v1.1 Fast Ethernet",
|
||||||
|
"wireless": false,
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"serialNumber": "88:ae:1d:a6:f3:d0",
|
||||||
|
"manufacturer": "Qualcomm Atheros"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ramMaxSize": 4,
|
||||||
|
"slots": 1,
|
||||||
|
"model": "AOHAPPY",
|
||||||
|
"pcmcia": 0,
|
||||||
|
"type": "Motherboard",
|
||||||
|
"version": "V3.05(DDR2)",
|
||||||
|
"ramSlots": 2,
|
||||||
|
"serialNumber": "Base Board Serial Number",
|
||||||
|
"manufacturer": "Acer",
|
||||||
|
"serial": 1,
|
||||||
|
"actions": [],
|
||||||
|
"biosDate": "2010-08-12T00:00:00",
|
||||||
|
"firewire": 0,
|
||||||
|
"usb": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"software": "Workbench",
|
||||||
|
"device": {
|
||||||
|
"sku": null,
|
||||||
|
"chassis": "Netbook",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "StressTest",
|
||||||
|
"elapsed": 60,
|
||||||
|
"severity": "Info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rate": 19.2726,
|
||||||
|
"type": "BenchmarkRamSysbench",
|
||||||
|
"elapsed": 19
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"model": "AOHAPPY",
|
||||||
|
"type": "Laptop",
|
||||||
|
"version": "V3.05",
|
||||||
|
"manufacturer": "Acer",
|
||||||
|
"serialNumber": "LUSEA0D010038879A01601"
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"lshw": {
|
||||||
|
"capabilities": {
|
||||||
|
"dmi-2.5": "DMI version 2.5",
|
||||||
|
"smbios-2.5": "SMBIOS version 2.5",
|
||||||
|
"smp": "Symmetric Multi-Processing",
|
||||||
|
"vsyscall32": "32-bit processes"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"boot": "normal",
|
||||||
|
"chassis": "notebook",
|
||||||
|
"family": "Intel_Mobile",
|
||||||
|
"sku": "NetTopSku",
|
||||||
|
"uuid": "364ee69c-9c82-9cb1-2111-88ae1da6f3d0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid": "0973fda0-589a-11eb-ae93-0242ac130002",
|
||||||
|
"type": "Snapshot",
|
||||||
|
"version": "11.0b9",
|
||||||
|
"endTime": "2019-10-23T07:43:13.625104+00:00",
|
||||||
|
"elapsed": 3138,
|
||||||
|
"closed": true
|
||||||
|
}
|
|
@ -130,7 +130,6 @@ def test_physical_properties():
|
||||||
'model': 'foo',
|
'model': 'foo',
|
||||||
'receiver_id': None,
|
'receiver_id': None,
|
||||||
'serial_number': 'foo-bar',
|
'serial_number': 'foo-bar',
|
||||||
'uuid': None,
|
|
||||||
'transfer_state': TransferState.Initial
|
'transfer_state': TransferState.Initial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1385,3 +1385,18 @@ def test_export_lots(user3: UserClientFlask):
|
||||||
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
assert fixture_csv[1][1:] == export_csv[1][1:], 'Computer information are not equal'
|
assert fixture_csv[1][1:] == export_csv[1][1:], 'Computer information are not equal'
|
||||||
UUID(export_csv[1][0])
|
UUID(export_csv[1][0])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_export_snapshot_json(user3: UserClientFlask):
|
||||||
|
file_name = 'real-eee-1001pxd.snapshot.12.json'
|
||||||
|
snap = create_device(user3, file_name)
|
||||||
|
|
||||||
|
snapshot = conftest.yaml2json(file_name.split(".json")[0])
|
||||||
|
snapshot = json.dumps(snapshot)
|
||||||
|
|
||||||
|
uri = "/inventory/export/snapshot/?id={}".format(snap.uuid)
|
||||||
|
body, status = user3.get(uri)
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert body == snapshot
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
# Generated by Selenium IDE
|
||||||
|
import time
|
||||||
|
|
||||||
|
from selenium import webdriver
|
||||||
|
from selenium.webdriver.common.action_chains import ActionChains
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.firefox.options import Options
|
||||||
|
|
||||||
|
|
||||||
|
class TestSelenium:
|
||||||
|
def setup_method(self, method):
|
||||||
|
options = Options()
|
||||||
|
options.add_argument("--headless")
|
||||||
|
self.driver = webdriver.Firefox(
|
||||||
|
options=options, executable_path=r'./bin/geckodriver'
|
||||||
|
)
|
||||||
|
self.vars = {}
|
||||||
|
|
||||||
|
def teardown_method(self, method):
|
||||||
|
self.driver.quit()
|
||||||
|
|
||||||
|
def test_selenium(self):
|
||||||
|
# setup
|
||||||
|
self.driver.get("http://localhost:5000/login/")
|
||||||
|
self.driver.set_window_size(1920, 1063)
|
||||||
|
|
||||||
|
# login
|
||||||
|
self.driver.find_element(By.ID, "yourEmail").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
self.driver.find_element(By.ID, "yourPassword").send_keys("1234")
|
||||||
|
self.driver.find_element(By.ID, "yourEmail").send_keys("user@dhub.com")
|
||||||
|
self.driver.find_element(By.CSS_SELECTOR, ".btn").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
|
||||||
|
# select the first lot and get the ID of it
|
||||||
|
self.driver.find_element(By.LINK_TEXT, "Temporary Lots").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
self.driver.find_element(
|
||||||
|
By.CSS_SELECTOR, "#temporal-lots-nav > li:nth-child(2) span"
|
||||||
|
).click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
lot_id = self.driver.current_url.split("/")[5]
|
||||||
|
|
||||||
|
# go to unassigned
|
||||||
|
self.driver.find_element(By.CSS_SELECTOR, ".nav-item:nth-child(5) span").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
|
||||||
|
# select the first device
|
||||||
|
self.driver.find_element(
|
||||||
|
By.CSS_SELECTOR, "tr:nth-child(1) .deviceSelect"
|
||||||
|
).click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
|
||||||
|
# add to new selenium_lot
|
||||||
|
self.driver.find_element(By.ID, "btnLots").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
self.driver.find_element(By.ID, lot_id).click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
self.driver.find_element(By.ID, "ApplyDeviceLots").click()
|
||||||
|
time.sleep(3)
|
||||||
|
element = self.driver.find_element(By.ID, "ApplyDeviceLots")
|
||||||
|
time.sleep(3)
|
||||||
|
actions = ActionChains(self.driver)
|
||||||
|
time.sleep(3)
|
||||||
|
actions.move_to_element(element).perform()
|
||||||
|
time.sleep(3)
|
||||||
|
element = self.driver.find_element(By.CSS_SELECTOR, "body")
|
||||||
|
time.sleep(3)
|
||||||
|
actions = ActionChains(self.driver)
|
||||||
|
time.sleep(3)
|
||||||
|
# actions.move_to_element(element, 0, 0).perform()
|
||||||
|
actions.move_to_element(element).perform()
|
||||||
|
time.sleep(3)
|
||||||
|
self.driver.find_element(By.ID, "SaveAllActions").click()
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# go to selenium lot
|
||||||
|
self.driver.find_element(By.LINK_TEXT, "Temporary Lots").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
self.driver.find_element(
|
||||||
|
By.CSS_SELECTOR, "#temporal-lots-nav > li:nth-child(2) span"
|
||||||
|
).click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
|
||||||
|
# select the first device
|
||||||
|
self.driver.find_element(By.CSS_SELECTOR, ".deviceSelect").click()
|
||||||
|
|
||||||
|
# remove to new selenium_lot
|
||||||
|
self.driver.find_element(By.ID, "btnLots").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
self.driver.find_element(By.ID, lot_id).click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
self.driver.find_element(By.ID, "ApplyDeviceLots").click()
|
||||||
|
time.sleep(3)
|
||||||
|
self.driver.find_element(By.ID, "SaveAllActions").click()
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
self.driver.find_element(By.CSS_SELECTOR, ".nav-item:nth-child(5) span").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
|
||||||
|
# logout
|
||||||
|
self.driver.find_element(By.CSS_SELECTOR, ".d-md-block:nth-child(2)").click()
|
||||||
|
self.driver.implicitly_wait(3)
|
||||||
|
self.driver.find_element(By.LINK_TEXT, "Sign Out").click()
|
|
@ -190,10 +190,12 @@ def test_snapshot_power_on_hours(user: UserClient):
|
||||||
)
|
)
|
||||||
|
|
||||||
errors = SnapshotsLog.query.filter().all()
|
errors = SnapshotsLog.query.filter().all()
|
||||||
snap_log = errors[0]
|
snap_log = errors[1]
|
||||||
assert str(snap_log.snapshot.uuid) == snap['uuid']
|
assert len(errors) == 2
|
||||||
assert len(errors) == 1
|
assert str(errors[0].snapshot_uuid) == snap['uuid']
|
||||||
assert errors[0].description == 'Ok'
|
assert str(errors[1].snapshot.uuid) == snap['uuid']
|
||||||
|
assert errors[0].description == 'There is not uuid'
|
||||||
|
assert errors[1].description == 'Ok'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -784,11 +786,13 @@ def test_backup_snapshot_with_errors(app: Devicehub, user: UserClient):
|
||||||
response = user.post(res=Snapshot, data=json_encode(snapshot_no_hid))
|
response = user.post(res=Snapshot, data=json_encode(snapshot_no_hid))
|
||||||
|
|
||||||
errors = SnapshotsLog.query.filter().all()
|
errors = SnapshotsLog.query.filter().all()
|
||||||
snap_log = errors[0]
|
snap_log = errors[1]
|
||||||
assert snap_log.description == "'BenchmarkProcessorr'"
|
assert snap_log.description == "'BenchmarkProcessorr'"
|
||||||
|
assert errors[0].description == 'There is not uuid'
|
||||||
assert snap_log.version == "11.0b9"
|
assert snap_log.version == "11.0b9"
|
||||||
assert str(snap_log.snapshot_uuid) == '9a3e7485-fdd0-47ce-bcc7-65c55226b598'
|
assert str(snap_log.snapshot_uuid) == '9a3e7485-fdd0-47ce-bcc7-65c55226b598'
|
||||||
assert len(errors) == 1
|
assert str(errors[0].snapshot_uuid) == '9a3e7485-fdd0-47ce-bcc7-65c55226b598'
|
||||||
|
assert len(errors) == 2
|
||||||
|
|
||||||
files = [x for x in os.listdir(path_dir_base) if uuid in x]
|
files = [x for x in os.listdir(path_dir_base) if uuid in x]
|
||||||
if files:
|
if files:
|
||||||
|
|
|
@ -0,0 +1,610 @@
|
||||||
|
import json
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask_wtf.csrf import generate_csrf
|
||||||
|
|
||||||
|
from ereuse_devicehub.client import UserClient, UserClientFlask
|
||||||
|
from ereuse_devicehub.resources.action.models import Snapshot
|
||||||
|
from ereuse_devicehub.resources.device.models import Computer
|
||||||
|
from tests import conftest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_form(user3: UserClientFlask):
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
file_name = 'system_uuid1.json'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
snapshot = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
|
||||||
|
db_snapthot = Snapshot.query.one()
|
||||||
|
device = db_snapthot.device
|
||||||
|
assert device.hid == 'laptop-toshiba-satellite_l655-2b335208q-00:26:6c:ae:ee:78'
|
||||||
|
assert str(device.system_uuid) == 'f0dc6a7f-c23f-e011-b5d0-00266caeee78'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_api(user: UserClient):
|
||||||
|
file_name = 'system_uuid1.json'
|
||||||
|
snapshot_11 = conftest.file_json(file_name)
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
|
||||||
|
db_snapthot = Snapshot.query.one()
|
||||||
|
device = db_snapthot.device
|
||||||
|
assert device.hid == 'laptop-toshiba-satellite_l655-2b335208q-00:26:6c:ae:ee:78'
|
||||||
|
assert str(device.system_uuid) == 'f0dc6a7f-c23f-e011-b5d0-00266caeee78'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wbLite_form(user3: UserClientFlask):
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
file_name = 'system_uuid2.json'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
snapshot = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
|
||||||
|
db_snapthot = Snapshot.query.one()
|
||||||
|
device = db_snapthot.device
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wbLite_api(user: UserClient):
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
|
||||||
|
user.post(snapshot_lite, uri="/api/inventory/")
|
||||||
|
|
||||||
|
db_snapthot = Snapshot.query.one()
|
||||||
|
device = db_snapthot.device
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_to_wb11_with_uuid_api(user: UserClient):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
|
||||||
|
db_snapthot = Snapshot.query.one()
|
||||||
|
device = db_snapthot.device
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert device.system_uuid is None
|
||||||
|
|
||||||
|
# insert the same computer with wb11 with hid and with uuid, (new version)
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']}
|
||||||
|
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
snapshot_11['debug']['lshw']['configuration']['uuid']
|
||||||
|
== '364ee69c-9c82-9cb1-2111-88ae1da6f3d0'
|
||||||
|
)
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_with_uuid_to_wb11_api(user: UserClient):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']}
|
||||||
|
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
# insert the same computer with wb11 with hid and with uuid, (new version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
assert 'debug' not in snapshot_11
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_with_uuid_to_wb11_without_hid_api(user: UserClient):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']}
|
||||||
|
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
# insert the same computer with wb11 with hid and with uuid, (new version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
components = [x for x in snapshot_11['components'] if x['type'] != 'NetworkAdapter']
|
||||||
|
snapshot_11['components'] = components
|
||||||
|
snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']}
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_to_wb11_with_uuid_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
file_name = 'system_uuid3.json'
|
||||||
|
snapshot = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
|
||||||
|
db_snapthot = Snapshot.query.one()
|
||||||
|
device = db_snapthot.device
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert device.system_uuid is None
|
||||||
|
|
||||||
|
# insert the same computer with wb11 with hid and with uuid, (new version)
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
snapshot['debug'] = {'lshw': snapshot_lite['data']['lshw']}
|
||||||
|
snapshot['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_with_uuid_to_wb11_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
file_name = 'system_uuid3.json'
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
snapshot = conftest.file_json(file_name)
|
||||||
|
snapshot['debug'] = {'lshw': snapshot_lite['data']['lshw']}
|
||||||
|
snapshot['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
# insert the same computer with wb11 with hid and with uuid, (new version)
|
||||||
|
snapshot = conftest.file_json('system_uuid3.json')
|
||||||
|
assert 'debug' not in snapshot
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_with_uuid_to_wb11_without_hid_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
file_name = 'system_uuid3.json'
|
||||||
|
snapshot_11 = conftest.file_json(file_name)
|
||||||
|
snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']}
|
||||||
|
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_11), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
# insert the same computer with wb11 with hid and with uuid, (new version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
components = [x for x in snapshot_11['components'] if x['type'] != 'NetworkAdapter']
|
||||||
|
snapshot_11['components'] = components
|
||||||
|
snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']}
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_11), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_to_wblite_api(user: UserClient):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert device.system_uuid is None
|
||||||
|
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
user.post(snapshot_lite, uri="/api/inventory/")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wblite_to_wb11_api(user: UserClient):
|
||||||
|
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
user.post(snapshot_lite, uri="/api/inventory/")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_to_wblite_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
file_name = 'system_uuid3.json'
|
||||||
|
snapshot_11 = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_11), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert device.system_uuid is None
|
||||||
|
|
||||||
|
file_name = 'system_uuid2.json'
|
||||||
|
snapshot_lite = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_lite), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wblite_to_wb11_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
file_name = 'system_uuid2.json'
|
||||||
|
snapshot_lite = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_lite), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
file_name = 'system_uuid3.json'
|
||||||
|
snapshot_11 = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_11), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wblite_to_wblite_api(user: UserClient):
|
||||||
|
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
user.post(snapshot_lite, uri="/api/inventory/")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
snapshot_lite['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
user.post(snapshot_lite, uri="/api/inventory/")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wblite_to_wblite_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
file_name = 'system_uuid2.json'
|
||||||
|
snapshot_lite = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_lite), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
file_name = 'system_uuid2.json'
|
||||||
|
snapshot_lite = conftest.file_json(file_name)
|
||||||
|
snapshot_lite['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_lite), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_to_wb11_duplicity_api(user: UserClient):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert device.system_uuid is None
|
||||||
|
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
components = [x for x in snapshot_11['components'] if x['type'] != 'NetworkAdapter']
|
||||||
|
snapshot_11['components'] = components
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
assert Computer.query.count() == 2
|
||||||
|
for c in Computer.query.all():
|
||||||
|
assert 'laptop-acer-aohappy-lusea0d010038879a01601' in c.hid
|
||||||
|
assert c.system_uuid is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_to_wb11_duplicity_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
file_name = 'system_uuid3.json'
|
||||||
|
snapshot_11 = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_11), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert device.system_uuid is None
|
||||||
|
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid3.json')
|
||||||
|
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
|
||||||
|
components = [x for x in snapshot_11['components'] if x['type'] != 'NetworkAdapter']
|
||||||
|
snapshot_11['components'] = components
|
||||||
|
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_11), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 2
|
||||||
|
for c in Computer.query.all():
|
||||||
|
assert 'laptop-acer-aohappy-lusea0d010038879a01601' in c.hid
|
||||||
|
assert c.system_uuid is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_smbios_2_5_api(user: UserClient):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
snapshot_11 = conftest.file_json('system_uuid4.json')
|
||||||
|
user.post(snapshot_11, res=Snapshot)
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert device.system_uuid is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb11_smbios_2_5_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
file_name = 'system_uuid4.json'
|
||||||
|
snapshot_11 = conftest.file_json(file_name)
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_11), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert device.system_uuid is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wblite_smbios_2_5_api(user: UserClient):
|
||||||
|
|
||||||
|
# insert computer with wb11 with hid and without uuid, (old version)
|
||||||
|
snapshot_lite = conftest.file_json('system_uuid2.json')
|
||||||
|
snapshot_lite['data']['lshw']['capabilities']['smbios-3.0'] = 'SMBIOS version 2.5'
|
||||||
|
user.post(snapshot_lite, uri="/api/inventory/")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wblite_smbios_2_5_form(user3: UserClientFlask):
|
||||||
|
|
||||||
|
uri = '/inventory/upload-snapshot/'
|
||||||
|
user3.get(uri)
|
||||||
|
|
||||||
|
file_name = 'system_uuid2.json'
|
||||||
|
snapshot_lite = conftest.file_json(file_name)
|
||||||
|
snapshot_lite['data']['lshw']['capabilities']['smbios-3.0'] = 'SMBIOS version 2.5'
|
||||||
|
b_snapshot = bytes(json.dumps(snapshot_lite), 'utf-8')
|
||||||
|
file_snap = (BytesIO(b_snapshot), file_name)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'snapshot': file_snap,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post(uri, data=data, content_type="multipart/form-data")
|
||||||
|
assert Computer.query.count() == 1
|
||||||
|
device = Computer.query.one()
|
||||||
|
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
|
||||||
|
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
|
Reference in New Issue