From 3c351e80ced88a12f49beb45a593f8401122b85f Mon Sep 17 00:00:00 2001 From: Fabio Verboso Date: Fri, 21 Sep 2018 14:41:13 +0200 Subject: [PATCH] "Boards in a fleet" Now a board can join a fleet Change-Id: I5f553421929e72dd1871cce1783043e5e38865f1 --- iotronic/api/controllers/v1/board.py | 4 +- iotronic/api/controllers/v1/fleet.py | 85 +++++++++---------- .../9c5c34dfd9f1_add_boards_in_fleet.py | 24 ++++++ iotronic/db/sqlalchemy/api.py | 2 + iotronic/db/sqlalchemy/models.py | 1 + iotronic/objects/board.py | 1 + 6 files changed, 72 insertions(+), 45 deletions(-) create mode 100644 iotronic/db/sqlalchemy/alembic/versions/9c5c34dfd9f1_add_boards_in_fleet.py diff --git a/iotronic/api/controllers/v1/board.py b/iotronic/api/controllers/v1/board.py index f479413..273e2be 100644 --- a/iotronic/api/controllers/v1/board.py +++ b/iotronic/api/controllers/v1/board.py @@ -30,7 +30,8 @@ from oslo_log import log as logging LOG = logging.getLogger(__name__) -_DEFAULT_RETURN_FIELDS = ('name', 'code', 'status', 'uuid', 'session', 'type') +_DEFAULT_RETURN_FIELDS = ('name', 'code', 'status', 'uuid', 'session', 'type', + 'fleet') class Board(base.APIBase): @@ -45,6 +46,7 @@ class Board(base.APIBase): owner = types.uuid session = wsme.wsattr(wtypes.text) project = types.uuid + fleet = types.uuid mobile = types.boolean links = wsme.wsattr([link.Link], readonly=True) location = wsme.wsattr([loc.Location]) diff --git a/iotronic/api/controllers/v1/fleet.py b/iotronic/api/controllers/v1/fleet.py index a36b291..9ae45df 100644 --- a/iotronic/api/controllers/v1/fleet.py +++ b/iotronic/api/controllers/v1/fleet.py @@ -13,6 +13,7 @@ from iotronic.api.controllers import base from iotronic.api.controllers import link +from iotronic.api.controllers.v1.board import BoardCollection from iotronic.api.controllers.v1 import collection from iotronic.api.controllers.v1 import types from iotronic.api.controllers.v1 import utils as api_utils @@ -29,6 +30,9 @@ from wsme import types as wtypes _DEFAULT_RETURN_FIELDS = ( 'name', 'uuid', 'project', 'description', 'extra') +_DEFAULT_BOARDS_RETURN_FIELDS = ('name', 'code', 'status', 'uuid', 'session', + 'type', 'fleet') + class Fleet(base.APIBase): """API representation of a fleet. @@ -94,48 +98,16 @@ class FleetCollection(collection.Collection): return collection -class PublicFleetsController(rest.RestController): - """REST controller for Public Fleets.""" +class FleetBoardsController(rest.RestController): + def __init__(self, fleet_ident): + self.fleet_ident = fleet_ident - invalid_sort_key_list = ['extra'] - - def _get_fleets_collection(self, marker, limit, - sort_key, sort_dir, - fields=None): - - limit = api_utils.validate_limit(limit) - sort_dir = api_utils.validate_sort_dir(sort_dir) - - marker_obj = None - if marker: - marker_obj = objects.Fleet.get_by_uuid(pecan.request.context, - marker) - - if sort_key in self.invalid_sort_key_list: - raise exception.InvalidParameterValue( - ("The sort_key value %(key)s is an invalid field for " - "sorting") % {'key': sort_key}) - - filters = {} - filters['public'] = True - - fleets = objects.Fleet.list(pecan.request.context, limit, - marker_obj, - sort_key=sort_key, sort_dir=sort_dir, - filters=filters) - - parameters = {'sort_key': sort_key, 'sort_dir': sort_dir} - - return FleetCollection.convert_with_links(fleets, limit, - fields=fields, - **parameters) - - @expose.expose(FleetCollection, types.uuid, int, wtypes.text, - wtypes.text, types.listtype, types.boolean, types.boolean) + @expose.expose(BoardCollection, types.uuid, int, wtypes.text, + wtypes.text, types.listtype) def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc', fields=None): - """Retrieve a list of fleets. + """Retrieve a list of boards. :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. @@ -148,19 +120,31 @@ class PublicFleetsController(rest.RestController): of the resource to be returned. """ cdict = pecan.request.context.to_policy_values() - policy.authorize('iot:fleet:get', cdict, cdict) + policy.authorize('iot:board:get', cdict, cdict) if fields is None: - fields = _DEFAULT_RETURN_FIELDS - return self._get_fleets_collection(marker, - limit, sort_key, sort_dir, - fields=fields) + fields = _DEFAULT_BOARDS_RETURN_FIELDS + + filters = {} + filters['fleet'] = self.fleet_ident + + boards = objects.Board.list(pecan.request.context, limit, marker, + sort_key=sort_key, sort_dir=sort_dir, + filters=filters) + + parameters = {'sort_key': sort_key, 'sort_dir': sort_dir} + + return BoardCollection.convert_with_links(boards, limit, + fields=fields, + **parameters) class FleetsController(rest.RestController): """REST controller for Fleets.""" - public = PublicFleetsController() + _subcontroller_map = { + 'boards': FleetBoardsController, + } invalid_sort_key_list = ['extra', ] @@ -168,6 +152,19 @@ class FleetsController(rest.RestController): 'detail': ['GET'], } + @pecan.expose() + def _lookup(self, ident, *remainder): + try: + ident = types.uuid_or_name.validate(ident) + except exception.InvalidUuidOrName as e: + pecan.abort('400', e.args[0]) + if not remainder: + return + + subcontroller = self._subcontroller_map.get(remainder[0]) + if subcontroller: + return subcontroller(fleet_ident=ident), remainder[1:] + def _get_fleets_collection(self, marker, limit, sort_key, sort_dir, fields=None): diff --git a/iotronic/db/sqlalchemy/alembic/versions/9c5c34dfd9f1_add_boards_in_fleet.py b/iotronic/db/sqlalchemy/alembic/versions/9c5c34dfd9f1_add_boards_in_fleet.py new file mode 100644 index 0000000..afa4f81 --- /dev/null +++ b/iotronic/db/sqlalchemy/alembic/versions/9c5c34dfd9f1_add_boards_in_fleet.py @@ -0,0 +1,24 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +# revision identifiers, used by Alembic. +revision = '9c5c34dfd9f1' +down_revision = '44e74b9170d1' + +from alembic import op + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_foreign_key(None, 'boards', 'fleets', ['fleet'], ['uuid']) + # ### end Alembic commands ### diff --git a/iotronic/db/sqlalchemy/api.py b/iotronic/db/sqlalchemy/api.py index a9a96ca..068a71f 100644 --- a/iotronic/db/sqlalchemy/api.py +++ b/iotronic/db/sqlalchemy/api.py @@ -131,6 +131,8 @@ class Connection(api.Connection): query = query.filter(models.Board.project == filters['project_id']) if 'status' in filters: query = query.filter(models.Board.status == filters['status']) + if 'fleet' in filters: + query = query.filter(models.Board.fleet == filters['fleet']) return query diff --git a/iotronic/db/sqlalchemy/models.py b/iotronic/db/sqlalchemy/models.py index 351d411..d585a12 100644 --- a/iotronic/db/sqlalchemy/models.py +++ b/iotronic/db/sqlalchemy/models.py @@ -154,6 +154,7 @@ class Board(Base): agent = Column(String(255), nullable=True) owner = Column(String(36)) project = Column(String(36)) + fleet = Column(String(36), ForeignKey('fleets.uuid'), nullable=True) mobile = Column(Boolean, default=False) config = Column(JSONEncodedDict) extra = Column(JSONEncodedDict) diff --git a/iotronic/objects/board.py b/iotronic/objects/board.py index 5d41065..fe0e6d2 100644 --- a/iotronic/objects/board.py +++ b/iotronic/objects/board.py @@ -39,6 +39,7 @@ class Board(base.IotronicObject): 'agent': obj_utils.str_or_none, 'owner': obj_utils.str_or_none, 'project': obj_utils.str_or_none, + 'fleet': obj_utils.str_or_none, 'mobile': bool, 'config': obj_utils.dict_or_none, 'extra': obj_utils.dict_or_none,