summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-api/opendc/models
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-05-15 13:09:06 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2021-05-18 15:46:40 +0200
commit2281d3265423d01e60f8cc088de5a5730bb8a910 (patch)
tree8dc81338cfd30845717f1b9025176d26c82fe930 /opendc-web/opendc-web-api/opendc/models
parent05d2318538eba71ac0555dc5ec146499d9cb0592 (diff)
api: Migrate to Flask Restful
This change updates the API to use Flask Restful instead of our own in-house REST library. This change reduces the maintenance effort and allows us to drastically simplify the API implementation needed for the OpenDC v2 API.
Diffstat (limited to 'opendc-web/opendc-web-api/opendc/models')
-rw-r--r--opendc-web/opendc-web-api/opendc/models/model.py19
-rw-r--r--opendc-web/opendc-web-api/opendc/models/portfolio.py21
-rw-r--r--opendc-web/opendc-web-api/opendc/models/prefab.py23
-rw-r--r--opendc-web/opendc-web-api/opendc/models/project.py27
-rw-r--r--opendc-web/opendc-web-api/opendc/models/scenario.py46
-rw-r--r--opendc-web/opendc-web-api/opendc/models/topology.py76
6 files changed, 187 insertions, 25 deletions
diff --git a/opendc-web/opendc-web-api/opendc/models/model.py b/opendc-web/opendc-web-api/opendc/models/model.py
index f9dfc9ad..28299453 100644
--- a/opendc-web/opendc-web-api/opendc/models/model.py
+++ b/opendc-web/opendc-web-api/opendc/models/model.py
@@ -1,8 +1,7 @@
from bson.objectid import ObjectId
+from werkzeug.exceptions import NotFound
-from opendc.util.database import DB
-from opendc.util.exceptions import ClientError
-from opendc.util.rest import Response
+from opendc.exts import db
class Model:
@@ -15,15 +14,13 @@ class Model:
"""Fetches the document with given ID from the collection."""
if isinstance(_id, str) and len(_id) == 24:
_id = ObjectId(_id)
- elif not isinstance(_id, ObjectId):
- return cls(None)
- return cls(DB.fetch_one({'_id': _id}, cls.collection_name))
+ return cls(db.fetch_one({'_id': _id}, cls.collection_name))
@classmethod
def get_all(cls):
"""Fetches all documents from the collection."""
- return cls(DB.fetch_all({}, cls.collection_name))
+ return cls(db.fetch_all({}, cls.collection_name))
def __init__(self, obj):
self.obj = obj
@@ -35,7 +32,7 @@ class Model:
def check_exists(self):
"""Raises an error if the enclosed object does not exist."""
if self.obj is None:
- raise ClientError(Response(404, 'Not found.'))
+ raise NotFound('Entity not found.')
def set_property(self, key, value):
"""Sets the given property on the enclosed object, with support for simple nested access."""
@@ -48,11 +45,11 @@ class Model:
def insert(self):
"""Inserts the enclosed object and generates a UUID for it."""
self.obj['_id'] = ObjectId()
- DB.insert(self.obj, self.collection_name)
+ db.insert(self.obj, self.collection_name)
def update(self):
"""Updates the enclosed object and updates the internal reference to the newly inserted object."""
- DB.update(self.get_id(), self.obj, self.collection_name)
+ db.update(self.get_id(), self.obj, self.collection_name)
def delete(self):
"""Deletes the enclosed object in the database, if it existed."""
@@ -60,5 +57,5 @@ class Model:
return None
old_object = self.obj.copy()
- DB.delete_one({'_id': self.get_id()}, self.collection_name)
+ db.delete_one({'_id': self.get_id()}, self.collection_name)
return old_object
diff --git a/opendc-web/opendc-web-api/opendc/models/portfolio.py b/opendc-web/opendc-web-api/opendc/models/portfolio.py
index 8e3f2a52..aff1d3f0 100644
--- a/opendc-web/opendc-web-api/opendc/models/portfolio.py
+++ b/opendc-web/opendc-web-api/opendc/models/portfolio.py
@@ -1,7 +1,28 @@
+from marshmallow import Schema, fields
+
from opendc.models.project import Project
from opendc.models.model import Model
+class TargetSchema(Schema):
+ """
+ Schema representing a target.
+ """
+ enabledMetrics = fields.List(fields.String())
+ repeatsPerScenario = fields.Integer(required=True)
+
+
+class PortfolioSchema(Schema):
+ """
+ Schema representing a portfolio.
+ """
+ _id = fields.String()
+ projectId = fields.String()
+ name = fields.String(required=True)
+ scenarioIds = fields.List(fields.String())
+ targets = fields.Nested(TargetSchema)
+
+
class Portfolio(Model):
"""Model representing a Portfolio."""
diff --git a/opendc-web/opendc-web-api/opendc/models/prefab.py b/opendc-web/opendc-web-api/opendc/models/prefab.py
index 05356358..d83ef4cb 100644
--- a/opendc-web/opendc-web-api/opendc/models/prefab.py
+++ b/opendc-web/opendc-web-api/opendc/models/prefab.py
@@ -1,17 +1,30 @@
+from marshmallow import Schema, fields
+from werkzeug.exceptions import Forbidden
+
+from opendc.models.topology import ObjectSchema
from opendc.models.model import Model
-from opendc.util.exceptions import ClientError
-from opendc.util.rest import Response
+
+
+class PrefabSchema(Schema):
+ """
+ Schema for a Prefab.
+ """
+ _id = fields.String()
+ name = fields.String(required=True)
+ datetimeCreated = fields.DateTime()
+ datetimeLastEdited = fields.DateTime()
+ rack = fields.Nested(ObjectSchema)
class Prefab(Model):
- """Model representing a Project."""
+ """Model representing a Prefab."""
collection_name = 'prefabs'
def check_user_access(self, user_id):
"""Raises an error if the user with given [user_id] has insufficient access to view this prefab.
- :param user_id: The Google ID of the user.
+ :param user_id: The user ID of the user.
"""
if self.obj['authorId'] != user_id and self.obj['visibility'] == "private":
- raise ClientError(Response(403, "Forbidden from retrieving prefab."))
+ raise Forbidden("Forbidden from retrieving prefab.")
diff --git a/opendc-web/opendc-web-api/opendc/models/project.py b/opendc-web/opendc-web-api/opendc/models/project.py
index 2b3fd5f4..ee84c73e 100644
--- a/opendc-web/opendc-web-api/opendc/models/project.py
+++ b/opendc-web/opendc-web-api/opendc/models/project.py
@@ -1,7 +1,20 @@
+from marshmallow import Schema, fields
+from werkzeug.exceptions import Forbidden
+
from opendc.models.model import Model
-from opendc.util.database import DB
-from opendc.util.exceptions import ClientError
-from opendc.util.rest import Response
+from opendc.exts import db
+
+
+class ProjectSchema(Schema):
+ """
+ Schema representing a Project.
+ """
+ _id = fields.String()
+ name = fields.String(required=True)
+ datetimeCreated = fields.DateTime()
+ datetimeLastEdited = fields.DateTime()
+ topologyIds = fields.List(fields.String())
+ portfolioIds = fields.List(fields.String())
class Project(Model):
@@ -16,13 +29,11 @@ class Project(Model):
:param edit_access: True when edit access should be checked, otherwise view access.
"""
for authorization in self.obj['authorizations']:
- if user_id == authorization['userId'] and authorization['authorizationLevel'] != 'VIEW' or not edit_access:
+ if user_id == authorization['userId'] and authorization['level'] != 'VIEW' or not edit_access:
return
- raise ClientError(Response(403, "Forbidden from retrieving project."))
+ raise Forbidden("Forbidden from retrieving project.")
@classmethod
def get_for_user(cls, user_id):
"""Get all projects for the specified user id."""
- return DB.fetch_all({'authorizations': {
- 'userId': user_id
- }}, Project.collection_name)
+ return db.fetch_all({'authorizations.userId': user_id}, Project.collection_name)
diff --git a/opendc-web/opendc-web-api/opendc/models/scenario.py b/opendc-web/opendc-web-api/opendc/models/scenario.py
index 3dfde012..2911b1ae 100644
--- a/opendc-web/opendc-web-api/opendc/models/scenario.py
+++ b/opendc-web/opendc-web-api/opendc/models/scenario.py
@@ -1,7 +1,52 @@
+from marshmallow import Schema, fields
from opendc.models.model import Model
from opendc.models.portfolio import Portfolio
+class SimulationSchema(Schema):
+ """
+ Simulation details.
+ """
+ state = fields.String()
+
+
+class TraceSchema(Schema):
+ """
+ Schema for specifying the trace of a scenario.
+ """
+ traceId = fields.String()
+ loadSamplingFraction = fields.Float()
+
+
+class TopologySchema(Schema):
+ """
+ Schema for topology specification for a scenario.
+ """
+ topologyId = fields.String()
+
+
+class OperationalSchema(Schema):
+ """
+ Schema for the operational phenomena for a scenario.
+ """
+ failuresEnabled = fields.Boolean()
+ performanceInterferenceEnabled = fields.Boolean()
+ schedulerName = fields.String()
+
+
+class ScenarioSchema(Schema):
+ """
+ Schema representing a scenario.
+ """
+ _id = fields.String()
+ portfolioId = fields.String()
+ name = fields.String(required=True)
+ simulation = fields.Nested(SimulationSchema)
+ trace = fields.Nested(TraceSchema)
+ topology = fields.Nested(TopologySchema)
+ operational = fields.Nested(OperationalSchema)
+
+
class Scenario(Model):
"""Model representing a Scenario."""
@@ -16,5 +61,4 @@ class Scenario(Model):
:param edit_access: True when edit access should be checked, otherwise view access.
"""
portfolio = Portfolio.from_id(self.obj['portfolioId'])
- print(portfolio.obj)
portfolio.check_user_access(user_id, edit_access)
diff --git a/opendc-web/opendc-web-api/opendc/models/topology.py b/opendc-web/opendc-web-api/opendc/models/topology.py
index 3ebec16d..c6354ae6 100644
--- a/opendc-web/opendc-web-api/opendc/models/topology.py
+++ b/opendc-web/opendc-web-api/opendc/models/topology.py
@@ -1,7 +1,83 @@
+from marshmallow import Schema, fields
+
from opendc.models.project import Project
from opendc.models.model import Model
+class MemorySchema(Schema):
+ """
+ Schema representing a memory unit.
+ """
+ _id = fields.String()
+ name = fields.String()
+ speedMbPerS = fields.Integer()
+ sizeMb = fields.Integer()
+ energyConsumptionW = fields.Integer()
+
+
+class PuSchema(Schema):
+ """
+ Schema representing a processing unit.
+ """
+ _id = fields.String()
+ name = fields.String()
+ clockRateMhz = fields.Integer()
+ numberOfCores = fields.Integer()
+ energyConsumptionW = fields.Integer()
+
+
+class MachineSchema(Schema):
+ """
+ Schema representing a machine.
+ """
+ _id = fields.String()
+ position = fields.Integer()
+ cpus = fields.List(fields.Nested(PuSchema))
+ gpus = fields.List(fields.Nested(PuSchema))
+ memories = fields.List(fields.Nested(MemorySchema))
+ storages = fields.List(fields.Nested(MemorySchema))
+
+
+class ObjectSchema(Schema):
+ """
+ Schema representing a room object.
+ """
+ _id = fields.String()
+ name = fields.String()
+ capacity = fields.Integer()
+ powerCapacityW = fields.Integer()
+ machines = fields.List(fields.Nested(MachineSchema))
+
+
+class TileSchema(Schema):
+ """
+ Schema representing a room tile.
+ """
+ _id = fields.String()
+ positionX = fields.Integer()
+ positionY = fields.Integer()
+ rack = fields.Nested(ObjectSchema)
+
+
+class RoomSchema(Schema):
+ """
+ Schema representing a room.
+ """
+ _id = fields.String()
+ name = fields.String(required=True)
+ tiles = fields.List(fields.Nested(TileSchema), required=True)
+
+
+class TopologySchema(Schema):
+ """
+ Schema representing a datacenter topology.
+ """
+ _id = fields.String()
+ projectId = fields.String()
+ name = fields.String(required=True)
+ rooms = fields.List(fields.Nested(RoomSchema), required=True)
+
+
class Topology(Model):
"""Model representing a Project."""