From 53a3b766e2fdfe07a41dc161f9c53f16ecabf3bd Mon Sep 17 00:00:00 2001 From: Fabio Verboso Date: Thu, 15 Feb 2018 11:15:27 +0100 Subject: [PATCH] API services/restore New API for restoring all the services on a board. Change-Id: Iceac65b1672a90d8662de6ef4df8ce51980339c8 --- iotronic/api/controllers/v1/board.py | 26 ++++++- iotronic/common/exception.py | 4 ++ iotronic/conductor/endpoints.py | 104 ++++++++++++--------------- iotronic/conductor/rpcapi.py | 13 ++++ iotronic/db/api.py | 2 +- iotronic/db/sqlalchemy/api.py | 6 +- iotronic/objects/exposedservice.py | 20 +++--- 7 files changed, 100 insertions(+), 75 deletions(-) diff --git a/iotronic/api/controllers/v1/board.py b/iotronic/api/controllers/v1/board.py index 054e7a3..d316be8 100644 --- a/iotronic/api/controllers/v1/board.py +++ b/iotronic/api/controllers/v1/board.py @@ -322,8 +322,10 @@ class BoardPluginsController(rest.RestController): class BoardServicesController(rest.RestController): + _custom_actions = { 'action': ['POST'], + 'restore': ['GET'] } def __init__(self, board_ident): @@ -358,9 +360,6 @@ class BoardServicesController(rest.RestController): raise exception.MissingParameterValue( ("Action is not specified.")) - if not ServiceAction.parameters: - ServiceAction.parameters = {} - rpc_board = api_utils.get_rpc_board(self.board_ident) rpc_service = api_utils.get_rpc_service(service_ident) @@ -380,6 +379,27 @@ class BoardServicesController(rest.RestController): ServiceAction.action) return result + @expose.expose(ExposedCollection, + status_code=200) + def restore(self): + rpc_board = api_utils.get_rpc_board(self.board_ident) + + try: + cdict = pecan.request.context.to_policy_values() + cdict['owner'] = rpc_board.owner + policy.authorize('iot:service_action:post', cdict, cdict) + + except exception: + return exception + + rpc_board.check_if_online() + + pecan.request.rpcapi.restore_services_on_board( + pecan.request.context, + rpc_board.uuid) + + return self._get_services_on_board_collection(rpc_board.uuid) + class BoardsController(rest.RestController): """REST controller for Boards.""" diff --git a/iotronic/common/exception.py b/iotronic/common/exception.py index 76d24d2..8309149 100644 --- a/iotronic/common/exception.py +++ b/iotronic/common/exception.py @@ -609,3 +609,7 @@ class ServiceAlreadyExposed(Conflict): class ExposedServiceNotFound(NotFound): message = _("ExposedService %(uuid)s could not be found.") + + +class NoExposedServices(NotFound): + message = _("No exposed services on the board %(uuid)s.") diff --git a/iotronic/conductor/endpoints.py b/iotronic/conductor/endpoints.py index 5e10c49..14a4518 100644 --- a/iotronic/conductor/endpoints.py +++ b/iotronic/conductor/endpoints.py @@ -305,12 +305,12 @@ class ConductorEndpoint(object): return serializer.serialize_entity(ctx, service) def action_service(self, ctx, service_uuid, board_uuid, action): - LOG.info('Enable service with id %s into the board %s', - service_uuid, board_uuid) service = objects.Service.get(ctx, service_uuid) objects.service.is_valid_action(action) if action == "ServiceEnable": + LOG.info('Enabling service with id %s into the board %s', + service_uuid, board_uuid) try: objects.ExposedService.get(ctx, board_uuid, @@ -348,6 +348,8 @@ class ConductorEndpoint(object): return res.message elif action == "ServiceDisable": + LOG.info('Disabling service with id %s into the board %s', + service_uuid, board_uuid) exposed = objects.ExposedService.get(ctx, board_uuid, service_uuid) @@ -361,12 +363,11 @@ class ConductorEndpoint(object): return result elif action == "ServiceRestore": - + LOG.info('Restoring service with id %s into the board %s', + service_uuid, board_uuid) exposed = objects.ExposedService.get(ctx, board_uuid, service_uuid) - print(exposed) - res = self.execute_on_board(ctx, board_uuid, action, (service.name, exposed.public_port, service.port, exposed.pid)) @@ -396,58 +397,41 @@ class ConductorEndpoint(object): LOG.debug(res.message) return res.message - # try: - # - # - # return exception.ServiceAlreadyExposed(uuid=service_uuid) - # except: - # name=service.name - # public_port=random_public_port() - # port=service.port - # - # res = self.execute_on_board(ctx, board_uuid, action, - # (name, public_port, port)) - # - # if res.result == wm.SUCCESS: - # pid = res.message[0] - # - # exp_data = { - # 'board_uuid': board_uuid, - # 'service_uuid': service_uuid, - # 'public_port': public_port, - # 'pid': pid, - # } - # exposed = objects.ExposedService(ctx, **exp_data) - # exposed.create() - # - # res.message = res.message[1] - # elif res.result == wm.ERROR: - # LOG.error('Error in the execution of %s on %s: %s', - # action, - # board_uuid, res.message) - # raise exception.ErrorExecutionOnBoard(call=action, - # board=board_uuid, - # error=res.message) - # LOG.debug(res.message) - # return res.message - # - # - # - # - # - # - # - # - # - # - # - # exposed = objects.ExposedService.get(ctx, board_uuid, - # service_uuid) - # - # res = self.execute_on_board(ctx, board_uuid, action, - # (service.name, exposed.pid)) - # - # result=manage_result(res,action,board_uuid) - # LOG.debug(res.message) - # exposed.destroy() - # return result + def restore_services_on_board(self, ctx, board_uuid): + LOG.info('Restoring the services into the board %s', + board_uuid) + + exposed_list = objects.ExposedService.get_by_board_uuid(ctx, + board_uuid) + + # response = [] + for exposed in exposed_list: + service = objects.Service.get_by_uuid(ctx, exposed.service_uuid) + res = self.execute_on_board(ctx, board_uuid, "ServiceRestore", + (service.name, exposed.public_port, + service.port, exposed.pid)) + + if res.result == wm.SUCCESS: + pid = res.message[0] + + exp_data = { + 'id': exposed.id, + 'board_uuid': exposed.board_uuid, + 'service_uuid': exposed.service_uuid, + 'public_port': exposed.public_port, + 'pid': pid, + } + + exposed = objects.ExposedService(ctx, **exp_data) + exposed.save() + + # response.append(exposed) + elif res.result == wm.ERROR: + LOG.error('Error in restoring %s on %s: %s', + service.name, + board_uuid, res.message) + raise exception.ErrorExecutionOnBoard(call="ServiceRestore", + board=board_uuid, + error=res.message) + + return 0 diff --git a/iotronic/conductor/rpcapi.py b/iotronic/conductor/rpcapi.py index 223c993..6dfebfd 100644 --- a/iotronic/conductor/rpcapi.py +++ b/iotronic/conductor/rpcapi.py @@ -263,3 +263,16 @@ class ConductorAPI(object): return cctxt.call(context, 'action_service', service_uuid=service_uuid, board_uuid=board_uuid, action=action) + + def restore_services_on_board(self, context, + board_uuid, topic=None): + """Restore all the services on a board. + + :param context: request context. + :param board_uuid: board id or uuid. + + """ + cctxt = self.client.prepare(topic=topic or self.topic, version='1.0') + + return cctxt.call(context, 'restore_services_on_board', + board_uuid=board_uuid) diff --git a/iotronic/db/api.py b/iotronic/db/api.py index 82fafff..dfdf4d2 100644 --- a/iotronic/db/api.py +++ b/iotronic/db/api.py @@ -475,7 +475,7 @@ class Connection(object): """ @abc.abstractmethod - def get_exposed_service_by_board_uuid(self, board_uuid): + def get_exposed_services_by_board_uuid(self, board_uuid): """get an exposed of a service using a board_uuid :param board_uuid: The id or uuid of a board. diff --git a/iotronic/db/sqlalchemy/api.py b/iotronic/db/sqlalchemy/api.py index 275f674..76f9d80 100644 --- a/iotronic/db/sqlalchemy/api.py +++ b/iotronic/db/sqlalchemy/api.py @@ -764,14 +764,14 @@ class Connection(api.Connection): # EXPOSED SERVICE api - def get_exposed_service_by_board_uuid(self, board_uuid): + def get_exposed_services_by_board_uuid(self, board_uuid): query = model_query( models.ExposedService).filter_by( board_uuid=board_uuid) try: - return query.one() + return query.all() except NoResultFound: - raise exception.ExposedServiceNotFound() + raise exception.NoExposedServices(uuid=board_uuid) def create_exposed_service(self, values): # ensure defaults are present for new services diff --git a/iotronic/objects/exposedservice.py b/iotronic/objects/exposedservice.py index 7e96cda..3396545 100644 --- a/iotronic/objects/exposedservice.py +++ b/iotronic/objects/exposedservice.py @@ -55,16 +55,20 @@ class ExposedService(base.IotronicObject): @base.remotable_classmethod def get_by_board_uuid(cls, context, board_uuid): - """Find a exposed_service based on uuid and return a Board object. + """Return a list of ExposedService objects. + + :param context: Security context. + :param limit: maximum number of resources to return in a single result. + :param marker: pagination marker for large data sets. + :param sort_key: column to sort results by. + :param sort_dir: direction to sort. "asc" or "desc". + :param filters: Filters to apply. + :returns: a list of :class:`ExposedService` object. - :param board_uuid: the uuid of a exposed_service. - :returns: a :class:`exposed_service` object. """ - db_exp_service = cls.dbapi.get_exposed_service_by_board_uuid( - board_uuid) - exp_service = ExposedService._from_db_object(cls(context), - db_exp_service) - return exp_service + db_exps = cls.dbapi.get_exposed_services_by_board_uuid(board_uuid) + return [ExposedService._from_db_object(cls(context), obj) + for obj in db_exps] @base.remotable_classmethod def get_by_service_uuid(cls, context, service_uuid):