Exposed Cloud Service
Iotronic exposes the services of the boards Change-Id: Id88c4e6a9d5752d1bffbcfd99827eca0b9b679f7
This commit is contained in:
parent
6e9e02e9c3
commit
f450f91c70
|
@ -152,11 +152,50 @@ class InjectionCollection(collection.Collection):
|
||||||
return collection
|
return collection
|
||||||
|
|
||||||
|
|
||||||
|
class ExposedService(base.APIBase):
|
||||||
|
service = types.uuid_or_name
|
||||||
|
board_uuid = types.uuid_or_name
|
||||||
|
public_port = wsme.types.IntegerType()
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.fields = []
|
||||||
|
fields = list(objects.ExposedService.fields)
|
||||||
|
fields.remove('board_uuid')
|
||||||
|
for k in fields:
|
||||||
|
# Skip fields we do not expose.
|
||||||
|
if not hasattr(self, k):
|
||||||
|
continue
|
||||||
|
self.fields.append(k)
|
||||||
|
setattr(self, k, kwargs.get(k, wtypes.Unset))
|
||||||
|
setattr(self, 'service', kwargs.get('service_uuid', wtypes.Unset))
|
||||||
|
|
||||||
|
|
||||||
|
class ExposedCollection(collection.Collection):
|
||||||
|
"""API representation of a collection of injection."""
|
||||||
|
|
||||||
|
exposed = [ExposedService]
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self._type = 'exposed'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_list(exposed, fields=None):
|
||||||
|
collection = ExposedCollection()
|
||||||
|
collection.exposed = [ExposedService(**n.as_dict())
|
||||||
|
for n in exposed]
|
||||||
|
return collection
|
||||||
|
|
||||||
|
|
||||||
class PluginAction(base.APIBase):
|
class PluginAction(base.APIBase):
|
||||||
action = wsme.wsattr(wtypes.text)
|
action = wsme.wsattr(wtypes.text)
|
||||||
parameters = types.jsontype
|
parameters = types.jsontype
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAction(base.APIBase):
|
||||||
|
action = wsme.wsattr(wtypes.text)
|
||||||
|
parameters = types.jsontype
|
||||||
|
|
||||||
|
|
||||||
class BoardPluginsController(rest.RestController):
|
class BoardPluginsController(rest.RestController):
|
||||||
def __init__(self, board_ident):
|
def __init__(self, board_ident):
|
||||||
self.board_ident = board_ident
|
self.board_ident = board_ident
|
||||||
|
@ -282,11 +321,72 @@ class BoardPluginsController(rest.RestController):
|
||||||
rpc_board.uuid)
|
rpc_board.uuid)
|
||||||
|
|
||||||
|
|
||||||
|
class BoardServicesController(rest.RestController):
|
||||||
|
_custom_actions = {
|
||||||
|
'action': ['POST'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, board_ident):
|
||||||
|
self.board_ident = board_ident
|
||||||
|
|
||||||
|
def _get_services_on_board_collection(self, board_uuid, fields=None):
|
||||||
|
services = objects.ExposedService.list(pecan.request.context,
|
||||||
|
board_uuid)
|
||||||
|
|
||||||
|
return ExposedCollection.get_list(services,
|
||||||
|
fields=fields)
|
||||||
|
|
||||||
|
@expose.expose(ExposedCollection,
|
||||||
|
status_code=200)
|
||||||
|
def get_all(self):
|
||||||
|
"""Retrieve a list of services of a board.
|
||||||
|
|
||||||
|
"""
|
||||||
|
rpc_board = api_utils.get_rpc_board(self.board_ident)
|
||||||
|
|
||||||
|
cdict = pecan.request.context.to_policy_values()
|
||||||
|
cdict['project_id'] = rpc_board.project
|
||||||
|
policy.authorize('iot:service_on_board:get', cdict, cdict)
|
||||||
|
|
||||||
|
return self._get_services_on_board_collection(rpc_board.uuid)
|
||||||
|
|
||||||
|
@expose.expose(wtypes.text, types.uuid_or_name, body=ServiceAction,
|
||||||
|
status_code=200)
|
||||||
|
def action(self, service_ident, ServiceAction):
|
||||||
|
|
||||||
|
if not ServiceAction.action:
|
||||||
|
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)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
result = pecan.request.rpcapi.action_service(pecan.request.context,
|
||||||
|
rpc_service.uuid,
|
||||||
|
rpc_board.uuid,
|
||||||
|
ServiceAction.action)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class BoardsController(rest.RestController):
|
class BoardsController(rest.RestController):
|
||||||
"""REST controller for Boards."""
|
"""REST controller for Boards."""
|
||||||
|
|
||||||
_subcontroller_map = {
|
_subcontroller_map = {
|
||||||
'plugins': BoardPluginsController,
|
'plugins': BoardPluginsController,
|
||||||
|
'services': BoardServicesController,
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid_sort_key_list = ['extra', 'location']
|
invalid_sort_key_list = ['extra', 'location']
|
||||||
|
|
|
@ -597,3 +597,15 @@ class ErrorExecutionOnBoard(IotronicException):
|
||||||
|
|
||||||
class ServiceNotFound(NotFound):
|
class ServiceNotFound(NotFound):
|
||||||
message = _("Service %(Service)s could not be found.")
|
message = _("Service %(Service)s could not be found.")
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAlreadyExists(Conflict):
|
||||||
|
message = _("A Service with UUID %(uuid)s already exists.")
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAlreadyExposed(Conflict):
|
||||||
|
message = _("A Service with UUID %(uuid)s already exposed.")
|
||||||
|
|
||||||
|
|
||||||
|
class ExposedServiceNotFound(NotFound):
|
||||||
|
message = _("ExposedService %(uuid)s could not be found.")
|
||||||
|
|
|
@ -136,6 +136,20 @@ service_policies = [
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
exposed_service_policies = [
|
||||||
|
policy.RuleDefault('iot:service_on_board:get',
|
||||||
|
'rule:admin_or_owner',
|
||||||
|
description='Retrieve Service records'),
|
||||||
|
policy.RuleDefault('iot:service_remove:delete', 'rule:admin_or_owner',
|
||||||
|
description='Delete Service records'),
|
||||||
|
policy.RuleDefault('iot:service_action:post',
|
||||||
|
'rule:admin_or_owner',
|
||||||
|
description='Create Service records'),
|
||||||
|
policy.RuleDefault('iot:service_inject:put', 'rule:admin_or_owner',
|
||||||
|
description='Retrieve a Service record'),
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def list_policies():
|
def list_policies():
|
||||||
policies = (default_policies
|
policies = (default_policies
|
||||||
|
@ -143,6 +157,7 @@ def list_policies():
|
||||||
+ plugin_policies
|
+ plugin_policies
|
||||||
+ injection_plugin_policies
|
+ injection_plugin_policies
|
||||||
+ service_policies
|
+ service_policies
|
||||||
|
+ exposed_service_policies
|
||||||
)
|
)
|
||||||
return policies
|
return policies
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,26 @@ def get_best_agent(ctx):
|
||||||
return agent.hostname
|
return agent.hostname
|
||||||
|
|
||||||
|
|
||||||
|
def random_public_port():
|
||||||
|
return random.randint(6000, 7000)
|
||||||
|
|
||||||
|
|
||||||
|
def manage_result(res, wamp_rpc_call, board_uuid):
|
||||||
|
if res.result == wm.SUCCESS:
|
||||||
|
return res.message
|
||||||
|
elif res.result == wm.WARNING:
|
||||||
|
LOG.warning('Warning in the execution of %s on %s', wamp_rpc_call,
|
||||||
|
board_uuid)
|
||||||
|
return res.message
|
||||||
|
elif res.result == wm.ERROR:
|
||||||
|
LOG.error('Error in the execution of %s on %s: %s', wamp_rpc_call,
|
||||||
|
board_uuid, res.message)
|
||||||
|
raise exception.ErrorExecutionOnBoard(call=wamp_rpc_call,
|
||||||
|
board=board_uuid,
|
||||||
|
error=res.message)
|
||||||
|
return res.message
|
||||||
|
|
||||||
|
|
||||||
class ConductorEndpoint(object):
|
class ConductorEndpoint(object):
|
||||||
def __init__(self, ragent):
|
def __init__(self, ragent):
|
||||||
transport = oslo_messaging.get_transport(cfg.CONF)
|
transport = oslo_messaging.get_transport(cfg.CONF)
|
||||||
|
@ -119,10 +139,12 @@ class ConductorEndpoint(object):
|
||||||
board_id,
|
board_id,
|
||||||
'destroyBoard',
|
'destroyBoard',
|
||||||
(p,))
|
(p,))
|
||||||
|
|
||||||
except exception:
|
except exception:
|
||||||
return exception
|
return exception
|
||||||
board.destroy()
|
board.destroy()
|
||||||
if result:
|
if result:
|
||||||
|
result = manage_result(result, 'destroyBoard', board_id)
|
||||||
LOG.debug(result)
|
LOG.debug(result)
|
||||||
return result
|
return result
|
||||||
return
|
return
|
||||||
|
@ -164,18 +186,7 @@ class ConductorEndpoint(object):
|
||||||
data=wamp_rpc_args)
|
data=wamp_rpc_args)
|
||||||
res = wm.deserialize(res)
|
res = wm.deserialize(res)
|
||||||
|
|
||||||
if res.result == wm.SUCCESS:
|
return res
|
||||||
return res.message
|
|
||||||
elif res.result == wm.WARNING:
|
|
||||||
LOG.warning('Warning in the execution of %s on %s', wamp_rpc_call,
|
|
||||||
board_uuid)
|
|
||||||
return res.message
|
|
||||||
elif res.result == wm.ERROR:
|
|
||||||
LOG.error('Error in the execution of %s on %s: %s', wamp_rpc_call,
|
|
||||||
board_uuid, res.message)
|
|
||||||
raise exception.ErrorExecutionOnBoard(call=wamp_rpc_call,
|
|
||||||
board=board.uuid,
|
|
||||||
error=res.message)
|
|
||||||
|
|
||||||
def destroy_plugin(self, ctx, plugin_id):
|
def destroy_plugin(self, ctx, plugin_id):
|
||||||
LOG.info('Destroying plugin with id %s',
|
LOG.info('Destroying plugin with id %s',
|
||||||
|
@ -231,7 +242,9 @@ class ConductorEndpoint(object):
|
||||||
injection = objects.InjectionPlugin(ctx, **inj_data)
|
injection = objects.InjectionPlugin(ctx, **inj_data)
|
||||||
injection.create()
|
injection.create()
|
||||||
|
|
||||||
|
result = manage_result(result, 'PluginInject', board_uuid)
|
||||||
LOG.debug(result)
|
LOG.debug(result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def remove_plugin(self, ctx, plugin_uuid, board_uuid):
|
def remove_plugin(self, ctx, plugin_uuid, board_uuid):
|
||||||
|
@ -247,7 +260,7 @@ class ConductorEndpoint(object):
|
||||||
(plugin.uuid,))
|
(plugin.uuid,))
|
||||||
except exception:
|
except exception:
|
||||||
return exception
|
return exception
|
||||||
|
result = manage_result(result, 'PluginRemove', board_uuid)
|
||||||
LOG.debug(result)
|
LOG.debug(result)
|
||||||
injection.destroy()
|
injection.destroy()
|
||||||
return result
|
return result
|
||||||
|
@ -267,7 +280,7 @@ class ConductorEndpoint(object):
|
||||||
(plugin.uuid,))
|
(plugin.uuid,))
|
||||||
except exception:
|
except exception:
|
||||||
return exception
|
return exception
|
||||||
|
result = manage_result(result, action, board_uuid)
|
||||||
LOG.debug(result)
|
LOG.debug(result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -290,3 +303,151 @@ class ConductorEndpoint(object):
|
||||||
LOG.debug('Updating service %s', service.name)
|
LOG.debug('Updating service %s', service.name)
|
||||||
service.save()
|
service.save()
|
||||||
return serializer.serialize_entity(ctx, service)
|
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":
|
||||||
|
try:
|
||||||
|
objects.ExposedService.get(ctx,
|
||||||
|
board_uuid,
|
||||||
|
service_uuid)
|
||||||
|
return exception.ServiceAlreadyExposed(uuid=service_uuid)
|
||||||
|
except Exception:
|
||||||
|
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
|
||||||
|
|
||||||
|
elif action == "ServiceDisable":
|
||||||
|
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
|
||||||
|
|
||||||
|
elif action == "ServiceRestore":
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
if res.result == wm.SUCCESS:
|
||||||
|
pid = res.message[0]
|
||||||
|
|
||||||
|
exp_data = {
|
||||||
|
'id': exposed.id,
|
||||||
|
'board_uuid': board_uuid,
|
||||||
|
'service_uuid': service_uuid,
|
||||||
|
'public_port': exposed.public_port,
|
||||||
|
'pid': pid,
|
||||||
|
}
|
||||||
|
|
||||||
|
exposed = objects.ExposedService(ctx, **exp_data)
|
||||||
|
exposed.save()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
|
@ -249,3 +249,17 @@ class ConductorAPI(object):
|
||||||
"""
|
"""
|
||||||
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||||
return cctxt.call(context, 'update_service', service_obj=service_obj)
|
return cctxt.call(context, 'update_service', service_obj=service_obj)
|
||||||
|
|
||||||
|
def action_service(self, context, service_uuid,
|
||||||
|
board_uuid, action, topic=None):
|
||||||
|
"""Action on a service into a board.
|
||||||
|
|
||||||
|
:param context: request context.
|
||||||
|
:param service_uuid: service id or uuid.
|
||||||
|
:param board_uuid: board id or uuid.
|
||||||
|
|
||||||
|
"""
|
||||||
|
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||||
|
|
||||||
|
return cctxt.call(context, 'action_service', service_uuid=service_uuid,
|
||||||
|
board_uuid=board_uuid, action=action)
|
||||||
|
|
|
@ -473,3 +473,57 @@ class Connection(object):
|
||||||
:raises: ServiceAssociated
|
:raises: ServiceAssociated
|
||||||
:raises: ServiceNotFound
|
:raises: ServiceNotFound
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_exposed_service_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.
|
||||||
|
:returns: An exposed_service.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_exposed_service_by_uuids(self, board_uuid, service_uuid):
|
||||||
|
"""get an exposed of a service using a board_uuid and service_uuid
|
||||||
|
|
||||||
|
:param board_uuid: The id or uuid of a board.
|
||||||
|
:param service_uuid: The id or uuid of a service.
|
||||||
|
:returns: An exposed_service.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def create_exposed_service(self, values):
|
||||||
|
"""Create a new exposed_service.
|
||||||
|
|
||||||
|
:param values: A dict containing several items used to identify
|
||||||
|
and track the service
|
||||||
|
:returns: An exposed service.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def destroy_exposed_service(self, exposed_service_id):
|
||||||
|
"""Destroy an exposed service and all associated interfaces.
|
||||||
|
|
||||||
|
:param exposed_service_id: The id or uuid of a service.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def update_exposed_service(self, service_exposed_id, values):
|
||||||
|
"""Update properties of a service.
|
||||||
|
|
||||||
|
:param service_id: The id or uuid of a service.
|
||||||
|
:param values: Dict of values to update.
|
||||||
|
:returns: A service.
|
||||||
|
:raises: ServiceAssociated
|
||||||
|
:raises: ServiceNotFound
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_exposed_service_list(self, board_uuid):
|
||||||
|
"""Return a list of exposed_services.
|
||||||
|
|
||||||
|
:param board_uuid: The id or uuid of a service.
|
||||||
|
:returns: A list of ExposedServices on the board.
|
||||||
|
"""
|
||||||
|
|
|
@ -761,3 +761,84 @@ class Connection(api.Connection):
|
||||||
|
|
||||||
ref.update(values)
|
ref.update(values)
|
||||||
return ref
|
return ref
|
||||||
|
|
||||||
|
# EXPOSED SERVICE api
|
||||||
|
|
||||||
|
def get_exposed_service_by_board_uuid(self, board_uuid):
|
||||||
|
query = model_query(
|
||||||
|
models.ExposedService).filter_by(
|
||||||
|
board_uuid=board_uuid)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.ExposedServiceNotFound()
|
||||||
|
|
||||||
|
def create_exposed_service(self, values):
|
||||||
|
# ensure defaults are present for new services
|
||||||
|
if 'uuid' not in values:
|
||||||
|
values['uuid'] = uuidutils.generate_uuid()
|
||||||
|
exp_serv = models.ExposedService()
|
||||||
|
exp_serv.update(values)
|
||||||
|
try:
|
||||||
|
exp_serv.save()
|
||||||
|
except db_exc.DBDuplicateEntry:
|
||||||
|
raise exception.ServiceAlreadyExposed(uuid=values['uuid'])
|
||||||
|
return exp_serv
|
||||||
|
|
||||||
|
def update_exposed_service(self, service_exposed_id, values):
|
||||||
|
|
||||||
|
if 'uuid' in values:
|
||||||
|
msg = _("Cannot overwrite UUID for an existing Service.")
|
||||||
|
raise exception.InvalidParameterValue(err=msg)
|
||||||
|
try:
|
||||||
|
return self._do_update_exposed_service(
|
||||||
|
service_exposed_id, values)
|
||||||
|
|
||||||
|
except db_exc.DBDuplicateEntry as e:
|
||||||
|
if 'name' in e.columns:
|
||||||
|
raise exception.DuplicateName(name=values['name'])
|
||||||
|
elif 'uuid' in e.columns:
|
||||||
|
raise exception.ServiceAlreadyExists(uuid=values['uuid'])
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def get_exposed_service_by_uuids(self, board_uuid, service_uuid):
|
||||||
|
query = model_query(
|
||||||
|
models.ExposedService).filter_by(
|
||||||
|
board_uuid=board_uuid).filter_by(
|
||||||
|
service_uuid=service_uuid)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.ExposedServiceNotFound(uuid=service_uuid)
|
||||||
|
|
||||||
|
def destroy_exposed_service(self, exposed_service_id):
|
||||||
|
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.ExposedService, session=session)
|
||||||
|
query = add_identity_filter(query, exposed_service_id)
|
||||||
|
try:
|
||||||
|
query.delete()
|
||||||
|
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.ExposedServiceNotFound()
|
||||||
|
|
||||||
|
def get_exposed_service_list(self, board_uuid):
|
||||||
|
query = model_query(
|
||||||
|
models.ExposedService).filter_by(
|
||||||
|
board_uuid=board_uuid)
|
||||||
|
return query.all()
|
||||||
|
|
||||||
|
def _do_update_exposed_service(self, service_id, values):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.ExposedService, session=session)
|
||||||
|
query = add_identity_filter(query, service_id)
|
||||||
|
try:
|
||||||
|
ref = query.with_lockmode('update').one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.ServiceNotFoundNotFound(uuid=service_id)
|
||||||
|
|
||||||
|
ref.update(values)
|
||||||
|
return ref
|
||||||
|
|
|
@ -248,3 +248,4 @@ class ExposedService(Base):
|
||||||
board_uuid = Column(String(36), ForeignKey('boards.uuid'))
|
board_uuid = Column(String(36), ForeignKey('boards.uuid'))
|
||||||
service_uuid = Column(String(36), ForeignKey('services.uuid'))
|
service_uuid = Column(String(36), ForeignKey('services.uuid'))
|
||||||
public_port = Column(Integer)
|
public_port = Column(Integer)
|
||||||
|
pid = Column(Integer)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
from iotronic.objects import board
|
from iotronic.objects import board
|
||||||
from iotronic.objects import conductor
|
from iotronic.objects import conductor
|
||||||
|
from iotronic.objects import exposedservice
|
||||||
from iotronic.objects import injectionplugin
|
from iotronic.objects import injectionplugin
|
||||||
from iotronic.objects import location
|
from iotronic.objects import location
|
||||||
from iotronic.objects import plugin
|
from iotronic.objects import plugin
|
||||||
|
@ -26,6 +27,7 @@ Board = board.Board
|
||||||
Location = location.Location
|
Location = location.Location
|
||||||
Plugin = plugin.Plugin
|
Plugin = plugin.Plugin
|
||||||
InjectionPlugin = injectionplugin.InjectionPlugin
|
InjectionPlugin = injectionplugin.InjectionPlugin
|
||||||
|
ExposedService = exposedservice.ExposedService
|
||||||
SessionWP = sessionwp.SessionWP
|
SessionWP = sessionwp.SessionWP
|
||||||
WampAgent = wampagent.WampAgent
|
WampAgent = wampagent.WampAgent
|
||||||
Service = service.Service
|
Service = service.Service
|
||||||
|
@ -39,4 +41,5 @@ __all__ = (
|
||||||
Service,
|
Service,
|
||||||
Plugin,
|
Plugin,
|
||||||
InjectionPlugin,
|
InjectionPlugin,
|
||||||
|
ExposedService
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
# coding=utf-8
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from iotronic.db import api as db_api
|
||||||
|
from iotronic.objects import base
|
||||||
|
from iotronic.objects import utils as obj_utils
|
||||||
|
|
||||||
|
|
||||||
|
class ExposedService(base.IotronicObject):
|
||||||
|
# Version 1.0: Initial version
|
||||||
|
VERSION = '1.0'
|
||||||
|
|
||||||
|
dbapi = db_api.get_instance()
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
'id': int,
|
||||||
|
'board_uuid': obj_utils.str_or_none,
|
||||||
|
'service_uuid': obj_utils.str_or_none,
|
||||||
|
'public_port': int,
|
||||||
|
'pid': int
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _from_db_object(exposed_service, db_exposed_service):
|
||||||
|
"""Converts a database entity to a formal object."""
|
||||||
|
for field in exposed_service.fields:
|
||||||
|
exposed_service[field] = db_exposed_service[field]
|
||||||
|
exposed_service.obj_reset_changes()
|
||||||
|
return exposed_service
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get_by_id(cls, context, exposed_service_id):
|
||||||
|
"""Find a exposed_service based on its integer id and return a Board object.
|
||||||
|
|
||||||
|
:param exposed_service_id: the id of a exposed_service.
|
||||||
|
:returns: a :class:`exposed_service` object.
|
||||||
|
"""
|
||||||
|
db_exp_service = cls.dbapi.get_exposed_service_by_id(
|
||||||
|
exposed_service_id)
|
||||||
|
exp_service = ExposedService._from_db_object(cls(context),
|
||||||
|
db_exp_service)
|
||||||
|
return exp_service
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get_by_board_uuid(cls, context, board_uuid):
|
||||||
|
"""Find a exposed_service based on uuid and return a Board 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
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get_by_service_uuid(cls, context, service_uuid):
|
||||||
|
"""Find a exposed_service based on uuid and return a Board object.
|
||||||
|
|
||||||
|
:param service_uuid: the uuid of a exposed_service.
|
||||||
|
:returns: a :class:`exposed_service` object.
|
||||||
|
"""
|
||||||
|
db_exp_service = cls.dbapi.get_exposed_service_by_service_uuid(
|
||||||
|
service_uuid)
|
||||||
|
exp_service = ExposedService._from_db_object(cls(context),
|
||||||
|
db_exp_service)
|
||||||
|
return exp_service
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get(cls, context, board_uuid, service_uuid):
|
||||||
|
"""Find a exposed_service based on uuid and return a Service 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_uuids(board_uuid,
|
||||||
|
service_uuid)
|
||||||
|
exp_service = ExposedService._from_db_object(cls(context),
|
||||||
|
db_exp_service)
|
||||||
|
return exp_service
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def list(cls, context, board_uuid):
|
||||||
|
"""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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_exps = cls.dbapi.get_exposed_service_list(board_uuid)
|
||||||
|
return [ExposedService._from_db_object(cls(context), obj)
|
||||||
|
for obj in db_exps]
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def create(self, context=None):
|
||||||
|
"""Create a ExposedService record in the DB.
|
||||||
|
|
||||||
|
Column-wise updates will be made based on the result of
|
||||||
|
self.what_changed(). If target_power_state is provided,
|
||||||
|
it will be checked against the in-database copy of the
|
||||||
|
exposed_service before updates are made.
|
||||||
|
|
||||||
|
:param context: Security context. NOTE: This should only
|
||||||
|
be used internally by the indirection_api.
|
||||||
|
Unfortunately, RPC requires context as the first
|
||||||
|
argument, even though we don't use it.
|
||||||
|
A context should be set when instantiating the
|
||||||
|
object, e.g.: ExposedService(context)
|
||||||
|
|
||||||
|
"""
|
||||||
|
values = self.obj_get_changes()
|
||||||
|
db_exposed_service = self.dbapi.create_exposed_service(values)
|
||||||
|
self._from_db_object(self, db_exposed_service)
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def destroy(self, context=None):
|
||||||
|
"""Delete the ExposedService from the DB.
|
||||||
|
|
||||||
|
:param context: Security context. NOTE: This should only
|
||||||
|
be used internally by the indirection_api.
|
||||||
|
Unfortunately, RPC requires context as the first
|
||||||
|
argument, even though we don't use it.
|
||||||
|
A context should be set when instantiating the
|
||||||
|
object, e.g.: ExposedService(context)
|
||||||
|
"""
|
||||||
|
self.dbapi.destroy_exposed_service(self.id)
|
||||||
|
self.obj_reset_changes()
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def save(self, context=None):
|
||||||
|
"""Save updates to this ExposedService.
|
||||||
|
|
||||||
|
Column-wise updates will be made based on the result of
|
||||||
|
self.what_changed(). If target_power_state is provided,
|
||||||
|
it will be checked against the in-database copy of the
|
||||||
|
exposed_service before updates are made.
|
||||||
|
|
||||||
|
:param context: Security context. NOTE: This should only
|
||||||
|
be used internally by the indirection_api.
|
||||||
|
Unfortunately, RPC requires context as the first
|
||||||
|
argument, even though we don't use it.
|
||||||
|
A context should be set when instantiating the
|
||||||
|
object, e.g.: ExposedService(context)
|
||||||
|
"""
|
||||||
|
updates = self.obj_get_changes()
|
||||||
|
self.dbapi.update_exposed_service(self.id, updates)
|
||||||
|
self.obj_reset_changes()
|
|
@ -21,11 +21,8 @@ from iotronic.db import api as db_api
|
||||||
from iotronic.objects import base
|
from iotronic.objects import base
|
||||||
from iotronic.objects import utils as obj_utils
|
from iotronic.objects import utils as obj_utils
|
||||||
|
|
||||||
"""
|
|
||||||
ACTIONS = ['ServiceCall', 'ServiceStop', 'ServiceStart',
|
ACTIONS = ['ServiceEnable', 'ServiceDisable', 'ServiceRestore']
|
||||||
'ServiceStatus', 'ServiceReboot']
|
|
||||||
CUSTOM_PARAMS = ['ServiceCall', 'ServiceStart', 'ServiceReboot']
|
|
||||||
NO_PARAMS = ['ServiceStatus']
|
|
||||||
|
|
||||||
|
|
||||||
def is_valid_action(action):
|
def is_valid_action(action):
|
||||||
|
@ -34,16 +31,6 @@ def is_valid_action(action):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def want_customs_params(action):
|
|
||||||
return True if action in CUSTOM_PARAMS else False
|
|
||||||
|
|
||||||
|
|
||||||
def want_params(action):
|
|
||||||
return False if action in NO_PARAMS else True
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Service(base.IotronicObject):
|
class Service(base.IotronicObject):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
VERSION = '1.0'
|
||||||
|
@ -154,6 +141,7 @@ class Service(base.IotronicObject):
|
||||||
object, e.g.: Service(context)
|
object, e.g.: Service(context)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
values = self.obj_get_changes()
|
values = self.obj_get_changes()
|
||||||
db_service = self.dbapi.create_service(values)
|
db_service = self.dbapi.create_service(values)
|
||||||
self._from_db_object(self, db_service)
|
self._from_db_object(self, db_service)
|
||||||
|
|
|
@ -167,8 +167,11 @@ CREATE TABLE IF NOT EXISTS `iotronic`.`exposed_services` (
|
||||||
`board_uuid` VARCHAR(36) NOT NULL,
|
`board_uuid` VARCHAR(36) NOT NULL,
|
||||||
`service_uuid` VARCHAR(36) NOT NULL,
|
`service_uuid` VARCHAR(36) NOT NULL,
|
||||||
`public_port` INT(5) NOT NULL,
|
`public_port` INT(5) NOT NULL,
|
||||||
|
`pid` INT(5) NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
INDEX `board_uuid` (`board_uuid` ASC),
|
INDEX `board_uuid` (`board_uuid` ASC),
|
||||||
|
CONSTRAINT unique_index
|
||||||
|
UNIQUE (service_uuid, board_uuid, pid),
|
||||||
CONSTRAINT `fk_board_uuid`
|
CONSTRAINT `fk_board_uuid`
|
||||||
FOREIGN KEY (`board_uuid`)
|
FOREIGN KEY (`board_uuid`)
|
||||||
REFERENCES `iotronic`.`boards` (`uuid`)
|
REFERENCES `iotronic`.`boards` (`uuid`)
|
||||||
|
|
Loading…
Reference in New Issue