diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-10-26 16:19:55 +0200 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2022-04-04 12:48:04 +0200 |
| commit | f0c472b1792779e63fdeb97a470b46300de00050 (patch) | |
| tree | 99646cf4448f4f73c2c98ede338df19a1497f9b5 /opendc-web/opendc-web-api/opendc/api | |
| parent | 8f958c5a578dc11b890c96c0dc48e3e3f92a4d07 (diff) | |
feat(web/api): Initial API implementation in Kotlin
This change adds the initial implementation of the new API server in Kotlin,
replacing the old API written in Python. The implementation uses Quarkus,
RESTEasy, and Hibernate to implement the new API endpoints.
The reason for replacing the old API server is unifying the build and
deployment toolchains, reducing the number of technologies necessary to
work with OpenDC. Furthermore, we envision bundling the entire OpenDC
project into a single distributions, allowing users to launch their own
deployment trivially.
Diffstat (limited to 'opendc-web/opendc-web-api/opendc/api')
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/__init__.py | 0 | ||||
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/jobs.py | 105 | ||||
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/portfolios.py | 153 | ||||
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/prefabs.py | 123 | ||||
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/projects.py | 224 | ||||
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/scenarios.py | 86 | ||||
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/schedulers.py | 46 | ||||
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/topologies.py | 97 | ||||
| -rw-r--r-- | opendc-web/opendc-web-api/opendc/api/traces.py | 51 |
9 files changed, 0 insertions, 885 deletions
diff --git a/opendc-web/opendc-web-api/opendc/api/__init__.py b/opendc-web/opendc-web-api/opendc/api/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/opendc-web/opendc-web-api/opendc/api/__init__.py +++ /dev/null diff --git a/opendc-web/opendc-web-api/opendc/api/jobs.py b/opendc-web/opendc-web-api/opendc/api/jobs.py deleted file mode 100644 index 6fb0522b..00000000 --- a/opendc-web/opendc-web-api/opendc/api/jobs.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) 2021 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -from flask import request -from flask_restful import Resource -from marshmallow import fields, Schema, validate -from werkzeug.exceptions import BadRequest, Conflict - -from opendc.exts import requires_auth, requires_scope -from opendc.models.scenario import Scenario - - -def convert_to_job(scenario): - """Convert a scenario to a job. - """ - return JobSchema().dump({ - '_id': scenario['_id'], - 'scenarioId': scenario['_id'], - 'state': scenario['simulation']['state'], - 'heartbeat': scenario['simulation'].get('heartbeat', None), - 'results': scenario.get('results', {}) - }) - - -class JobSchema(Schema): - """ - Schema representing a simulation job. - """ - _id = fields.String(dump_only=True) - scenarioId = fields.String(dump_only=True) - state = fields.String(required=True, - validate=validate.OneOf(["QUEUED", "CLAIMED", "RUNNING", "FINISHED", "FAILED"])) - heartbeat = fields.DateTime() - results = fields.Dict() - - -class JobList(Resource): - """ - Resource representing the list of available jobs. - """ - method_decorators = [requires_auth, requires_scope('runner')] - - def get(self): - """Get all available jobs.""" - jobs = Scenario.get_jobs() - data = list(map(convert_to_job, jobs.obj)) - return {'data': data} - - -class Job(Resource): - """ - Resource representing a single job. - """ - method_decorators = [requires_auth, requires_scope('runner')] - - def get(self, job_id): - """Get the details of a single job.""" - job = Scenario.from_id(job_id) - job.check_exists() - data = convert_to_job(job.obj) - return {'data': data} - - def post(self, job_id): - """Update the details of a single job.""" - action = JobSchema(only=('state', 'results')).load(request.json) - - job = Scenario.from_id(job_id) - job.check_exists() - - old_state = job.obj['simulation']['state'] - new_state = action['state'] - - if old_state == new_state: - data = job.update_state(new_state) - elif (old_state, new_state) == ('QUEUED', 'CLAIMED'): - data = job.update_state('CLAIMED') - elif (old_state, new_state) == ('CLAIMED', 'RUNNING'): - data = job.update_state('RUNNING') - elif (old_state, new_state) == ('RUNNING', 'FINISHED'): - data = job.update_state('FINISHED', results=action.get('results', None)) - elif old_state in ('CLAIMED', 'RUNNING') and new_state == 'FAILED': - data = job.update_state('FAILED') - else: - raise BadRequest('Invalid state transition') - - if not data: - raise Conflict('State conflict') - - return {'data': convert_to_job(data)} diff --git a/opendc-web/opendc-web-api/opendc/api/portfolios.py b/opendc-web/opendc-web-api/opendc/api/portfolios.py deleted file mode 100644 index 4d8f54fd..00000000 --- a/opendc-web/opendc-web-api/opendc/api/portfolios.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright (c) 2021 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from flask import request -from flask_restful import Resource -from marshmallow import Schema, fields - -from opendc.exts import requires_auth, current_user, has_scope -from opendc.models.portfolio import Portfolio as PortfolioModel, PortfolioSchema -from opendc.models.project import Project -from opendc.models.scenario import ScenarioSchema, Scenario -from opendc.models.topology import Topology - - -class Portfolio(Resource): - """ - Resource representing a portfolio. - """ - method_decorators = [requires_auth] - - def get(self, portfolio_id): - """ - Get a portfolio by identifier. - """ - portfolio = PortfolioModel.from_id(portfolio_id) - - portfolio.check_exists() - - # Users with scope runner can access all portfolios - if not has_scope('runner'): - portfolio.check_user_access(current_user['sub'], False) - - data = PortfolioSchema().dump(portfolio.obj) - return {'data': data} - - def put(self, portfolio_id): - """ - Replace the portfolio. - """ - schema = Portfolio.PutSchema() - result = schema.load(request.json) - - portfolio = PortfolioModel.from_id(portfolio_id) - portfolio.check_exists() - portfolio.check_user_access(current_user['sub'], True) - - portfolio.set_property('name', result['portfolio']['name']) - portfolio.set_property('targets.enabledMetrics', result['portfolio']['targets']['enabledMetrics']) - portfolio.set_property('targets.repeatsPerScenario', result['portfolio']['targets']['repeatsPerScenario']) - - portfolio.update() - data = PortfolioSchema().dump(portfolio.obj) - return {'data': data} - - def delete(self, portfolio_id): - """ - Delete a portfolio. - """ - portfolio = PortfolioModel.from_id(portfolio_id) - - portfolio.check_exists() - portfolio.check_user_access(current_user['sub'], True) - - portfolio_id = portfolio.get_id() - - project = Project.from_id(portfolio.obj['projectId']) - project.check_exists() - if portfolio_id in project.obj['portfolioIds']: - project.obj['portfolioIds'].remove(portfolio_id) - project.update() - - old_object = portfolio.delete() - data = PortfolioSchema().dump(old_object) - return {'data': data} - - class PutSchema(Schema): - """ - Schema for the PUT operation on a portfolio. - """ - portfolio = fields.Nested(PortfolioSchema, required=True) - - -class PortfolioScenarios(Resource): - """ - Resource representing the scenarios of a portfolio. - """ - method_decorators = [requires_auth] - - def get(self, portfolio_id): - """ - Get all scenarios belonging to a portfolio. - """ - portfolio = PortfolioModel.from_id(portfolio_id) - - portfolio.check_exists() - portfolio.check_user_access(current_user['sub'], True) - - scenarios = Scenario.get_for_portfolio(portfolio_id) - - data = ScenarioSchema().dump(scenarios, many=True) - return {'data': data} - - def post(self, portfolio_id): - """ - Add a new scenario to this portfolio - """ - schema = PortfolioScenarios.PostSchema() - result = schema.load(request.json) - - portfolio = PortfolioModel.from_id(portfolio_id) - - portfolio.check_exists() - portfolio.check_user_access(current_user['sub'], True) - - scenario = Scenario(result['scenario']) - - topology = Topology.from_id(scenario.obj['topology']['topologyId']) - topology.check_exists() - topology.check_user_access(current_user['sub'], True) - - scenario.set_property('portfolioId', portfolio.get_id()) - scenario.set_property('simulation', {'state': 'QUEUED'}) - scenario.set_property('topology.topologyId', topology.get_id()) - - scenario.insert() - - portfolio.obj['scenarioIds'].append(scenario.get_id()) - portfolio.update() - data = ScenarioSchema().dump(scenario.obj) - return {'data': data} - - class PostSchema(Schema): - """ - Schema for the POST operation on a portfolio's scenarios. - """ - scenario = fields.Nested(ScenarioSchema, required=True) diff --git a/opendc-web/opendc-web-api/opendc/api/prefabs.py b/opendc-web/opendc-web-api/opendc/api/prefabs.py deleted file mode 100644 index 730546ba..00000000 --- a/opendc-web/opendc-web-api/opendc/api/prefabs.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (c) 2021 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from datetime import datetime -from flask import request -from flask_restful import Resource -from marshmallow import Schema, fields - -from opendc.models.prefab import Prefab as PrefabModel, PrefabSchema -from opendc.exts import current_user, requires_auth, db - - -class PrefabList(Resource): - """ - Resource for the list of prefabs available to the user. - """ - method_decorators = [requires_auth] - - def get(self): - """ - Get the available prefabs for a user. - """ - user_id = current_user['sub'] - - own_prefabs = db.fetch_all({'authorId': user_id}, PrefabModel.collection_name) - public_prefabs = db.fetch_all({'visibility': 'public'}, PrefabModel.collection_name) - - authorizations = {"authorizations": []} - authorizations["authorizations"].append(own_prefabs) - authorizations["authorizations"].append(public_prefabs) - return {'data': authorizations} - - def post(self): - """ - Create a new prefab. - """ - schema = PrefabList.PostSchema() - result = schema.load(request.json) - - prefab = PrefabModel(result['prefab']) - prefab.set_property('datetimeCreated', datetime.now()) - prefab.set_property('datetimeLastEdited', datetime.now()) - - user_id = current_user['sub'] - prefab.set_property('authorId', user_id) - - prefab.insert() - data = PrefabSchema().dump(prefab.obj) - return {'data': data} - - class PostSchema(Schema): - """ - Schema for the POST operation on the prefab list. - """ - prefab = fields.Nested(PrefabSchema, required=True) - - -class Prefab(Resource): - """ - Resource representing a single prefab. - """ - method_decorators = [requires_auth] - - def get(self, prefab_id): - """Get this Prefab.""" - prefab = PrefabModel.from_id(prefab_id) - prefab.check_exists() - prefab.check_user_access(current_user['sub']) - data = PrefabSchema().dump(prefab.obj) - return {'data': data} - - def put(self, prefab_id): - """Update a prefab's name and/or contents.""" - - schema = Prefab.PutSchema() - result = schema.load(request.json) - - prefab = PrefabModel.from_id(prefab_id) - prefab.check_exists() - prefab.check_user_access(current_user['sub']) - - prefab.set_property('name', result['prefab']['name']) - prefab.set_property('rack', result['prefab']['rack']) - prefab.set_property('datetimeLastEdited', datetime.now()) - prefab.update() - - data = PrefabSchema().dump(prefab.obj) - return {'data': data} - - def delete(self, prefab_id): - """Delete this Prefab.""" - prefab = PrefabModel.from_id(prefab_id) - - prefab.check_exists() - prefab.check_user_access(current_user['sub']) - - old_object = prefab.delete() - - data = PrefabSchema().dump(old_object) - return {'data': data} - - class PutSchema(Schema): - """ - Schema for the PUT operation on a prefab. - """ - prefab = fields.Nested(PrefabSchema, required=True) diff --git a/opendc-web/opendc-web-api/opendc/api/projects.py b/opendc-web/opendc-web-api/opendc/api/projects.py deleted file mode 100644 index 2b47c12e..00000000 --- a/opendc-web/opendc-web-api/opendc/api/projects.py +++ /dev/null @@ -1,224 +0,0 @@ -# Copyright (c) 2021 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from datetime import datetime -from flask import request -from flask_restful import Resource -from marshmallow import Schema, fields - -from opendc.models.portfolio import Portfolio, PortfolioSchema -from opendc.models.topology import Topology, TopologySchema -from opendc.models.project import Project as ProjectModel, ProjectSchema -from opendc.exts import current_user, requires_auth - - -class ProjectList(Resource): - """ - Resource representing the list of projects available to a user. - """ - method_decorators = [requires_auth] - - def get(self): - """Get the authorized projects of the user""" - user_id = current_user['sub'] - projects = ProjectModel.get_for_user(user_id) - data = ProjectSchema().dump(projects, many=True) - return {'data': data} - - def post(self): - """Create a new project, and return that new project.""" - user_id = current_user['sub'] - - schema = Project.PutSchema() - result = schema.load(request.json) - - topology = Topology({'name': 'Default topology', 'rooms': []}) - topology.insert() - - project = ProjectModel(result['project']) - project.set_property('datetimeCreated', datetime.now()) - project.set_property('datetimeLastEdited', datetime.now()) - project.set_property('topologyIds', [topology.get_id()]) - project.set_property('portfolioIds', []) - project.set_property('authorizations', [{'userId': user_id, 'level': 'OWN'}]) - project.insert() - - topology.set_property('projectId', project.get_id()) - topology.update() - - data = ProjectSchema().dump(project.obj) - return {'data': data} - - -class Project(Resource): - """ - Resource representing a single project. - """ - method_decorators = [requires_auth] - - def get(self, project_id): - """Get this Project.""" - project = ProjectModel.from_id(project_id) - - project.check_exists() - project.check_user_access(current_user['sub'], False) - - data = ProjectSchema().dump(project.obj) - return {'data': data} - - def put(self, project_id): - """Update a project's name.""" - schema = Project.PutSchema() - result = schema.load(request.json) - - project = ProjectModel.from_id(project_id) - - project.check_exists() - project.check_user_access(current_user['sub'], True) - - project.set_property('name', result['project']['name']) - project.set_property('datetimeLastEdited', datetime.now()) - project.update() - - data = ProjectSchema().dump(project.obj) - return {'data': data} - - def delete(self, project_id): - """Delete this Project.""" - project = ProjectModel.from_id(project_id) - - project.check_exists() - project.check_user_access(current_user['sub'], True) - - for topology_id in project.obj['topologyIds']: - topology = Topology.from_id(topology_id) - topology.delete() - - for portfolio_id in project.obj['portfolioIds']: - portfolio = Portfolio.from_id(portfolio_id) - portfolio.delete() - - old_object = project.delete() - data = ProjectSchema().dump(old_object) - return {'data': data} - - class PutSchema(Schema): - """ - Schema for the PUT operation on a project. - """ - project = fields.Nested(ProjectSchema, required=True) - - -class ProjectTopologies(Resource): - """ - Resource representing the topologies of a project. - """ - method_decorators = [requires_auth] - - def get(self, project_id): - """Get all topologies belonging to the project.""" - project = ProjectModel.from_id(project_id) - - project.check_exists() - project.check_user_access(current_user['sub'], True) - - topologies = Topology.get_for_project(project_id) - data = TopologySchema().dump(topologies, many=True) - - return {'data': data} - - def post(self, project_id): - """Add a new Topology to the specified project and return it""" - schema = ProjectTopologies.PutSchema() - result = schema.load(request.json) - - project = ProjectModel.from_id(project_id) - - project.check_exists() - project.check_user_access(current_user['sub'], True) - - topology = Topology({ - 'projectId': project.get_id(), - 'name': result['topology']['name'], - 'rooms': result['topology']['rooms'], - }) - - topology.insert() - - project.obj['topologyIds'].append(topology.get_id()) - project.set_property('datetimeLastEdited', datetime.now()) - project.update() - - data = TopologySchema().dump(topology.obj) - return {'data': data} - - class PutSchema(Schema): - """ - Schema for the PUT operation on a project topology. - """ - topology = fields.Nested(TopologySchema, required=True) - - -class ProjectPortfolios(Resource): - """ - Resource representing the portfolios of a project. - """ - method_decorators = [requires_auth] - - def get(self, project_id): - """Get all portfolios belonging to the project.""" - project = ProjectModel.from_id(project_id) - - project.check_exists() - project.check_user_access(current_user['sub'], True) - - portfolios = Portfolio.get_for_project(project_id) - data = PortfolioSchema().dump(portfolios, many=True) - - return {'data': data} - - def post(self, project_id): - """Add a new Portfolio for this Project.""" - schema = ProjectPortfolios.PutSchema() - result = schema.load(request.json) - - project = ProjectModel.from_id(project_id) - - project.check_exists() - project.check_user_access(current_user['sub'], True) - - portfolio = Portfolio(result['portfolio']) - - portfolio.set_property('projectId', project.get_id()) - portfolio.set_property('scenarioIds', []) - - portfolio.insert() - - project.obj['portfolioIds'].append(portfolio.get_id()) - project.update() - - data = PortfolioSchema().dump(portfolio.obj) - return {'data': data} - - class PutSchema(Schema): - """ - Schema for the PUT operation on a project portfolio. - """ - portfolio = fields.Nested(PortfolioSchema, required=True) diff --git a/opendc-web/opendc-web-api/opendc/api/scenarios.py b/opendc-web/opendc-web-api/opendc/api/scenarios.py deleted file mode 100644 index eacb0b49..00000000 --- a/opendc-web/opendc-web-api/opendc/api/scenarios.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2021 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from flask import request -from flask_restful import Resource -from marshmallow import Schema, fields - -from opendc.models.scenario import Scenario as ScenarioModel, ScenarioSchema -from opendc.models.portfolio import Portfolio -from opendc.exts import current_user, requires_auth, has_scope - - -class Scenario(Resource): - """ - A Scenario resource. - """ - method_decorators = [requires_auth] - - def get(self, scenario_id): - """Get scenario by identifier.""" - scenario = ScenarioModel.from_id(scenario_id) - scenario.check_exists() - - # Users with scope runner can access all scenarios - if not has_scope('runner'): - scenario.check_user_access(current_user['sub'], False) - - data = ScenarioSchema().dump(scenario.obj) - return {'data': data} - - def put(self, scenario_id): - """Update this Scenarios name.""" - schema = Scenario.PutSchema() - result = schema.load(request.json) - - scenario = ScenarioModel.from_id(scenario_id) - - scenario.check_exists() - scenario.check_user_access(current_user['sub'], True) - - scenario.set_property('name', result['scenario']['name']) - - scenario.update() - data = ScenarioSchema().dump(scenario.obj) - return {'data': data} - - def delete(self, scenario_id): - """Delete this Scenario.""" - scenario = ScenarioModel.from_id(scenario_id) - scenario.check_exists() - scenario.check_user_access(current_user['sub'], True) - - scenario_id = scenario.get_id() - - portfolio = Portfolio.from_id(scenario.obj['portfolioId']) - portfolio.check_exists() - if scenario_id in portfolio.obj['scenarioIds']: - portfolio.obj['scenarioIds'].remove(scenario_id) - portfolio.update() - - old_object = scenario.delete() - data = ScenarioSchema().dump(old_object) - return {'data': data} - - class PutSchema(Schema): - """ - Schema for the put operation. - """ - scenario = fields.Nested(ScenarioSchema, required=True) diff --git a/opendc-web/opendc-web-api/opendc/api/schedulers.py b/opendc-web/opendc-web-api/opendc/api/schedulers.py deleted file mode 100644 index b00d8c31..00000000 --- a/opendc-web/opendc-web-api/opendc/api/schedulers.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2021 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - -from flask_restful import Resource -from opendc.exts import requires_auth - -SCHEDULERS = [ - 'mem', - 'mem-inv', - 'core-mem', - 'core-mem-inv', - 'active-servers', - 'active-servers-inv', - 'provisioned-cores', - 'provisioned-cores-inv', - 'random' -] - - -class SchedulerList(Resource): - """ - Resource for the list of schedulers to pick from. - """ - method_decorators = [requires_auth] - - def get(self): - """Get all available Traces.""" - return {'data': [{'name': name} for name in SCHEDULERS]} diff --git a/opendc-web/opendc-web-api/opendc/api/topologies.py b/opendc-web/opendc-web-api/opendc/api/topologies.py deleted file mode 100644 index c0b2e7ee..00000000 --- a/opendc-web/opendc-web-api/opendc/api/topologies.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2021 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from datetime import datetime - -from flask import request -from flask_restful import Resource -from marshmallow import Schema, fields - -from opendc.models.project import Project -from opendc.models.topology import Topology as TopologyModel, TopologySchema -from opendc.exts import current_user, requires_auth, has_scope - - -class Topology(Resource): - """ - Resource representing a single topology. - """ - method_decorators = [requires_auth] - - def get(self, topology_id): - """ - Get a single topology. - """ - topology = TopologyModel.from_id(topology_id) - topology.check_exists() - - # Users with scope runner can access all topologies - if not has_scope('runner'): - topology.check_user_access(current_user['sub'], False) - - data = TopologySchema().dump(topology.obj) - return {'data': data} - - def put(self, topology_id): - """ - Replace the topology. - """ - topology = TopologyModel.from_id(topology_id) - - schema = Topology.PutSchema() - result = schema.load(request.json) - - topology.check_exists() - topology.check_user_access(current_user['sub'], True) - - topology.set_property('name', result['topology']['name']) - topology.set_property('rooms', result['topology']['rooms']) - topology.set_property('datetimeLastEdited', datetime.now()) - - topology.update() - data = TopologySchema().dump(topology.obj) - return {'data': data} - - def delete(self, topology_id): - """ - Delete a topology. - """ - topology = TopologyModel.from_id(topology_id) - - topology.check_exists() - topology.check_user_access(current_user['sub'], True) - - topology_id = topology.get_id() - - project = Project.from_id(topology.obj['projectId']) - project.check_exists() - if topology_id in project.obj['topologyIds']: - project.obj['topologyIds'].remove(topology_id) - project.update() - - old_object = topology.delete() - data = TopologySchema().dump(old_object) - return {'data': data} - - class PutSchema(Schema): - """ - Schema for the PUT operation on a topology. - """ - topology = fields.Nested(TopologySchema, required=True) diff --git a/opendc-web/opendc-web-api/opendc/api/traces.py b/opendc-web/opendc-web-api/opendc/api/traces.py deleted file mode 100644 index 6be8c5e5..00000000 --- a/opendc-web/opendc-web-api/opendc/api/traces.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2021 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from flask_restful import Resource - -from opendc.exts import requires_auth -from opendc.models.trace import Trace as TraceModel, TraceSchema - - -class TraceList(Resource): - """ - Resource for the list of traces to pick from. - """ - method_decorators = [requires_auth] - - def get(self): - """Get all available Traces.""" - traces = TraceModel.get_all() - data = TraceSchema().dump(traces.obj, many=True) - return {'data': data} - - -class Trace(Resource): - """ - Resource representing a single trace. - """ - method_decorators = [requires_auth] - - def get(self, trace_id): - """Get trace information by identifier.""" - trace = TraceModel.from_id(trace_id) - trace.check_exists() - data = TraceSchema().dump(trace.obj) - return {'data': data} |
