First real snapshots :-)

This commit is contained in:
Xavier Bustamante Talavera 2018-07-02 12:52:54 +02:00
parent cd8928e1cd
commit af885c2aec
23 changed files with 732 additions and 121 deletions

View File

@ -75,4 +75,17 @@ Devicehub has many commands that allows you to administrate it. You
can, for example, create a dummy database of devices with ``flask dummy`` can, for example, create a dummy database of devices with ``flask dummy``
or create users with ``flask create-user``. See all the or create users with ``flask create-user``. See all the
available commands by just executing ``flask`` and get more information available commands by just executing ``flask`` and get more information
per command by executing ``flask command --help``. per command by executing ``flask command --help``.
## Understand the software
See the [docs](docs/index.rst) to understand how the software works and
the design principles.
### Use the API
Checkout [Swagger](https://app.swaggerhub.com/apis/ereuse/devicehub/0.2)
to see the schemas and endpoints (we are working in making it
interactive).
Use postman as an example of how to use the API.
[![Run in Postman](https://run.pstmn.io/button.svg)](https://documenter.getpostman.com/view/254251/RWEnmFPs)

View File

@ -5,7 +5,7 @@ from ereuse_devicehub.resources.device import CellphoneDef, ComponentDef, Comput
ComputerMonitorDef, DataStorageDef, DesktopDef, DeviceDef, DisplayDef, GraphicCardDef, \ ComputerMonitorDef, DataStorageDef, DesktopDef, DeviceDef, DisplayDef, GraphicCardDef, \
HardDriveDef, LaptopDef, MobileDef, MonitorDef, MotherboardDef, NetworkAdapterDef, \ HardDriveDef, LaptopDef, MobileDef, MonitorDef, MotherboardDef, NetworkAdapterDef, \
ProcessorDef, RamModuleDef, ServerDef, SmartphoneDef, SolidStateDriveDef, TabletDef, \ ProcessorDef, RamModuleDef, ServerDef, SmartphoneDef, SolidStateDriveDef, TabletDef, \
TelevisionSetDef TelevisionSetDef, SoundCardDef
from ereuse_devicehub.resources.event import AddDef, AggregateRateDef, AppRateDef, \ from ereuse_devicehub.resources.event import AddDef, AggregateRateDef, AppRateDef, \
BenchmarkDataStorageDef, BenchmarkDef, BenchmarkProcessorDef, BenchmarkProcessorSysbenchDef, \ BenchmarkDataStorageDef, BenchmarkDef, BenchmarkProcessorDef, BenchmarkProcessorSysbenchDef, \
BenchmarkRamSysbenchDef, BenchmarkWithRateDef, EraseBasicDef, EraseSectorsDef, EventDef, \ BenchmarkRamSysbenchDef, BenchmarkWithRateDef, EraseBasicDef, EraseSectorsDef, EventDef, \
@ -23,7 +23,8 @@ class DevicehubConfig(Config):
DeviceDef, ComputerDef, DesktopDef, LaptopDef, ServerDef, MonitorDef, TelevisionSetDef, DeviceDef, ComputerDef, DesktopDef, LaptopDef, ServerDef, MonitorDef, TelevisionSetDef,
ComputerMonitorDef, ComponentDef, GraphicCardDef, DataStorageDef, ComputerMonitorDef, ComponentDef, GraphicCardDef, DataStorageDef,
SolidStateDriveDef, MobileDef, DisplayDef, SmartphoneDef, TabletDef, CellphoneDef, SolidStateDriveDef, MobileDef, DisplayDef, SmartphoneDef, TabletDef, CellphoneDef,
HardDriveDef, MotherboardDef, NetworkAdapterDef, RamModuleDef, ProcessorDef, UserDef, HardDriveDef, MotherboardDef, NetworkAdapterDef, RamModuleDef, ProcessorDef, SoundCardDef,
UserDef,
OrganizationDef, TagDef, EventDef, AddDef, RemoveDef, EraseBasicDef, EraseSectorsDef, OrganizationDef, TagDef, EventDef, AddDef, RemoveDef, EraseBasicDef, EraseSectorsDef,
StepDef, StepZeroDef, StepRandomDef, RateDef, AggregateRateDef, WorkbenchRateDef, StepDef, StepZeroDef, StepRandomDef, RateDef, AggregateRateDef, WorkbenchRateDef,
PhotoboxUserDef, PhotoboxSystemRateDef, InstallDef, SnapshotDef, TestDef, PhotoboxUserDef, PhotoboxSystemRateDef, InstallDef, SnapshotDef, TestDef,
@ -33,7 +34,7 @@ class DevicehubConfig(Config):
} }
PASSWORD_SCHEMES = {'pbkdf2_sha256'} # type: Set[str] PASSWORD_SCHEMES = {'pbkdf2_sha256'} # type: Set[str]
SQLALCHEMY_DATABASE_URI = 'postgresql://dhub:ereuse@localhost/devicehub' # type: str SQLALCHEMY_DATABASE_URI = 'postgresql://dhub:ereuse@localhost/devicehub' # type: str
MIN_WORKBENCH = StrictVersion('11.0') # type: StrictVersion MIN_WORKBENCH = StrictVersion('11.0a1') # type: StrictVersion
""" """
the minimum algorithm_version of ereuse.org workbench that this devicehub the minimum algorithm_version of ereuse.org workbench that this devicehub
accepts. we recommend not changing this value. accepts. we recommend not changing this value.

View File

@ -3,11 +3,10 @@ from pathlib import Path
import click import click
import click_spinner import click_spinner
import yaml import yaml
from tqdm import tqdm
from ereuse_devicehub.client import UserClient from ereuse_devicehub.client import UserClient
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.resources.event.models import Snapshot from ereuse_devicehub.resources.event.models import Snapshot
from ereuse_devicehub.resources.inventory import Inventory
from ereuse_devicehub.resources.tag.model import Tag from ereuse_devicehub.resources.tag.model import Tag
from ereuse_devicehub.resources.user import User from ereuse_devicehub.resources.user import User
@ -32,16 +31,21 @@ class Dummy:
@click.confirmation_option(prompt='This command deletes the DB in the process. ' @click.confirmation_option(prompt='This command deletes the DB in the process. '
'Do you want to continue?') 'Do you want to continue?')
def run(self): def run(self):
print('Preparing the database...') print('Preparing the database...'.ljust(30), end='')
with click_spinner.spinner(): with click_spinner.spinner():
self.app.init_db(erase=True) self.app.init_db(erase=True)
user = self.user_client('user@dhub.com', '1234') user = self.user_client('user@dhub.com', '1234')
user.post(res=Tag, query=[('ids', i) for i in self.TAGS], data={}) user.post(res=Tag, query=[('ids', i) for i in self.TAGS], data={})
print('Creating devices...') files = tuple(Path(__file__).parent.joinpath('files').iterdir())
for file_name in tqdm(self.SNAPSHOTS): print('done.')
snapshot = self.file(file_name) with click.progressbar(files, label='Creating devices...'.ljust(28)) as bar:
user.post(res=Snapshot, data=snapshot) for path in bar:
print('Done :-)') with path.open() as f:
snapshot = yaml.load(f)
user.post(res=Snapshot, data=snapshot)
inventory, _ = user.get(res=Inventory)
assert len(inventory['devices'])
print('⭐ Done.')
def user_client(self, email: str, password: str): def user_client(self, email: str, password: str):
user = User(email=email, password=password) user = User(email=email, password=password)
@ -53,10 +57,3 @@ class Dummy:
password=password) password=password)
client.user, _ = client.login(client.email, client.password) client.user, _ = client.login(client.email, client.password)
return client return client
def file(self, name: str):
with Path(__file__) \
.parent \
.joinpath('files') \
.joinpath(name + '.snapshot.yaml').open() as f:
return yaml.load(f)

View File

@ -0,0 +1,160 @@
{
"closed": false,
"uuid": "f9e5e587-baee-44e1-9a94-255d216bbda9",
"components": [
{
"events": [],
"serialNumber": "6c:62:6d:81:22:9f",
"type": "NetworkAdapter",
"manufacturer": "Intel Corporation",
"speed": 1000,
"model": "82578DM Gigabit Network Connection"
},
{
"format": "DIMM",
"events": [
],
"interface": "DDR3",
"serialNumber": "B4012F30",
"size": 2048,
"type": "RamModule",
"manufacturer": "JEDEC ID:80 2C",
"speed": 1333.0,
"model": "16JTF25664AZ-1G4F"
},
{
"format": "DIMM",
"events": [],
"interface": "DDR3",
"serialNumber": "8E9F2E29",
"size": 2048,
"type": "RamModule",
"manufacturer": "JEDEC ID:80 2C",
"speed": 1333.0,
"model": "16JTF25664AZ-1G4F"
},
{
"format": "DIMM",
"events": [],
"interface": "DDR3",
"serialNumber": "9A012F30",
"size": 2048,
"type": "RamModule",
"manufacturer": "JEDEC ID:80 2C",
"speed": 1333.0,
"model": "16JTF25664AZ-1G4F"
},
{
"format": "DIMM",
"events": [
],
"interface": "DDR3",
"serialNumber": "8F9F2E29",
"size": 2048,
"type": "RamModule",
"manufacturer": "JEDEC ID:80 2C",
"speed": 1333.0,
"model": "16JTF25664AZ-1G4F"
},
{
"cores": 2,
"events": [
{
"rate": 23410.76,
"type": "BenchmarkProcessor",
"elapsed": 1
},
{
"rate": 17.0186,
"type": "BenchmarkProcessorSysbench",
"elapsed": 18
}
],
"serialNumber": null,
"type": "Processor",
"manufacturer": "Intel Corp.",
"address": 64,
"speed": 1.199,
"model": "Intel Core i3 CPU 530 @ 2.93GHz"
},
{
"events": [
{
"writeSpeed": 24.5,
"readSpeed": 111.0,
"type": "BenchmarkDataStorage",
"elapsed": 14
},
{
"status": "Unspecified Error. Self-test not started.",
"error": true,
"type": "TestDataStorage",
"elapsed": 1,
"length": Short
}
],
"interface": "ATA",
"serialNumber": "WD-WCAV2U909540",
"size": 305245,
"type": "HardDrive",
"manufacturer": "Western Digital",
"model": "WDC WD3200AAJS-6"
},
{
"events": [],
"serialNumber": null,
"type": "SoundCard",
"manufacturer": "Intel Corporation",
"model": "5 Series/3400 Series Chipset High Definition Audio"
},
{
"memory": 256.0,
"events": [],
"serialNumber": null,
"type": "GraphicCard",
"manufacturer": "Intel Corporation",
"model": "Core Processor Integrated Graphics Controller"
},
{
"events": [],
"firewire": 0,
"pcmcia": 0,
"serialNumber": "CZC0408YJG",
"slots": 0,
"type": "Motherboard",
"manufacturer": "Hewlett-Packard",
"usb": 2,
"serial": 0,
"model": "304Ah"
}
],
"software": "Workbench",
"elapsed": 96,
"version": "11.0a1",
"expectedEvents": [
"StressTest",
"Benchmark",
"SmartTest"
],
"device": {
"events": [
{
"type": "StressTest",
"elapsed": 60,
"error": false
},
{
"rate": 0.9759,
"type": "BenchmarkRamSysbench",
"elapsed": 1
}
],
"serialNumber": "CZC0408YJG",
"type": "Desktop",
"manufacturer": "Hewlett-Packard",
"chassis": "Tower",
"model": "HP Compaq 8100 Elite SFF"
},
"type": "Snapshot",
"date": "2018-06-29T12:28:54.508266"
}

View File

@ -0,0 +1,139 @@
{
"expectedEvents": [
"SmartTest",
"Benchmark",
"StressTest"
],
"date": "2018-06-29T15:29:29.322424",
"elapsed": 391,
"software": "Workbench",
"components": [
{
"serialNumber": null,
"interface": "DDR",
"size": 1024,
"manufacturer": null,
"format": "SODIMM",
"events": [
],
"speed": 533.0,
"type": "RamModule",
"model": null
},
{
"serialNumber": null,
"manufacturer": "Intel Corporation",
"events": [],
"type": "SoundCard",
"model": "NM10/ICH7 Family High Definition Audio Controller"
},
{
"serialNumber": null,
"manufacturer": "Chicony Electronics Co., Ltd.",
"events": [],
"type": "SoundCard",
"model": "USB2.0 UVC WebCam"
},
{
"serialNumber": "00:23:08:a5:07:6d",
"manufacturer": "Qualcomm Atheros",
"events": [],
"type": "NetworkAdapter",
"model": "AR9285 Wireless Network Adapter"
},
{
"serialNumber": "00:23:5a:fe:d7:14",
"manufacturer": "Realtek Semiconductor Co., Ltd.",
"events": [],
"speed": 100,
"type": "NetworkAdapter",
"model": "RTL810xE PCI Express Fast Ethernet controller"
},
{
"serialNumber": null,
"address": 32,
"manufacturer": "Intel Corp.",
"events": [
{
"rate": 171.3049,
"type": "BenchmarkProcessorSysbench",
"elapsed": 171
},
{
"rate": 6383.9,
"type": "BenchmarkProcessor",
"elapsed": 1
}
],
"speed": 1.3330000000000002,
"type": "Processor",
"model": "Intel Atom CPU N270 @ 1.60GHz"
},
{
"memory": 256.0,
"serialNumber": null,
"manufacturer": "Intel Corporation",
"events": [],
"type": "GraphicCard",
"model": "Mobile 945GSE Express Integrated Graphics Controller"
},
{
"serialNumber": "090623PB5B00QCGREMAH",
"interface": "ATA",
"size": 152627,
"manufacturer": "Hitachi",
"events": [
{
"elapsed": 71,
"readSpeed": 19.1,
"type": "BenchmarkDataStorage",
"writeSpeed": 4.7
},
{
"elapsed": 2,
"status": "Unspecified Error. Self-test not started.",
"type": "TestDataStorage",
"length": Short,
"error": true
}
],
"type": "HardDrive",
"model": "HTS54501"
},
{
"slots": 0,
"serialNumber": "0123456789AB",
"manufacturer": "TOSHIBA",
"firewire": 0,
"events": [],
"pcmcia": 0,
"usb": 5,
"serial": 1,
"type": "Motherboard",
"model": "KAVAA"
}
],
"version": "11.0a1",
"device": {
"serialNumber": "79545417K",
"manufacturer": "TOSHIBA",
"events": [
{
"type": "StressTest",
"error": false,
"elapsed": 120
},
{
"rate": 19.0443,
"type": "BenchmarkRamSysbench",
"elapsed": 19
}
],
"type": "Laptop",
"chassis": "Netbook",
"model": "NB200"
},
"uuid": "918726ae-c6bc-40aa-97cf-ad80d69268f9",
"closed": false,
"type": "Snapshot"
}

View File

@ -60,7 +60,6 @@ components:
elapsed: 21 elapsed: 21
- type: TestDataStorage - type: TestDataStorage
elapsed: 233 elapsed: 233
firstError: 0
error: False error: False
status: Completed without error status: Completed without error
length: Short length: Short
@ -76,17 +75,14 @@ components:
remainingLifetimePercentage: 1 remainingLifetimePercentage: 1
- type: EraseSectors - type: EraseSectors
error: False error: False
cleanWithZeros: False zeros: False
startTime: 2018-01-01T10:10:10 startTime: 2018-01-01T10:10:10
endTime: 2018-01-01T12:10:10 endTime: 2018-01-01T12:10:10
secureRandomSteps: 0
steps: steps:
- type: StepRandom - type: StepRandom
startTime: 2018-01-01T10:10:10 startTime: 2018-01-01T10:10:10
endTime: 2018-01-01T12:10:10 endTime: 2018-01-01T12:10:10
error: False error: False
cleanWithZeros: False
secureRandomSteps: 0
- type: HardDrive - type: HardDrive
serialNumber: hdd1-1s serialNumber: hdd1-1s
model: hdd1-1ml model: hdd1-1ml
@ -97,17 +93,14 @@ components:
writeSpeed: 5 writeSpeed: 5
- type: EraseSectors - type: EraseSectors
error: False error: False
cleanWithZeros: False zeros: False
startTime: 2018-01-01T10:10:10 startTime: 2018-01-01T10:10:10
endTime: 2018-01-01T12:10:10 endTime: 2018-01-01T12:10:10
secureRandomSteps: 0
steps: steps:
- type: StepRandom - type: StepRandom
startTime: 2018-01-01T10:10:10 startTime: 2018-01-01T10:10:10
endTime: 2018-01-01T12:10:10 endTime: 2018-01-01T12:10:10
error: False error: False
cleanWithZeros: False
secureRandomSteps: 0
- type: Install - type: Install
elapsed: 420 elapsed: 420
error: False error: False

View File

@ -1,7 +1,7 @@
from ereuse_devicehub.resources.device.schemas import Cellphone, Component, Computer, \ from ereuse_devicehub.resources.device.schemas import Cellphone, Component, Computer, \
ComputerMonitor, DataStorage, Desktop, Device, Display, GraphicCard, HardDrive, Laptop, Mobile, \ ComputerMonitor, DataStorage, Desktop, Device, Display, GraphicCard, HardDrive, Laptop, Mobile, \
Monitor, Motherboard, NetworkAdapter, Processor, RamModule, Server, Smartphone, \ Monitor, Motherboard, NetworkAdapter, Processor, RamModule, Server, Smartphone, \
SolidStateDrive, Tablet, TelevisionSet SolidStateDrive, SoundCard, Tablet, TelevisionSet
from ereuse_devicehub.resources.device.views import DeviceView from ereuse_devicehub.resources.device.views import DeviceView
from teal.resource import Converters, Resource from teal.resource import Converters, Resource
@ -113,6 +113,11 @@ class ProcessorDef(ComponentDef):
SCHEMA = Processor SCHEMA = Processor
class SoundCardDef(ComponentDef):
VIEW = None
SCHEMA = SoundCard
class DisplayDef(ComponentDef): class DisplayDef(ComponentDef):
VIEW = None VIEW = None
SCHEMA = Display SCHEMA = Display

View File

@ -1,8 +1,11 @@
from contextlib import suppress from contextlib import suppress
from itertools import chain
from operator import attrgetter from operator import attrgetter
from typing import Dict, Set from typing import Dict, Set
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
RamFormat, RamInterface
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE, STR_SM_SIZE, Thing
from itertools import chain
from sqlalchemy import BigInteger, Column, Enum as DBEnum, Float, ForeignKey, Integer, Sequence, \ from sqlalchemy import BigInteger, Column, Enum as DBEnum, Float, ForeignKey, Integer, Sequence, \
SmallInteger, Unicode, inspect SmallInteger, Unicode, inspect
from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.ext.declarative import declared_attr
@ -10,14 +13,11 @@ from sqlalchemy.orm import ColumnProperty, backref, relationship, validates
from sqlalchemy.util import OrderedSet from sqlalchemy.util import OrderedSet
from sqlalchemy_utils import ColorType from sqlalchemy_utils import ColorType
from stdnum import imei, meid from stdnum import imei, meid
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
RamFormat, RamInterface
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE, STR_SM_SIZE, Thing
from ereuse_utils.naming import Naming
from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, check_range from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, check_range
from teal.marshmallow import ValidationError from teal.marshmallow import ValidationError
from ereuse_utils.naming import Naming
class Device(Thing): class Device(Thing):
""" """
@ -264,14 +264,14 @@ class SolidStateDrive(DataStorage):
class Motherboard(JoinedComponentTableMixin, Component): class Motherboard(JoinedComponentTableMixin, Component):
slots = Column(SmallInteger, check_range('slots')) slots = Column(SmallInteger, check_range('slots', min=0))
slots.comment = """ slots.comment = """
PCI slots the motherboard has. PCI slots the motherboard has.
""" """
usb = Column(SmallInteger, check_range('usb')) usb = Column(SmallInteger, check_range('usb', min=0))
firewire = Column(SmallInteger, check_range('firewire')) firewire = Column(SmallInteger, check_range('firewire', min=0))
serial = Column(SmallInteger, check_range('serial')) serial = Column(SmallInteger, check_range('serial', min=0))
pcmcia = Column(SmallInteger, check_range('pcmcia')) pcmcia = Column(SmallInteger, check_range('pcmcia', min=0))
class NetworkMixin: class NetworkMixin:
@ -298,6 +298,10 @@ class RamModule(JoinedComponentTableMixin, Component):
format = Column(DBEnum(RamFormat)) format = Column(DBEnum(RamFormat))
class SoundCard(JoinedComponentTableMixin, Component):
pass
class Display(JoinedComponentTableMixin, DisplayMixin, Component): class Display(JoinedComponentTableMixin, DisplayMixin, Component):
""" """
The display of a device. This is used in all devices that have The display of a device. This is used in all devices that have

View File

@ -1,15 +1,14 @@
from ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources.device import models as m
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
RamFormat, RamInterface
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
from marshmallow import post_load, pre_load from marshmallow import post_load, pre_load
from marshmallow.fields import Float, Integer, Str from marshmallow.fields import Float, Integer, Str
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 ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources.device import models as m
from ereuse_devicehub.resources.enums import ComputerChassis, DisplayTech, \
RamFormat, RamInterface
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
from teal.marshmallow import EnumField, ValidationError from teal.marshmallow import EnumField, ValidationError
@ -138,6 +137,7 @@ class DataStorage(Component):
size = Integer(validate=Range(0, 10 ** 8), size = Integer(validate=Range(0, 10 ** 8),
unit=UnitCodes.mbyte, unit=UnitCodes.mbyte,
description=m.DataStorage.size.comment) description=m.DataStorage.size.comment)
interface = EnumField(DataStorageInterface)
class HardDrive(DataStorage): class HardDrive(DataStorage):
@ -149,7 +149,7 @@ class SolidStateDrive(DataStorage):
class Motherboard(Component): class Motherboard(Component):
slots = Integer(validate=Range(1, 20), slots = Integer(validate=Range(0, 20),
description=m.Motherboard.slots.comment) description=m.Motherboard.slots.comment)
usb = Integer(validate=Range(0, 20)) usb = Integer(validate=Range(0, 20))
firewire = Integer(validate=Range(0, 20)) firewire = Integer(validate=Range(0, 20))
@ -174,5 +174,9 @@ class RamModule(Component):
format = EnumField(RamFormat) format = EnumField(RamFormat)
class SoundCard(Component):
pass
class Display(DisplayMixin, Component): class Display(DisplayMixin, Component):
pass pass

View File

@ -116,9 +116,11 @@ class ImageMimeTypes(Enum):
@unique @unique
class SnapshotExpectedEvents(Enum): class SnapshotExpectedEvents(Enum):
"""Events that Workbench can perform when processing a device.""" """Events that Workbench can perform when processing a device."""
Benchmark = 'Benchmark'
TestDataStorage = 'TestDataStorage' TestDataStorage = 'TestDataStorage'
StressTest = 'StressTest' StressTest = 'StressTest'
EraseSectors = 'EraseSectors' EraseSectors = 'EraseSectors'
SmartTest = 'SmartTest'
Install = 'Install' Install = 'Install'

View File

@ -1,17 +1,8 @@
from collections import Iterable from collections import Iterable
from datetime import timedelta
from typing import Set, Union from typing import Set, Union
from uuid import uuid4 from uuid import uuid4
from flask import g
from sqlalchemy import BigInteger, Boolean, CheckConstraint, Column, DateTime, Enum as DBEnum, \
Float, ForeignKey, Interval, JSON, SmallInteger, Unicode, event
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.orm import backref, relationship
from sqlalchemy.orm.events import AttributeEvents as Events
from sqlalchemy.util import OrderedSet
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Device from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Device
from ereuse_devicehub.resources.enums import AppearanceRange, BOX_RATE_3, BOX_RATE_5, Bios, \ from ereuse_devicehub.resources.enums import AppearanceRange, BOX_RATE_3, BOX_RATE_5, Bios, \
@ -20,6 +11,15 @@ from ereuse_devicehub.resources.enums import AppearanceRange, BOX_RATE_3, BOX_RA
from ereuse_devicehub.resources.image.models import Image from ereuse_devicehub.resources.image.models import Image
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE, STR_SM_SIZE, Thing from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE, STR_SM_SIZE, Thing
from ereuse_devicehub.resources.user.models import User from ereuse_devicehub.resources.user.models import User
from flask import g
from sqlalchemy import BigInteger, Boolean, CheckConstraint, Column, DateTime, Enum as DBEnum, \
Float, ForeignKey, Interval, JSON, SmallInteger, Unicode, event
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.orm import backref, relationship, validates
from sqlalchemy.orm.events import AttributeEvents as Events
from sqlalchemy.util import OrderedSet
from teal.db import ArrayOfEnum, CASCADE, CASCADE_OWN, INHERIT_COND, POLYMORPHIC_ID, \ from teal.db import ArrayOfEnum, CASCADE, CASCADE_OWN, INHERIT_COND, POLYMORPHIC_ID, \
POLYMORPHIC_ON, StrictVersionType, check_range POLYMORPHIC_ON, StrictVersionType, check_range
@ -198,10 +198,11 @@ class Deallocate(JoinedTableMixin, EventWithMultipleDevices):
class EraseBasic(JoinedTableMixin, EventWithOneDevice): class EraseBasic(JoinedTableMixin, EventWithOneDevice):
start_time = Column(DateTime, nullable=False) start_time = Column(DateTime, nullable=False)
end_time = Column(DateTime, CheckConstraint('end_time > start_time'), nullable=False) end_time = Column(DateTime, CheckConstraint('end_time > start_time'), nullable=False)
secure_random_steps = Column(SmallInteger, zeros = Column(Boolean, nullable=False)
check_range('secure_random_steps', min=0), zeros.comment = """
nullable=False) Whether this erasure had a first erasure step consisting of
clean_with_zeros = Column(Boolean, nullable=False) only writing zeros.
"""
class Ready(EventWithMultipleDevices): class Ready(EventWithMultipleDevices):
@ -219,10 +220,6 @@ class Step(db.Model):
error = Column(Boolean, default=False, nullable=False) error = Column(Boolean, default=False, nullable=False)
start_time = Column(DateTime, nullable=False) start_time = Column(DateTime, nullable=False)
end_time = Column(DateTime, CheckConstraint('end_time > start_time'), nullable=False) end_time = Column(DateTime, CheckConstraint('end_time > start_time'), nullable=False)
secure_random_steps = Column(SmallInteger,
check_range('secure_random_steps', min=0),
nullable=False)
clean_with_zeros = Column(Boolean, nullable=False)
erasure = relationship(EraseBasic, erasure = relationship(EraseBasic,
backref=backref('steps', backref=backref('steps',
@ -412,8 +409,7 @@ class TestDataStorage(Test):
id = Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True) id = Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
length = Column(DBEnum(TestHardDriveLength), nullable=False) # todo from type length = Column(DBEnum(TestHardDriveLength), nullable=False) # todo from type
status = Column(Unicode(STR_SIZE), nullable=False) status = Column(Unicode(STR_SIZE), nullable=False)
lifetime = Column(Interval, nullable=False) lifetime = Column(Interval)
first_error = Column(SmallInteger, nullable=False, default=0)
assessment = Column(Boolean) assessment = Column(Boolean)
reallocated_sector_count = Column(SmallInteger) reallocated_sector_count = Column(SmallInteger)
power_cycle_count = Column(SmallInteger) power_cycle_count = Column(SmallInteger)
@ -429,6 +425,10 @@ class TestDataStorage(Test):
class StressTest(Test): class StressTest(Test):
pass pass
@validates('elapsed')
def bigger_than_a_minute(self, _, value: timedelta):
assert value.total_seconds() >= 60
class Benchmark(JoinedTableMixin, EventWithOneDevice): class Benchmark(JoinedTableMixin, EventWithOneDevice):
elapsed = Column(Interval) elapsed = Column(Interval)
@ -483,6 +483,13 @@ def validate_device_is_data_storage(target: Event, value: DataStorage, old_value
raise TypeError('{} must be a DataStorage but you passed {}'.format(initiator.impl, value)) raise TypeError('{} must be a DataStorage but you passed {}'.format(initiator.impl, value))
@event.listens_for(BenchmarkRamSysbench.device, Events.set.__name__, propagate=True)
def events_not_for_components(target: Event, value: Device, old_value, initiator):
"""Validates events that cannot be performed to components."""
if isinstance(value, Component):
raise TypeError('{!r} cannot be performed to a component ({!r}).'.format(target, value))
# The following listeners keep relationships with device <-> components synced with the event # The following listeners keep relationships with device <-> components synced with the event
# So, if you add or remove devices from events these listeners will # So, if you add or remove devices from events these listeners will
# automatically add/remove the ``components`` and ``parent`` of such events # automatically add/remove the ``components`` and ``parent`` of such events

View File

@ -81,8 +81,6 @@ class Step(Model):
self.success = ... # type: bool self.success = ... # type: bool
self.start_time = ... # type: datetime self.start_time = ... # type: datetime
self.end_time = ... # type: datetime self.end_time = ... # type: datetime
self.secure_random_steps = ... # type: int
self.clean_with_zeros = ... # type: bool
self.erasure = ... # type: EraseBasic self.erasure = ... # type: EraseBasic
@ -230,9 +228,8 @@ class EraseBasic(EventWithOneDevice):
super().__init__(**kwargs) super().__init__(**kwargs)
self.start_time = ... # type: datetime self.start_time = ... # type: datetime
self.end_time = ... # type: datetime self.end_time = ... # type: datetime
self.secure_random_steps = ... # type: int
self.steps = ... # type: List[Step] self.steps = ... # type: List[Step]
self.clean_with_zeros = ... # type: bool self.zeros = ... # type: bool
self.success = ... # type: bool self.success = ... # type: bool

View File

@ -63,9 +63,7 @@ class Deallocate(EventWithMultipleDevices):
class EraseBasic(EventWithOneDevice): class EraseBasic(EventWithOneDevice):
start_time = DateTime(required=True, data_key='startTime') start_time = DateTime(required=True, data_key='startTime')
end_time = DateTime(required=True, data_key='endTime') end_time = DateTime(required=True, data_key='endTime')
secure_random_steps = Integer(validate=Range(min=0), required=True, zeros = Boolean(required=True, description=m.EraseBasic.zeros.comment)
data_key='secureRandomSteps')
clean_with_zeros = Boolean(required=True, data_key='cleanWithZeros')
steps = NestedOn('Step', many=True, required=True) steps = NestedOn('Step', many=True, required=True)
@ -77,10 +75,6 @@ class Step(Schema):
type = String(description='Only required when it is nested.') type = String(description='Only required when it is nested.')
start_time = DateTime(required=True, data_key='startTime') start_time = DateTime(required=True, data_key='startTime')
end_time = DateTime(required=True, data_key='endTime') end_time = DateTime(required=True, data_key='endTime')
secure_random_steps = Integer(validate=Range(min=0),
required=True,
data_key='secureRandomSteps')
clean_with_zeros = Boolean(required=True, data_key='cleanWithZeros')
error = Boolean(default=False, description='Did the event fail?') error = Boolean(default=False, description='Did the event fail?')
@ -243,8 +237,7 @@ class Test(EventWithOneDevice):
class TestDataStorage(Test): class TestDataStorage(Test):
length = EnumField(TestHardDriveLength, required=True) length = EnumField(TestHardDriveLength, required=True)
status = String(validate=Length(max=STR_SIZE), required=True) status = String(validate=Length(max=STR_SIZE), required=True)
lifetime = TimeDelta(precision=TimeDelta.DAYS, required=True) lifetime = TimeDelta(precision=TimeDelta.DAYS)
first_error = Integer(missing=0, data_key='firstError')
assessment = Boolean() assessment = Boolean()
reallocated_sector_count = Integer(data_key='reallocatedSectorCount') reallocated_sector_count = Integer(data_key='reallocatedSectorCount')
power_cycle_count = Integer(data_key='powerCycleCount') power_cycle_count = Integer(data_key='powerCycleCount')

View File

@ -5,7 +5,7 @@ with open('README.md') as f:
setup( setup(
name='ereuse-devicehub', name='ereuse-devicehub',
version='0.2.0a9', version='0.2.0a10',
packages=find_packages(), packages=find_packages(),
url='https://github.com/ereuse/devicehub-teal', url='https://github.com/ereuse/devicehub-teal',
license='Affero', license='Affero',
@ -16,14 +16,14 @@ setup(
long_description=long_description, long_description=long_description,
long_description_content_type='text/markdown', long_description_content_type='text/markdown',
install_requires=[ install_requires=[
'teal>=0.2.0a5', 'teal>=0.2.0a6',
'marshmallow_enum', 'marshmallow_enum',
'ereuse-utils[Naming]>=0.3.0b2', 'ereuse-utils[Naming]>=0.3.0b2',
'psycopg2-binary', 'psycopg2-binary',
'requests', 'requests',
'requests-toolbelt', 'requests-toolbelt',
'hashids', 'hashids',
'tqdm', 'click',
'click-spinner', 'click-spinner',
'sqlalchemy-utils[password, color, babel]', 'sqlalchemy-utils[password, color, babel]',
'PyYAML', 'PyYAML',

View File

@ -16,23 +16,18 @@ components:
manufacturer: c1mr manufacturer: c1mr
events: events:
- type: EraseSectors - type: EraseSectors
cleanWithZeros: True zeros: True
startTime: 2018-06-01T08:12:06 startTime: 2018-06-01T08:12:06
endTime: 2018-06-01T09:12:06 endTime: 2018-06-01T09:12:06
secureRandomSteps: 20
steps: steps:
- type: StepZero - type: StepZero
error: False error: False
startTime: 2018-06-01T08:15:00 startTime: 2018-06-01T08:15:00
endTime: 2018-06-01T09:16:00 endTime: 2018-06-01T09:16:00
secureRandomSteps: 1
cleanWithZeros: True
- type: StepZero - type: StepZero
error: False error: False
startTime: 2018-06-01T08:16:00 startTime: 2018-06-01T08:16:00
endTime: 2018-06-01T09:17:00 endTime: 2018-06-01T09:17:00
secureRandomSteps: 1
cleanWithZeros: True
- type: GraphicCard - type: GraphicCard
serialNumber: gc1s serialNumber: gc1s
model: gc1ml model: gc1ml

View File

@ -0,0 +1,160 @@
{
"closed": false,
"uuid": "f9e5e587-baee-44e1-9a94-255d216bbda9",
"components": [
{
"events": [],
"serialNumber": "6c:62:6d:81:22:9f",
"type": "NetworkAdapter",
"manufacturer": "Intel Corporation",
"speed": 1000,
"model": "82578DM Gigabit Network Connection"
},
{
"format": "DIMM",
"events": [
],
"interface": "DDR3",
"serialNumber": "B4012F30",
"size": 2048,
"type": "RamModule",
"manufacturer": "JEDEC ID:80 2C",
"speed": 1333.0,
"model": "16JTF25664AZ-1G4F"
},
{
"format": "DIMM",
"events": [],
"interface": "DDR3",
"serialNumber": "8E9F2E29",
"size": 2048,
"type": "RamModule",
"manufacturer": "JEDEC ID:80 2C",
"speed": 1333.0,
"model": "16JTF25664AZ-1G4F"
},
{
"format": "DIMM",
"events": [],
"interface": "DDR3",
"serialNumber": "9A012F30",
"size": 2048,
"type": "RamModule",
"manufacturer": "JEDEC ID:80 2C",
"speed": 1333.0,
"model": "16JTF25664AZ-1G4F"
},
{
"format": "DIMM",
"events": [
],
"interface": "DDR3",
"serialNumber": "8F9F2E29",
"size": 2048,
"type": "RamModule",
"manufacturer": "JEDEC ID:80 2C",
"speed": 1333.0,
"model": "16JTF25664AZ-1G4F"
},
{
"cores": 2,
"events": [
{
"rate": 23410.76,
"type": "BenchmarkProcessor",
"elapsed": 1
},
{
"rate": 17.0186,
"type": "BenchmarkProcessorSysbench",
"elapsed": 18
}
],
"serialNumber": null,
"type": "Processor",
"manufacturer": "Intel Corp.",
"address": 64,
"speed": 1.199,
"model": "Intel Core i3 CPU 530 @ 2.93GHz"
},
{
"events": [
{
"writeSpeed": 24.5,
"readSpeed": 111.0,
"type": "BenchmarkDataStorage",
"elapsed": 14
},
{
"status": "Unspecified Error. Self-test not started.",
"error": true,
"type": "TestDataStorage",
"elapsed": 1,
"length": Short
}
],
"interface": "ATA",
"serialNumber": "WD-WCAV2U909540",
"size": 305245,
"type": "HardDrive",
"manufacturer": "Western Digital",
"model": "WDC WD3200AAJS-6"
},
{
"events": [],
"serialNumber": null,
"type": "SoundCard",
"manufacturer": "Intel Corporation",
"model": "5 Series/3400 Series Chipset High Definition Audio"
},
{
"memory": 256.0,
"events": [],
"serialNumber": null,
"type": "GraphicCard",
"manufacturer": "Intel Corporation",
"model": "Core Processor Integrated Graphics Controller"
},
{
"events": [],
"firewire": 0,
"pcmcia": 0,
"serialNumber": "CZC0408YJG",
"slots": 0,
"type": "Motherboard",
"manufacturer": "Hewlett-Packard",
"usb": 2,
"serial": 0,
"model": "304Ah"
}
],
"software": "Workbench",
"elapsed": 96,
"version": "11.0a1",
"expectedEvents": [
"StressTest",
"Benchmark",
"SmartTest"
],
"device": {
"events": [
{
"type": "StressTest",
"elapsed": 60,
"error": false
},
{
"rate": 0.9759,
"type": "BenchmarkRamSysbench",
"elapsed": 1
}
],
"serialNumber": "CZC0408YJG",
"type": "Desktop",
"manufacturer": "Hewlett-Packard",
"chassis": "Tower",
"model": "HP Compaq 8100 Elite SFF"
},
"type": "Snapshot",
"date": "2018-06-29T12:28:54.508266"
}

View File

@ -0,0 +1,139 @@
{
"expectedEvents": [
"SmartTest",
"Benchmark",
"StressTest"
],
"date": "2018-06-29T15:29:29.322424",
"elapsed": 391,
"software": "Workbench",
"components": [
{
"serialNumber": null,
"interface": "DDR",
"size": 1024,
"manufacturer": null,
"format": "SODIMM",
"events": [
],
"speed": 533.0,
"type": "RamModule",
"model": null
},
{
"serialNumber": null,
"manufacturer": "Intel Corporation",
"events": [],
"type": "SoundCard",
"model": "NM10/ICH7 Family High Definition Audio Controller"
},
{
"serialNumber": null,
"manufacturer": "Chicony Electronics Co., Ltd.",
"events": [],
"type": "SoundCard",
"model": "USB2.0 UVC WebCam"
},
{
"serialNumber": "00:23:08:a5:07:6d",
"manufacturer": "Qualcomm Atheros",
"events": [],
"type": "NetworkAdapter",
"model": "AR9285 Wireless Network Adapter"
},
{
"serialNumber": "00:23:5a:fe:d7:14",
"manufacturer": "Realtek Semiconductor Co., Ltd.",
"events": [],
"speed": 100,
"type": "NetworkAdapter",
"model": "RTL810xE PCI Express Fast Ethernet controller"
},
{
"serialNumber": null,
"address": 32,
"manufacturer": "Intel Corp.",
"events": [
{
"rate": 171.3049,
"type": "BenchmarkProcessorSysbench",
"elapsed": 171
},
{
"rate": 6383.9,
"type": "BenchmarkProcessor",
"elapsed": 1
}
],
"speed": 1.3330000000000002,
"type": "Processor",
"model": "Intel Atom CPU N270 @ 1.60GHz"
},
{
"memory": 256.0,
"serialNumber": null,
"manufacturer": "Intel Corporation",
"events": [],
"type": "GraphicCard",
"model": "Mobile 945GSE Express Integrated Graphics Controller"
},
{
"serialNumber": "090623PB5B00QCGREMAH",
"interface": "ATA",
"size": 152627,
"manufacturer": "Hitachi",
"events": [
{
"elapsed": 71,
"readSpeed": 19.1,
"type": "BenchmarkDataStorage",
"writeSpeed": 4.7
},
{
"elapsed": 2,
"status": "Unspecified Error. Self-test not started.",
"type": "TestDataStorage",
"length": Short,
"error": true
}
],
"type": "HardDrive",
"model": "HTS54501"
},
{
"slots": 0,
"serialNumber": "0123456789AB",
"manufacturer": "TOSHIBA",
"firewire": 0,
"events": [],
"pcmcia": 0,
"usb": 5,
"serial": 1,
"type": "Motherboard",
"model": "KAVAA"
}
],
"version": "11.0a1",
"device": {
"serialNumber": "79545417K",
"manufacturer": "TOSHIBA",
"events": [
{
"type": "StressTest",
"error": false,
"elapsed": 120
},
{
"rate": 19.0443,
"type": "BenchmarkRamSysbench",
"elapsed": 19
}
],
"type": "Laptop",
"chassis": "Netbook",
"model": "NB200"
},
"uuid": "918726ae-c6bc-40aa-97cf-ad80d69268f9",
"closed": false,
"type": "Snapshot"
}

View File

@ -58,7 +58,6 @@ components:
elapsed: 21 elapsed: 21
- type: TestDataStorage - type: TestDataStorage
elapsed: 233 elapsed: 233
firstError: 0
error: False error: False
status: Completed without error status: Completed without error
length: Short length: Short

View File

@ -10,14 +10,11 @@ type: 'EraseSectors'
error: False error: False
# snapshot: None fulfill! # snapshot: None fulfill!
# device: None fulfill! # device: None fulfill!
cleanWithZeros: False zeros: False
startTime: 2018-01-01T10:10:10 startTime: 2018-01-01T10:10:10
endTime: 2018-01-01T12:10:10 endTime: 2018-01-01T12:10:10
secureRandomSteps: 0
steps: steps:
- type: 'StepRandom' - type: 'StepRandom'
startTime: '2018-01-01T10:10:10' startTime: '2018-01-01T10:10:10'
endTime: '2018-01-01T12:10:10' endTime: '2018-01-01T12:10:10'
error: False error: False
cleanWithZeros: False
secureRandomSteps: 0

View File

@ -35,4 +35,4 @@ def test_api_docs(client: Client):
'scheme': 'basic', 'scheme': 'basic',
'name': 'Authorization' 'name': 'Authorization'
} }
assert len(docs['definitions']) == 51 assert 52 == len(docs['definitions'])

View File

@ -1,15 +1,14 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
import pytest import pytest
from flask import g
from sqlalchemy.util import OrderedSet
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.resources.device.models import Computer, Device, GraphicCard, HardDrive, \ from ereuse_devicehub.resources.device.models import Computer, Device, GraphicCard, HardDrive, \
RamModule, SolidStateDrive RamModule, SolidStateDrive
from ereuse_devicehub.resources.enums import TestHardDriveLength from ereuse_devicehub.resources.enums import TestHardDriveLength
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, EraseBasic, EraseSectors, \ from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, EraseBasic, EraseSectors, \
EventWithOneDevice, Install, Ready, StepZero, StressTest, TestDataStorage EventWithOneDevice, Install, Ready, StepRandom, StepZero, StressTest, TestDataStorage
from flask import g
from sqlalchemy.util import OrderedSet
from tests.conftest import create_user from tests.conftest import create_user
@ -34,10 +33,9 @@ def test_author():
def test_erase_basic(): def test_erase_basic():
erasure = EraseBasic( erasure = EraseBasic(
device=HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar'), device=HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar'),
clean_with_zeros=True, zeros=True,
start_time=datetime.now(), start_time=datetime.now(),
end_time=datetime.now(), end_time=datetime.now(),
secure_random_steps=25,
error=False error=False
) )
db.session.add(erasure) db.session.add(erasure)
@ -59,7 +57,6 @@ def test_validate_device_data_storage():
clean_with_zeros=True, clean_with_zeros=True,
start_time=datetime.now(), start_time=datetime.now(),
end_time=datetime.now(), end_time=datetime.now(),
secure_random_steps=25,
error=False error=False
) )
@ -68,38 +65,28 @@ def test_validate_device_data_storage():
def test_erase_sectors_steps(): def test_erase_sectors_steps():
erasure = EraseSectors( erasure = EraseSectors(
device=SolidStateDrive(serial_number='foo', manufacturer='bar', model='foo-bar'), device=SolidStateDrive(serial_number='foo', manufacturer='bar', model='foo-bar'),
clean_with_zeros=True, zeros=True,
start_time=datetime.now(), start_time=datetime.now(),
end_time=datetime.now(), end_time=datetime.now(),
secure_random_steps=25,
error=False, error=False,
steps=[ steps=[
StepZero(error=False, StepZero(error=False,
start_time=datetime.now(), start_time=datetime.now(),
end_time=datetime.now(), end_time=datetime.now()),
secure_random_steps=1, StepRandom(error=False,
clean_with_zeros=True), start_time=datetime.now(),
end_time=datetime.now()),
StepZero(error=False, StepZero(error=False,
start_time=datetime.now(), start_time=datetime.now(),
end_time=datetime.now(), end_time=datetime.now())
secure_random_steps=2,
clean_with_zeros=True),
StepZero(error=False,
start_time=datetime.now(),
end_time=datetime.now(),
secure_random_steps=3,
clean_with_zeros=True)
] ]
) )
db.session.add(erasure) db.session.add(erasure)
db.session.commit() db.session.commit()
db_erasure = EraseSectors.query.one() db_erasure = EraseSectors.query.one()
# Steps are in order # Steps are in order
assert db_erasure.steps[0].secure_random_steps == 1
assert db_erasure.steps[0].num == 0 assert db_erasure.steps[0].num == 0
assert db_erasure.steps[1].secure_random_steps == 2
assert db_erasure.steps[1].num == 1 assert db_erasure.steps[1].num == 1
assert db_erasure.steps[2].secure_random_steps == 3
assert db_erasure.steps[2].num == 2 assert db_erasure.steps[2].num == 2

View File

@ -330,8 +330,6 @@ def test_erase(user: UserClient):
for step in erasure['steps']: for step in erasure['steps']:
assert step['type'] == 'StepZero' assert step['type'] == 'StepZero'
assert step['error'] is False assert step['error'] is False
assert step['secureRandomSteps'] == 1
assert step['cleanWithZeros'] is True
assert 'num' not in step assert 'num' not in step

View File

@ -112,3 +112,24 @@ def test_workbench_server_phases(user: UserClient):
pc, _ = user.get(res=Device, item=snapshot['id']) pc, _ = user.get(res=Device, item=snapshot['id'])
assert len(pc['events']) == 10 # todo shall I add child events? assert len(pc['events']) == 10 # todo shall I add child events?
def test_real_hp_11(user: UserClient):
s = file('real-hp.snapshot.11')
snapshot, _ = user.post(res=Snapshot, data=s)
assert snapshot['device']['hid'] == 'hewlett-packard-czc0408yjg-hp_compaq_8100_elite_sff'
assert snapshot['device']['chassis'] == 'Tower'
assert set(e['type'] for e in snapshot['events']) == {
'BenchmarkDataStorage',
'BenchmarkProcessor',
'BenchmarkProcessorSysbench',
'TestDataStorage',
'BenchmarkRamSysbench',
'StressTest'
}
assert len(list(e['type'] for e in snapshot['events'])) == 6
def test_real_toshiba_11(user: UserClient):
s = file('real-toshiba.snapshot.11')
snapshot, _ = user.post(res=Snapshot, data=s)