summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabio Verboso <fverboso@unime.it>2018-02-13 18:06:24 +0100
committerFabio Verboso <fverboso@unime.it>2018-02-13 18:06:24 +0100
commitf450f91c70fabd2788666a37f1fdbe3e4bff6092 (patch)
tree12ae7cca1a98e01bcb3749063f20143f01ab45ff
parent6e9e02e9c3e014bb2f40323b12123c004873d48b (diff)
Exposed Cloud Service
Iotronic exposes the services of the boards Change-Id: Id88c4e6a9d5752d1bffbcfd99827eca0b9b679f7
Notes
Notes (review): Code-Review+2: Nicola Peditto <npeditto@unime.it> Workflow+1: Nicola Peditto <npeditto@unime.it> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Tue, 13 Feb 2018 17:32:09 +0000 Reviewed-on: https://review.openstack.org/544007 Project: openstack/iotronic Branch: refs/heads/master
-rw-r--r--iotronic/api/controllers/v1/board.py100
-rw-r--r--iotronic/common/exception.py12
-rw-r--r--iotronic/common/policy.py15
-rw-r--r--iotronic/conductor/endpoints.py189
-rw-r--r--iotronic/conductor/rpcapi.py14
-rw-r--r--iotronic/db/api.py54
-rw-r--r--iotronic/db/sqlalchemy/api.py81
-rw-r--r--iotronic/db/sqlalchemy/models.py1
-rw-r--r--iotronic/objects/__init__.py3
-rw-r--r--iotronic/objects/exposedservice.py165
-rw-r--r--iotronic/objects/service.py18
-rw-r--r--utils/iotronic.sql3
12 files changed, 626 insertions, 29 deletions
diff --git a/iotronic/api/controllers/v1/board.py b/iotronic/api/controllers/v1/board.py
index 6539c85..054e7a3 100644
--- a/iotronic/api/controllers/v1/board.py
+++ b/iotronic/api/controllers/v1/board.py
@@ -152,11 +152,50 @@ class InjectionCollection(collection.Collection):
152 return collection 152 return collection
153 153
154 154
155class ExposedService(base.APIBase):
156 service = types.uuid_or_name
157 board_uuid = types.uuid_or_name
158 public_port = wsme.types.IntegerType()
159
160 def __init__(self, **kwargs):
161 self.fields = []
162 fields = list(objects.ExposedService.fields)
163 fields.remove('board_uuid')
164 for k in fields:
165 # Skip fields we do not expose.
166 if not hasattr(self, k):
167 continue
168 self.fields.append(k)
169 setattr(self, k, kwargs.get(k, wtypes.Unset))
170 setattr(self, 'service', kwargs.get('service_uuid', wtypes.Unset))
171
172
173class ExposedCollection(collection.Collection):
174 """API representation of a collection of injection."""
175
176 exposed = [ExposedService]
177
178 def __init__(self, **kwargs):
179 self._type = 'exposed'
180
181 @staticmethod
182 def get_list(exposed, fields=None):
183 collection = ExposedCollection()
184 collection.exposed = [ExposedService(**n.as_dict())
185 for n in exposed]
186 return collection
187
188
155class PluginAction(base.APIBase): 189class PluginAction(base.APIBase):
156 action = wsme.wsattr(wtypes.text) 190 action = wsme.wsattr(wtypes.text)
157 parameters = types.jsontype 191 parameters = types.jsontype
158 192
159 193
194class ServiceAction(base.APIBase):
195 action = wsme.wsattr(wtypes.text)
196 parameters = types.jsontype
197
198
160class BoardPluginsController(rest.RestController): 199class BoardPluginsController(rest.RestController):
161 def __init__(self, board_ident): 200 def __init__(self, board_ident):
162 self.board_ident = board_ident 201 self.board_ident = board_ident
@@ -282,11 +321,72 @@ class BoardPluginsController(rest.RestController):
282 rpc_board.uuid) 321 rpc_board.uuid)
283 322
284 323
324class BoardServicesController(rest.RestController):
325 _custom_actions = {
326 'action': ['POST'],
327 }
328
329 def __init__(self, board_ident):
330 self.board_ident = board_ident
331
332 def _get_services_on_board_collection(self, board_uuid, fields=None):
333 services = objects.ExposedService.list(pecan.request.context,
334 board_uuid)
335
336 return ExposedCollection.get_list(services,
337 fields=fields)
338
339 @expose.expose(ExposedCollection,
340 status_code=200)
341 def get_all(self):
342 """Retrieve a list of services of a board.
343
344 """
345 rpc_board = api_utils.get_rpc_board(self.board_ident)
346
347 cdict = pecan.request.context.to_policy_values()
348 cdict['project_id'] = rpc_board.project
349 policy.authorize('iot:service_on_board:get', cdict, cdict)
350
351 return self._get_services_on_board_collection(rpc_board.uuid)
352
353 @expose.expose(wtypes.text, types.uuid_or_name, body=ServiceAction,
354 status_code=200)
355 def action(self, service_ident, ServiceAction):
356
357 if not ServiceAction.action:
358 raise exception.MissingParameterValue(
359 ("Action is not specified."))
360
361 if not ServiceAction.parameters:
362 ServiceAction.parameters = {}
363
364 rpc_board = api_utils.get_rpc_board(self.board_ident)
365 rpc_service = api_utils.get_rpc_service(service_ident)
366
367 try:
368 cdict = pecan.request.context.to_policy_values()
369 cdict['owner'] = rpc_board.owner
370 policy.authorize('iot:service_action:post', cdict, cdict)
371
372 except exception:
373 return exception
374
375 rpc_board.check_if_online()
376
377 result = pecan.request.rpcapi.action_service(pecan.request.context,
378 rpc_service.uuid,
379 rpc_board.uuid,
380 ServiceAction.action)
381 return result
382
383
285class BoardsController(rest.RestController): 384class BoardsController(rest.RestController):
286 """REST controller for Boards.""" 385 """REST controller for Boards."""
287 386
288 _subcontroller_map = { 387 _subcontroller_map = {
289 'plugins': BoardPluginsController, 388 'plugins': BoardPluginsController,
389 'services': BoardServicesController,
290 } 390 }
291 391
292 invalid_sort_key_list = ['extra', 'location'] 392 invalid_sort_key_list = ['extra', 'location']
diff --git a/iotronic/common/exception.py b/iotronic/common/exception.py
index f786645..76d24d2 100644
--- a/iotronic/common/exception.py
+++ b/iotronic/common/exception.py
@@ -597,3 +597,15 @@ class ErrorExecutionOnBoard(IotronicException):
597 597
598class ServiceNotFound(NotFound): 598class ServiceNotFound(NotFound):
599 message = _("Service %(Service)s could not be found.") 599 message = _("Service %(Service)s could not be found.")
600
601
602class ServiceAlreadyExists(Conflict):
603 message = _("A Service with UUID %(uuid)s already exists.")
604
605
606class ServiceAlreadyExposed(Conflict):
607 message = _("A Service with UUID %(uuid)s already exposed.")
608
609
610class ExposedServiceNotFound(NotFound):
611 message = _("ExposedService %(uuid)s could not be found.")
diff --git a/iotronic/common/policy.py b/iotronic/common/policy.py
index 5fff673..38555aa 100644
--- a/iotronic/common/policy.py
+++ b/iotronic/common/policy.py
@@ -136,6 +136,20 @@ service_policies = [
136 136
137] 137]
138 138
139exposed_service_policies = [
140 policy.RuleDefault('iot:service_on_board:get',
141 'rule:admin_or_owner',
142 description='Retrieve Service records'),
143 policy.RuleDefault('iot:service_remove:delete', 'rule:admin_or_owner',
144 description='Delete Service records'),
145 policy.RuleDefault('iot:service_action:post',
146 'rule:admin_or_owner',
147 description='Create Service records'),
148 policy.RuleDefault('iot:service_inject:put', 'rule:admin_or_owner',
149 description='Retrieve a Service record'),
150
151]
152
139 153
140def list_policies(): 154def list_policies():
141 policies = (default_policies 155 policies = (default_policies
@@ -143,6 +157,7 @@ def list_policies():
143 + plugin_policies 157 + plugin_policies
144 + injection_plugin_policies 158 + injection_plugin_policies
145 + service_policies 159 + service_policies
160 + exposed_service_policies
146 ) 161 )
147 return policies 162 return policies
148 163
diff --git a/iotronic/conductor/endpoints.py b/iotronic/conductor/endpoints.py
index 73226c8..5e10c49 100644
--- a/iotronic/conductor/endpoints.py
+++ b/iotronic/conductor/endpoints.py
@@ -39,6 +39,26 @@ def get_best_agent(ctx):
39 return agent.hostname 39 return agent.hostname
40 40
41 41
42def random_public_port():
43 return random.randint(6000, 7000)
44
45
46def manage_result(res, wamp_rpc_call, board_uuid):
47 if res.result == wm.SUCCESS:
48 return res.message
49 elif res.result == wm.WARNING:
50 LOG.warning('Warning in the execution of %s on %s', wamp_rpc_call,
51 board_uuid)
52 return res.message
53 elif res.result == wm.ERROR:
54 LOG.error('Error in the execution of %s on %s: %s', wamp_rpc_call,
55 board_uuid, res.message)
56 raise exception.ErrorExecutionOnBoard(call=wamp_rpc_call,
57 board=board_uuid,
58 error=res.message)
59 return res.message
60
61
42class ConductorEndpoint(object): 62class ConductorEndpoint(object):
43 def __init__(self, ragent): 63 def __init__(self, ragent):
44 transport = oslo_messaging.get_transport(cfg.CONF) 64 transport = oslo_messaging.get_transport(cfg.CONF)
@@ -119,10 +139,12 @@ class ConductorEndpoint(object):
119 board_id, 139 board_id,
120 'destroyBoard', 140 'destroyBoard',
121 (p,)) 141 (p,))
142
122 except exception: 143 except exception:
123 return exception 144 return exception
124 board.destroy() 145 board.destroy()
125 if result: 146 if result:
147 result = manage_result(result, 'destroyBoard', board_id)
126 LOG.debug(result) 148 LOG.debug(result)
127 return result 149 return result
128 return 150 return
@@ -164,18 +186,7 @@ class ConductorEndpoint(object):
164 data=wamp_rpc_args) 186 data=wamp_rpc_args)
165 res = wm.deserialize(res) 187 res = wm.deserialize(res)
166 188
167 if res.result == wm.SUCCESS: 189 return res
168 return res.message
169 elif res.result == wm.WARNING:
170 LOG.warning('Warning in the execution of %s on %s', wamp_rpc_call,
171 board_uuid)
172 return res.message
173 elif res.result == wm.ERROR:
174 LOG.error('Error in the execution of %s on %s: %s', wamp_rpc_call,
175 board_uuid, res.message)
176 raise exception.ErrorExecutionOnBoard(call=wamp_rpc_call,
177 board=board.uuid,
178 error=res.message)
179 190
180 def destroy_plugin(self, ctx, plugin_id): 191 def destroy_plugin(self, ctx, plugin_id):
181 LOG.info('Destroying plugin with id %s', 192 LOG.info('Destroying plugin with id %s',
@@ -231,7 +242,9 @@ class ConductorEndpoint(object):
231 injection = objects.InjectionPlugin(ctx, **inj_data) 242 injection = objects.InjectionPlugin(ctx, **inj_data)
232 injection.create() 243 injection.create()
233 244
245 result = manage_result(result, 'PluginInject', board_uuid)
234 LOG.debug(result) 246 LOG.debug(result)
247
235 return result 248 return result
236 249
237 def remove_plugin(self, ctx, plugin_uuid, board_uuid): 250 def remove_plugin(self, ctx, plugin_uuid, board_uuid):
@@ -247,7 +260,7 @@ class ConductorEndpoint(object):
247 (plugin.uuid,)) 260 (plugin.uuid,))
248 except exception: 261 except exception:
249 return exception 262 return exception
250 263 result = manage_result(result, 'PluginRemove', board_uuid)
251 LOG.debug(result) 264 LOG.debug(result)
252 injection.destroy() 265 injection.destroy()
253 return result 266 return result
@@ -267,7 +280,7 @@ class ConductorEndpoint(object):
267 (plugin.uuid,)) 280 (plugin.uuid,))
268 except exception: 281 except exception:
269 return exception 282 return exception
270 283 result = manage_result(result, action, board_uuid)
271 LOG.debug(result) 284 LOG.debug(result)
272 return result 285 return result
273 286
@@ -290,3 +303,151 @@ class ConductorEndpoint(object):
290 LOG.debug('Updating service %s', service.name) 303 LOG.debug('Updating service %s', service.name)
291 service.save() 304 service.save()
292 return serializer.serialize_entity(ctx, service) 305 return serializer.serialize_entity(ctx, service)
306
307 def action_service(self, ctx, service_uuid, board_uuid, action):
308 LOG.info('Enable service with id %s into the board %s',
309 service_uuid, board_uuid)
310 service = objects.Service.get(ctx, service_uuid)
311 objects.service.is_valid_action(action)
312
313 if action == "ServiceEnable":
314 try:
315 objects.ExposedService.get(ctx,
316 board_uuid,
317 service_uuid)
318 return exception.ServiceAlreadyExposed(uuid=service_uuid)
319 except Exception:
320 name = service.name
321 public_port = random_public_port()
322 port = service.port
323
324 res = self.execute_on_board(ctx, board_uuid, action,
325 (name, public_port, port))
326
327 if res.result == wm.SUCCESS:
328 pid = res.message[0]
329
330 exp_data = {
331 'board_uuid': board_uuid,
332 'service_uuid': service_uuid,
333 'public_port': public_port,
334 'pid': pid,
335 }
336 exposed = objects.ExposedService(ctx, **exp_data)
337 exposed.create()
338
339 res.message = res.message[1]
340 elif res.result == wm.ERROR:
341 LOG.error('Error in the execution of %s on %s: %s',
342 action,
343 board_uuid, res.message)
344 raise exception.ErrorExecutionOnBoard(call=action,
345 board=board_uuid,
346 error=res.message)
347 LOG.debug(res.message)
348 return res.message
349
350 elif action == "ServiceDisable":
351 exposed = objects.ExposedService.get(ctx,
352 board_uuid,
353 service_uuid)
354
355 res = self.execute_on_board(ctx, board_uuid, action,
356 (service.name, exposed.pid))
357
358 result = manage_result(res, action, board_uuid)
359 LOG.debug(res.message)
360 exposed.destroy()
361 return result
362
363 elif action == "ServiceRestore":
364
365 exposed = objects.ExposedService.get(ctx, board_uuid,
366 service_uuid)
367
368 print(exposed)
369
370 res = self.execute_on_board(ctx, board_uuid, action,
371 (service.name, exposed.public_port,
372 service.port, exposed.pid))
373
374 if res.result == wm.SUCCESS:
375 pid = res.message[0]
376
377 exp_data = {
378 'id': exposed.id,
379 'board_uuid': board_uuid,
380 'service_uuid': service_uuid,
381 'public_port': exposed.public_port,
382 'pid': pid,
383 }
384
385 exposed = objects.ExposedService(ctx, **exp_data)
386 exposed.save()
387
388 res.message = res.message[1]
389 elif res.result == wm.ERROR:
390 LOG.error('Error in the execution of %s on %s: %s',
391 action,
392 board_uuid, res.message)
393 raise exception.ErrorExecutionOnBoard(call=action,
394 board=board_uuid,
395 error=res.message)
396 LOG.debug(res.message)
397 return res.message
398
399 # try:
400 #
401 #
402 # return exception.ServiceAlreadyExposed(uuid=service_uuid)
403 # except:
404 # name=service.name
405 # public_port=random_public_port()
406 # port=service.port
407 #
408 # res = self.execute_on_board(ctx, board_uuid, action,
409 # (name, public_port, port))
410 #
411 # if res.result == wm.SUCCESS:
412 # pid = res.message[0]
413 #
414 # exp_data = {
415 # 'board_uuid': board_uuid,
416 # 'service_uuid': service_uuid,
417 # 'public_port': public_port,
418 # 'pid': pid,
419 # }
420 # exposed = objects.ExposedService(ctx, **exp_data)
421 # exposed.create()
422 #
423 # res.message = res.message[1]
424 # elif res.result == wm.ERROR:
425 # LOG.error('Error in the execution of %s on %s: %s',
426 # action,
427 # board_uuid, res.message)
428 # raise exception.ErrorExecutionOnBoard(call=action,
429 # board=board_uuid,
430 # error=res.message)
431 # LOG.debug(res.message)
432 # return res.message
433 #
434 #
435 #
436 #
437 #
438 #
439 #
440 #
441 #
442 #
443 #
444 # exposed = objects.ExposedService.get(ctx, board_uuid,
445 # service_uuid)
446 #
447 # res = self.execute_on_board(ctx, board_uuid, action,
448 # (service.name, exposed.pid))
449 #
450 # result=manage_result(res,action,board_uuid)
451 # LOG.debug(res.message)
452 # exposed.destroy()
453 # return result
diff --git a/iotronic/conductor/rpcapi.py b/iotronic/conductor/rpcapi.py
index 4d5b333..223c993 100644
--- a/iotronic/conductor/rpcapi.py
+++ b/iotronic/conductor/rpcapi.py
@@ -249,3 +249,17 @@ class ConductorAPI(object):
249 """ 249 """
250 cctxt = self.client.prepare(topic=topic or self.topic, version='1.0') 250 cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
251 return cctxt.call(context, 'update_service', service_obj=service_obj) 251 return cctxt.call(context, 'update_service', service_obj=service_obj)
252
253 def action_service(self, context, service_uuid,
254 board_uuid, action, topic=None):
255 """Action on a service into a board.
256
257 :param context: request context.
258 :param service_uuid: service id or uuid.
259 :param board_uuid: board id or uuid.
260
261 """
262 cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
263
264 return cctxt.call(context, 'action_service', service_uuid=service_uuid,
265 board_uuid=board_uuid, action=action)
diff --git a/iotronic/db/api.py b/iotronic/db/api.py
index 51c3e6a..82fafff 100644
--- a/iotronic/db/api.py
+++ b/iotronic/db/api.py
@@ -473,3 +473,57 @@ class Connection(object):
473 :raises: ServiceAssociated 473 :raises: ServiceAssociated
474 :raises: ServiceNotFound 474 :raises: ServiceNotFound
475 """ 475 """
476
477 @abc.abstractmethod
478 def get_exposed_service_by_board_uuid(self, board_uuid):
479 """get an exposed of a service using a board_uuid
480
481 :param board_uuid: The id or uuid of a board.
482 :returns: An exposed_service.
483
484 """
485
486 @abc.abstractmethod
487 def get_exposed_service_by_uuids(self, board_uuid, service_uuid):
488 """get an exposed of a service using a board_uuid and service_uuid
489
490 :param board_uuid: The id or uuid of a board.
491 :param service_uuid: The id or uuid of a service.
492 :returns: An exposed_service.
493
494 """
495
496 @abc.abstractmethod
497 def create_exposed_service(self, values):
498 """Create a new exposed_service.
499
500 :param values: A dict containing several items used to identify
501 and track the service
502 :returns: An exposed service.
503 """
504
505 @abc.abstractmethod
506 def destroy_exposed_service(self, exposed_service_id):
507 """Destroy an exposed service and all associated interfaces.
508
509 :param exposed_service_id: The id or uuid of a service.
510 """
511
512 @abc.abstractmethod
513 def update_exposed_service(self, service_exposed_id, values):
514 """Update properties of a service.
515
516 :param service_id: The id or uuid of a service.
517 :param values: Dict of values to update.
518 :returns: A service.
519 :raises: ServiceAssociated
520 :raises: ServiceNotFound
521 """
522
523 @abc.abstractmethod
524 def get_exposed_service_list(self, board_uuid):
525 """Return a list of exposed_services.
526
527 :param board_uuid: The id or uuid of a service.
528 :returns: A list of ExposedServices on the board.
529 """
diff --git a/iotronic/db/sqlalchemy/api.py b/iotronic/db/sqlalchemy/api.py
index 50132ca..275f674 100644
--- a/iotronic/db/sqlalchemy/api.py
+++ b/iotronic/db/sqlalchemy/api.py
@@ -761,3 +761,84 @@ class Connection(api.Connection):
761 761
762 ref.update(values) 762 ref.update(values)
763 return ref 763 return ref
764
765 # EXPOSED SERVICE api
766
767 def get_exposed_service_by_board_uuid(self, board_uuid):
768 query = model_query(
769 models.ExposedService).filter_by(
770 board_uuid=board_uuid)
771 try:
772 return query.one()
773 except NoResultFound:
774 raise exception.ExposedServiceNotFound()
775
776 def create_exposed_service(self, values):
777 # ensure defaults are present for new services
778 if 'uuid' not in values:
779 values['uuid'] = uuidutils.generate_uuid()
780 exp_serv = models.ExposedService()
781 exp_serv.update(values)
782 try:
783 exp_serv.save()
784 except db_exc.DBDuplicateEntry:
785 raise exception.ServiceAlreadyExposed(uuid=values['uuid'])
786 return exp_serv
787
788 def update_exposed_service(self, service_exposed_id, values):
789
790 if 'uuid' in values:
791 msg = _("Cannot overwrite UUID for an existing Service.")
792 raise exception.InvalidParameterValue(err=msg)
793 try:
794 return self._do_update_exposed_service(
795 service_exposed_id, values)
796
797 except db_exc.DBDuplicateEntry as e:
798 if 'name' in e.columns:
799 raise exception.DuplicateName(name=values['name'])
800 elif 'uuid' in e.columns:
801 raise exception.ServiceAlreadyExists(uuid=values['uuid'])
802 else:
803 raise e
804
805 def get_exposed_service_by_uuids(self, board_uuid, service_uuid):
806 query = model_query(
807 models.ExposedService).filter_by(
808 board_uuid=board_uuid).filter_by(
809 service_uuid=service_uuid)
810 try:
811 return query.one()
812 except NoResultFound:
813 raise exception.ExposedServiceNotFound(uuid=service_uuid)
814
815 def destroy_exposed_service(self, exposed_service_id):
816
817 session = get_session()
818 with session.begin():
819 query = model_query(models.ExposedService, session=session)
820 query = add_identity_filter(query, exposed_service_id)
821 try:
822 query.delete()
823
824 except NoResultFound:
825 raise exception.ExposedServiceNotFound()
826
827 def get_exposed_service_list(self, board_uuid):
828 query = model_query(
829 models.ExposedService).filter_by(
830 board_uuid=board_uuid)
831 return query.all()
832
833 def _do_update_exposed_service(self, service_id, values):
834 session = get_session()
835 with session.begin():
836 query = model_query(models.ExposedService, session=session)
837 query = add_identity_filter(query, service_id)
838 try:
839 ref = query.with_lockmode('update').one()
840 except NoResultFound:
841 raise exception.ServiceNotFoundNotFound(uuid=service_id)
842
843 ref.update(values)
844 return ref
diff --git a/iotronic/db/sqlalchemy/models.py b/iotronic/db/sqlalchemy/models.py
index 5fd78ed..e0d67cb 100644
--- a/iotronic/db/sqlalchemy/models.py
+++ b/iotronic/db/sqlalchemy/models.py
@@ -248,3 +248,4 @@ class ExposedService(Base):
248 board_uuid = Column(String(36), ForeignKey('boards.uuid')) 248 board_uuid = Column(String(36), ForeignKey('boards.uuid'))
249 service_uuid = Column(String(36), ForeignKey('services.uuid')) 249 service_uuid = Column(String(36), ForeignKey('services.uuid'))
250 public_port = Column(Integer) 250 public_port = Column(Integer)
251 pid = Column(Integer)
diff --git a/iotronic/objects/__init__.py b/iotronic/objects/__init__.py
index 2cfbf31..55f54d9 100644
--- a/iotronic/objects/__init__.py
+++ b/iotronic/objects/__init__.py
@@ -14,6 +14,7 @@
14 14
15from iotronic.objects import board 15from iotronic.objects import board
16from iotronic.objects import conductor 16from iotronic.objects import conductor
17from iotronic.objects import exposedservice
17from iotronic.objects import injectionplugin 18from iotronic.objects import injectionplugin
18from iotronic.objects import location 19from iotronic.objects import location
19from iotronic.objects import plugin 20from iotronic.objects import plugin
@@ -26,6 +27,7 @@ Board = board.Board
26Location = location.Location 27Location = location.Location
27Plugin = plugin.Plugin 28Plugin = plugin.Plugin
28InjectionPlugin = injectionplugin.InjectionPlugin 29InjectionPlugin = injectionplugin.InjectionPlugin
30ExposedService = exposedservice.ExposedService
29SessionWP = sessionwp.SessionWP 31SessionWP = sessionwp.SessionWP
30WampAgent = wampagent.WampAgent 32WampAgent = wampagent.WampAgent
31Service = service.Service 33Service = service.Service
@@ -39,4 +41,5 @@ __all__ = (
39 Service, 41 Service,
40 Plugin, 42 Plugin,
41 InjectionPlugin, 43 InjectionPlugin,
44 ExposedService
42) 45)
diff --git a/iotronic/objects/exposedservice.py b/iotronic/objects/exposedservice.py
new file mode 100644
index 0000000..7e96cda
--- /dev/null
+++ b/iotronic/objects/exposedservice.py
@@ -0,0 +1,165 @@
1# coding=utf-8
2#
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16from iotronic.db import api as db_api
17from iotronic.objects import base
18from iotronic.objects import utils as obj_utils
19
20
21class ExposedService(base.IotronicObject):
22 # Version 1.0: Initial version
23 VERSION = '1.0'
24
25 dbapi = db_api.get_instance()
26
27 fields = {
28 'id': int,
29 'board_uuid': obj_utils.str_or_none,
30 'service_uuid': obj_utils.str_or_none,
31 'public_port': int,
32 'pid': int
33 }
34
35 @staticmethod
36 def _from_db_object(exposed_service, db_exposed_service):
37 """Converts a database entity to a formal object."""
38 for field in exposed_service.fields:
39 exposed_service[field] = db_exposed_service[field]
40 exposed_service.obj_reset_changes()
41 return exposed_service
42
43 @base.remotable_classmethod
44 def get_by_id(cls, context, exposed_service_id):
45 """Find a exposed_service based on its integer id and return a Board object.
46
47 :param exposed_service_id: the id of a exposed_service.
48 :returns: a :class:`exposed_service` object.
49 """
50 db_exp_service = cls.dbapi.get_exposed_service_by_id(
51 exposed_service_id)
52 exp_service = ExposedService._from_db_object(cls(context),
53 db_exp_service)
54 return exp_service
55
56 @base.remotable_classmethod
57 def get_by_board_uuid(cls, context, board_uuid):
58 """Find a exposed_service based on uuid and return a Board object.
59
60 :param board_uuid: the uuid of a exposed_service.
61 :returns: a :class:`exposed_service` object.
62 """
63 db_exp_service = cls.dbapi.get_exposed_service_by_board_uuid(
64 board_uuid)
65 exp_service = ExposedService._from_db_object(cls(context),
66 db_exp_service)
67 return exp_service
68
69 @base.remotable_classmethod
70 def get_by_service_uuid(cls, context, service_uuid):
71 """Find a exposed_service based on uuid and return a Board object.
72
73 :param service_uuid: the uuid of a exposed_service.
74 :returns: a :class:`exposed_service` object.
75 """
76 db_exp_service = cls.dbapi.get_exposed_service_by_service_uuid(
77 service_uuid)
78 exp_service = ExposedService._from_db_object(cls(context),
79 db_exp_service)
80 return exp_service
81
82 @base.remotable_classmethod
83 def get(cls, context, board_uuid, service_uuid):
84 """Find a exposed_service based on uuid and return a Service object.
85
86 :param board_uuid: the uuid of a exposed_service.
87 :returns: a :class:`exposed_service` object.
88 """
89 db_exp_service = cls.dbapi.get_exposed_service_by_uuids(board_uuid,
90 service_uuid)
91 exp_service = ExposedService._from_db_object(cls(context),
92 db_exp_service)
93 return exp_service
94
95 @base.remotable_classmethod
96 def list(cls, context, board_uuid):
97 """Return a list of ExposedService objects.
98
99 :param context: Security context.
100 :param limit: maximum number of resources to return in a single result.
101 :param marker: pagination marker for large data sets.
102 :param sort_key: column to sort results by.
103 :param sort_dir: direction to sort. "asc" or "desc".
104 :param filters: Filters to apply.
105 :returns: a list of :class:`ExposedService` object.
106
107 """
108 db_exps = cls.dbapi.get_exposed_service_list(board_uuid)
109 return [ExposedService._from_db_object(cls(context), obj)
110 for obj in db_exps]
111
112 @base.remotable
113 def create(self, context=None):
114 """Create a ExposedService record in the DB.
115
116 Column-wise updates will be made based on the result of
117 self.what_changed(). If target_power_state is provided,
118 it will be checked against the in-database copy of the
119 exposed_service before updates are made.
120
121 :param context: Security context. NOTE: This should only
122 be used internally by the indirection_api.
123 Unfortunately, RPC requires context as the first
124 argument, even though we don't use it.
125 A context should be set when instantiating the
126 object, e.g.: ExposedService(context)
127
128 """
129 values = self.obj_get_changes()
130 db_exposed_service = self.dbapi.create_exposed_service(values)
131 self._from_db_object(self, db_exposed_service)
132
133 @base.remotable
134 def destroy(self, context=None):
135 """Delete the ExposedService from the DB.
136
137 :param context: Security context. NOTE: This should only
138 be used internally by the indirection_api.
139 Unfortunately, RPC requires context as the first
140 argument, even though we don't use it.
141 A context should be set when instantiating the
142 object, e.g.: ExposedService(context)
143 """
144 self.dbapi.destroy_exposed_service(self.id)
145 self.obj_reset_changes()
146
147 @base.remotable
148 def save(self, context=None):
149 """Save updates to this ExposedService.
150
151 Column-wise updates will be made based on the result of
152 self.what_changed(). If target_power_state is provided,
153 it will be checked against the in-database copy of the
154 exposed_service before updates are made.
155
156 :param context: Security context. NOTE: This should only
157 be used internally by the indirection_api.
158 Unfortunately, RPC requires context as the first
159 argument, even though we don't use it.
160 A context should be set when instantiating the
161 object, e.g.: ExposedService(context)
162 """
163 updates = self.obj_get_changes()
164 self.dbapi.update_exposed_service(self.id, updates)
165 self.obj_reset_changes()
diff --git a/iotronic/objects/service.py b/iotronic/objects/service.py
index b894e4b..0b1c3a8 100644
--- a/iotronic/objects/service.py
+++ b/iotronic/objects/service.py
@@ -21,11 +21,8 @@ from iotronic.db import api as db_api
21from iotronic.objects import base 21from iotronic.objects import base
22from iotronic.objects import utils as obj_utils 22from iotronic.objects import utils as obj_utils
23 23
24""" 24
25ACTIONS = ['ServiceCall', 'ServiceStop', 'ServiceStart', 25ACTIONS = ['ServiceEnable', 'ServiceDisable', 'ServiceRestore']
26 'ServiceStatus', 'ServiceReboot']
27CUSTOM_PARAMS = ['ServiceCall', 'ServiceStart', 'ServiceReboot']
28NO_PARAMS = ['ServiceStatus']
29 26
30 27
31def is_valid_action(action): 28def is_valid_action(action):
@@ -34,16 +31,6 @@ def is_valid_action(action):
34 return True 31 return True
35 32
36 33
37def want_customs_params(action):
38 return True if action in CUSTOM_PARAMS else False
39
40
41def want_params(action):
42 return False if action in NO_PARAMS else True
43
44"""
45
46
47class Service(base.IotronicObject): 34class Service(base.IotronicObject):
48 # Version 1.0: Initial version 35 # Version 1.0: Initial version
49 VERSION = '1.0' 36 VERSION = '1.0'
@@ -154,6 +141,7 @@ class Service(base.IotronicObject):
154 object, e.g.: Service(context) 141 object, e.g.: Service(context)
155 142
156 """ 143 """
144
157 values = self.obj_get_changes() 145 values = self.obj_get_changes()
158 db_service = self.dbapi.create_service(values) 146 db_service = self.dbapi.create_service(values)
159 self._from_db_object(self, db_service) 147 self._from_db_object(self, db_service)
diff --git a/utils/iotronic.sql b/utils/iotronic.sql
index 180a254..c6ead5b 100644
--- a/utils/iotronic.sql
+++ b/utils/iotronic.sql
@@ -167,8 +167,11 @@ CREATE TABLE IF NOT EXISTS `iotronic`.`exposed_services` (
167 `board_uuid` VARCHAR(36) NOT NULL, 167 `board_uuid` VARCHAR(36) NOT NULL,
168 `service_uuid` VARCHAR(36) NOT NULL, 168 `service_uuid` VARCHAR(36) NOT NULL,
169 `public_port` INT(5) NOT NULL, 169 `public_port` INT(5) NOT NULL,
170 `pid` INT(5) NOT NULL,
170 PRIMARY KEY (`id`), 171 PRIMARY KEY (`id`),
171 INDEX `board_uuid` (`board_uuid` ASC), 172 INDEX `board_uuid` (`board_uuid` ASC),
173 CONSTRAINT unique_index
174 UNIQUE (service_uuid, board_uuid, pid),
172 CONSTRAINT `fk_board_uuid` 175 CONSTRAINT `fk_board_uuid`
173 FOREIGN KEY (`board_uuid`) 176 FOREIGN KEY (`board_uuid`)
174 REFERENCES `iotronic`.`boards` (`uuid`) 177 REFERENCES `iotronic`.`boards` (`uuid`)