A LOT OF STUFF :)
Change-Id: I7c4c386559def70142b76da6112dff50c55183cc
This commit is contained in:
parent
0960dc4527
commit
b9e45b1eee
|
@ -3,7 +3,6 @@
|
|||
function build_install {
|
||||
python setup.py build
|
||||
python setup.py install
|
||||
cp utils/iotronic_curl_client /usr/bin/iotronic
|
||||
}
|
||||
|
||||
function restart_apache {
|
||||
|
|
|
@ -32,7 +32,7 @@ from iotronic.api.controllers.v1 import plugin
|
|||
# from iotronic.api.controllers.v1 import ramdisk
|
||||
# from iotronic.api.controllers.v1 import utils
|
||||
|
||||
from iotronic.api.controllers.v1 import node
|
||||
from iotronic.api.controllers.v1 import board
|
||||
|
||||
from iotronic.api.controllers.v1 import versions
|
||||
from iotronic.api import expose
|
||||
|
@ -57,8 +57,8 @@ class V1(base.APIBase):
|
|||
# links = [link.Link]
|
||||
"""Links that point to a specific URL for this version and documentation"""
|
||||
|
||||
nodes = [link.Link]
|
||||
"""Links to the nodes resource"""
|
||||
boards = [link.Link]
|
||||
"""Links to the boards resource"""
|
||||
|
||||
@staticmethod
|
||||
def convert():
|
||||
|
@ -81,13 +81,13 @@ class V1(base.APIBase):
|
|||
bookmark=True)
|
||||
]
|
||||
|
||||
v1.nodes = [link.Link.make_link('self', pecan.request.public_url,
|
||||
'nodes', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
pecan.request.public_url,
|
||||
'nodes', '',
|
||||
bookmark=True)
|
||||
]
|
||||
v1.boards = [link.Link.make_link('self', pecan.request.public_url,
|
||||
'boards', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
pecan.request.public_url,
|
||||
'boards', '',
|
||||
bookmark=True)
|
||||
]
|
||||
|
||||
return v1
|
||||
|
||||
|
@ -95,7 +95,7 @@ class V1(base.APIBase):
|
|||
class Controller(rest.RestController):
|
||||
"""Version 1 API controller root."""
|
||||
|
||||
nodes = node.NodesController()
|
||||
boards = board.BoardsController()
|
||||
plugins = plugin.PluginsController()
|
||||
|
||||
@expose.expose(V1)
|
||||
|
@ -113,19 +113,20 @@ class Controller(rest.RestController):
|
|||
raise exc.HTTPNotAcceptable(_(
|
||||
"Mutually exclusive versions requested. Version %(ver)s "
|
||||
"requested but not supported by this service. The supported "
|
||||
"version range is: [%(min)s, %(max)s].") %
|
||||
{'ver': version, 'min': versions.MIN_VERSION_STRING,
|
||||
'max': versions.MAX_VERSION_STRING},
|
||||
headers=headers)
|
||||
"version range is: [%(min)s, %(max)s].") % {
|
||||
'ver': version, 'min': versions.MIN_VERSION_STRING,
|
||||
'max': versions.MAX_VERSION_STRING
|
||||
}, headers=headers)
|
||||
# ensure the minor version is within the supported range
|
||||
if version < MIN_VER or version > MAX_VER:
|
||||
raise exc.HTTPNotAcceptable(_(
|
||||
"Version %(ver)s was requested but the minor version is not "
|
||||
"supported by this service. The supported version range is: "
|
||||
"[%(min)s, %(max)s].") %
|
||||
{'ver': version, 'min': versions.MIN_VERSION_STRING,
|
||||
'max': versions.MAX_VERSION_STRING},
|
||||
headers=headers)
|
||||
"[%(min)s, %(max)s].") % {
|
||||
'ver': version,
|
||||
'min': versions.MIN_VERSION_STRING,
|
||||
'max': versions.MAX_VERSION_STRING
|
||||
}, headers=headers)
|
||||
|
||||
@pecan.expose()
|
||||
def _route(self, args):
|
||||
|
|
|
@ -0,0 +1,481 @@
|
|||
# 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.api.controllers import base
|
||||
from iotronic.api.controllers import link
|
||||
from iotronic.api.controllers.v1 import collection
|
||||
from iotronic.api.controllers.v1 import location as loc
|
||||
from iotronic.api.controllers.v1 import types
|
||||
from iotronic.api.controllers.v1 import utils as api_utils
|
||||
from iotronic.api import expose
|
||||
from iotronic.common import exception
|
||||
from iotronic.common import policy
|
||||
from iotronic import objects
|
||||
import pecan
|
||||
from pecan import rest
|
||||
import wsme
|
||||
from wsme import types as wtypes
|
||||
|
||||
_DEFAULT_RETURN_FIELDS = ('name', 'code', 'status', 'uuid', 'session', 'type')
|
||||
|
||||
|
||||
class Board(base.APIBase):
|
||||
"""API representation of a board.
|
||||
|
||||
"""
|
||||
uuid = types.uuid
|
||||
code = wsme.wsattr(wtypes.text)
|
||||
status = wsme.wsattr(wtypes.text)
|
||||
name = wsme.wsattr(wtypes.text)
|
||||
type = wsme.wsattr(wtypes.text)
|
||||
owner = types.uuid
|
||||
session = wsme.wsattr(wtypes.text)
|
||||
project = types.uuid
|
||||
mobile = types.boolean
|
||||
links = wsme.wsattr([link.Link], readonly=True)
|
||||
location = wsme.wsattr([loc.Location])
|
||||
extra = types.jsontype
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = []
|
||||
fields = list(objects.Board.fields)
|
||||
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))
|
||||
|
||||
@staticmethod
|
||||
def _convert_with_links(board, url, fields=None):
|
||||
board_uuid = board.uuid
|
||||
if fields is not None:
|
||||
board.unset_fields_except(fields)
|
||||
|
||||
# rel_name, url, resource, resource_args,
|
||||
# bookmark=False, type=wtypes.Unset
|
||||
board.links = [link.Link.make_link('self', url, 'boards',
|
||||
board_uuid),
|
||||
link.Link.make_link('bookmark', url, 'boards',
|
||||
board_uuid, bookmark=True)
|
||||
]
|
||||
return board
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_board, fields=None):
|
||||
board = Board(**rpc_board.as_dict())
|
||||
|
||||
try:
|
||||
session = objects.SessionWP.get_session_by_board_uuid(
|
||||
pecan.request.context, board.uuid)
|
||||
board.session = session.session_id
|
||||
except Exception:
|
||||
board.session = None
|
||||
|
||||
try:
|
||||
list_loc = objects.Location.list_by_board_uuid(
|
||||
pecan.request.context, board.uuid)
|
||||
board.location = loc.Location.convert_with_list(list_loc)
|
||||
except Exception:
|
||||
board.location = []
|
||||
|
||||
# to enable as soon as a better session and location management
|
||||
# is implemented
|
||||
# if fields is not None:
|
||||
# api_utils.check_for_invalid_fields(fields, board_dict)
|
||||
|
||||
return cls._convert_with_links(board,
|
||||
pecan.request.public_url,
|
||||
fields=fields)
|
||||
|
||||
|
||||
class BoardCollection(collection.Collection):
|
||||
"""API representation of a collection of boards."""
|
||||
|
||||
boards = [Board]
|
||||
"""A list containing boards objects"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._type = 'boards'
|
||||
|
||||
@staticmethod
|
||||
def convert_with_links(boards, limit, url=None, fields=None, **kwargs):
|
||||
collection = BoardCollection()
|
||||
collection.boards = [Board.convert_with_links(n, fields=fields)
|
||||
for n in boards]
|
||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||
return collection
|
||||
|
||||
|
||||
class InjectionPlugin(base.APIBase):
|
||||
plugin = types.uuid_or_name
|
||||
board_uuid = types.uuid_or_name
|
||||
status = wtypes.text
|
||||
onboot = types.boolean
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = []
|
||||
fields = list(objects.InjectionPlugin.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, 'plugin', kwargs.get('plugin_uuid', wtypes.Unset))
|
||||
|
||||
|
||||
class InjectionCollection(collection.Collection):
|
||||
"""API representation of a collection of injection."""
|
||||
|
||||
injections = [InjectionPlugin]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._type = 'injections'
|
||||
|
||||
@staticmethod
|
||||
def get_list(injections, fields=None):
|
||||
collection = InjectionCollection()
|
||||
collection.injections = [InjectionPlugin(**n.as_dict())
|
||||
for n in injections]
|
||||
return collection
|
||||
|
||||
|
||||
class PluginAction(base.APIBase):
|
||||
action = wsme.wsattr(wtypes.text)
|
||||
parameters = types.jsontype
|
||||
|
||||
|
||||
class BoardPluginsController(rest.RestController):
|
||||
def __init__(self, board_ident):
|
||||
self.board_ident = board_ident
|
||||
|
||||
def _get_plugins_on_board_collection(self, board_uuid, fields=None):
|
||||
injections = objects.InjectionPlugin.list(pecan.request.context,
|
||||
board_uuid)
|
||||
|
||||
return InjectionCollection.get_list(injections,
|
||||
fields=fields)
|
||||
|
||||
@expose.expose(InjectionCollection,
|
||||
status_code=200)
|
||||
def get_all(self):
|
||||
"""Retrieve a list of plugins of a board.
|
||||
|
||||
"""
|
||||
|
||||
# cdict = pecan.request.context.to_policy_values()
|
||||
# policy.authorize('iot:plugins_on_board:get', cdict, cdict)
|
||||
|
||||
rpc_board = api_utils.get_rpc_board(self.board_ident)
|
||||
|
||||
return self._get_plugins_on_board_collection(rpc_board.uuid)
|
||||
|
||||
@expose.expose(InjectionPlugin, types.uuid_or_name)
|
||||
def get_one(self, plugin_ident):
|
||||
"""Retrieve information about the given board.
|
||||
|
||||
:param plugin_ident: UUID or logical name of a board.
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned.
|
||||
"""
|
||||
|
||||
# cdict = pecan.request.context.to_policy_values()
|
||||
# policy.authorize('iot:plugins_on_board:get', cdict, cdict)
|
||||
|
||||
rpc_board = api_utils.get_rpc_board(self.board_ident)
|
||||
rpc_plugin = api_utils.get_rpc_plugin(plugin_ident)
|
||||
inj_plug = objects.InjectionPlugin.get(pecan.request.context,
|
||||
rpc_board.uuid,
|
||||
rpc_plugin.uuid)
|
||||
return InjectionPlugin(**inj_plug.as_dict())
|
||||
|
||||
@expose.expose(wtypes.text, types.uuid_or_name, body=PluginAction,
|
||||
status_code=200)
|
||||
def post(self, plugin_ident, PluginAction):
|
||||
# cdict = pecan.request.context.to_policy_values()
|
||||
# policy.authorize('iot:plugin_action:post', cdict, cdict)
|
||||
|
||||
rpc_plugin = api_utils.get_rpc_plugin(plugin_ident)
|
||||
rpc_board = api_utils.get_rpc_board(self.board_ident)
|
||||
|
||||
result = pecan.request.rpcapi.action_plugin(pecan.request.context,
|
||||
rpc_plugin.uuid,
|
||||
rpc_board.uuid,
|
||||
PluginAction.action,
|
||||
PluginAction.parameters)
|
||||
return result
|
||||
|
||||
@expose.expose(wtypes.text, body=InjectionPlugin,
|
||||
status_code=200)
|
||||
def put(self, Injection):
|
||||
"""inject a plugin into a board.
|
||||
|
||||
:param plugin_ident: UUID or logical name of a plugin.
|
||||
:param board_ident: UUID or logical name of a board.
|
||||
"""
|
||||
|
||||
# cdict = context.to_policy_values()
|
||||
# policy.authorize('iot:plugin:inject', cdict, cdict)
|
||||
|
||||
rpc_plugin = api_utils.get_rpc_plugin(Injection.plugin)
|
||||
rpc_board = api_utils.get_rpc_board(self.board_ident)
|
||||
result = pecan.request.rpcapi.inject_plugin(pecan.request.context,
|
||||
rpc_plugin.uuid,
|
||||
rpc_board.uuid,
|
||||
Injection.onboot)
|
||||
return result
|
||||
|
||||
@expose.expose(wtypes.text, types.uuid_or_name,
|
||||
status_code=204)
|
||||
def delete(self, plugin_uuid):
|
||||
"""inject a plugin into a board.
|
||||
|
||||
:param plugin_ident: UUID or logical name of a plugin.
|
||||
:param board_ident: UUID or logical name of a board.
|
||||
"""
|
||||
|
||||
# cdict = context.to_policy_values()
|
||||
# policy.authorize('iot:plugin:remove', cdict, cdict)
|
||||
|
||||
rpc_plugin = api_utils.get_rpc_plugin(plugin_uuid)
|
||||
rpc_board = api_utils.get_rpc_board(self.board_ident)
|
||||
return pecan.request.rpcapi.remove_plugin(pecan.request.context,
|
||||
rpc_plugin.uuid,
|
||||
rpc_board.uuid)
|
||||
|
||||
|
||||
class BoardsController(rest.RestController):
|
||||
"""REST controller for Boards."""
|
||||
|
||||
_subcontroller_map = {
|
||||
'plugins': BoardPluginsController,
|
||||
}
|
||||
|
||||
invalid_sort_key_list = ['extra', 'location']
|
||||
|
||||
_custom_actions = {
|
||||
'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(board_ident=ident), remainder[1:]
|
||||
|
||||
def _get_boards_collection(self, marker, limit,
|
||||
sort_key, sort_dir,
|
||||
project=None,
|
||||
resource_url=None, 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.Board.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 = {}
|
||||
|
||||
# bounding the request to a project
|
||||
if project:
|
||||
if pecan.request.context.is_admin:
|
||||
filters['project_id'] = project
|
||||
else:
|
||||
msg = ("Project parameter can be used only "
|
||||
"by the administrator.")
|
||||
raise wsme.exc.ClientSideError(msg,
|
||||
status_code=400)
|
||||
else:
|
||||
filters['project_id'] = pecan.request.context.project_id
|
||||
|
||||
boards = objects.Board.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 BoardCollection.convert_with_links(boards, limit,
|
||||
url=resource_url,
|
||||
fields=fields,
|
||||
**parameters)
|
||||
|
||||
@expose.expose(Board, types.uuid_or_name, types.listtype)
|
||||
def get_one(self, board_ident, fields=None):
|
||||
"""Retrieve information about the given board.
|
||||
|
||||
:param board_ident: UUID or logical name of a board.
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned.
|
||||
"""
|
||||
|
||||
cdict = pecan.request.context.to_policy_values()
|
||||
policy.authorize('iot:board:get', cdict, cdict)
|
||||
|
||||
rpc_board = api_utils.get_rpc_board(board_ident)
|
||||
|
||||
return Board.convert_with_links(rpc_board, fields=fields)
|
||||
|
||||
@expose.expose(BoardCollection, types.uuid, int, wtypes.text,
|
||||
wtypes.text, types.listtype, wtypes.text)
|
||||
def get_all(self, marker=None,
|
||||
limit=None, sort_key='id', sort_dir='asc',
|
||||
fields=None):
|
||||
"""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.
|
||||
This value cannot be larger than the value of max_limit
|
||||
in the [api] section of the ironic configuration, or only
|
||||
max_limit resources will be returned.
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned.
|
||||
"""
|
||||
cdict = pecan.request.context.to_policy_values()
|
||||
policy.authorize('iot:board:get', cdict, cdict)
|
||||
|
||||
if fields is None:
|
||||
fields = _DEFAULT_RETURN_FIELDS
|
||||
return self._get_boards_collection(marker,
|
||||
limit, sort_key, sort_dir,
|
||||
fields=fields)
|
||||
|
||||
@expose.expose(Board, body=Board, status_code=201)
|
||||
def post(self, Board):
|
||||
"""Create a new Board.
|
||||
|
||||
:param Board: a Board within the request body.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
cdict = context.to_policy_values()
|
||||
policy.authorize('iot:board:create', cdict, cdict)
|
||||
|
||||
if not Board.name:
|
||||
raise exception.MissingParameterValue(
|
||||
("Name is not specified."))
|
||||
if not Board.code:
|
||||
raise exception.MissingParameterValue(
|
||||
("Code is not specified."))
|
||||
if not Board.location:
|
||||
raise exception.MissingParameterValue(
|
||||
("Location is not specified."))
|
||||
|
||||
if Board.name:
|
||||
if not api_utils.is_valid_board_name(Board.name):
|
||||
msg = ("Cannot create board with invalid name %(name)s")
|
||||
raise wsme.exc.ClientSideError(msg % {'name': Board.name},
|
||||
status_code=400)
|
||||
|
||||
new_Board = objects.Board(pecan.request.context,
|
||||
**Board.as_dict())
|
||||
|
||||
new_Board.owner = pecan.request.context.user_id
|
||||
new_Board.project = pecan.request.context.project_id
|
||||
|
||||
new_Location = objects.Location(pecan.request.context,
|
||||
**Board.location[0].as_dict())
|
||||
|
||||
new_Board = pecan.request.rpcapi.create_board(pecan.request.context,
|
||||
new_Board, new_Location)
|
||||
|
||||
return Board.convert_with_links(new_Board)
|
||||
|
||||
@expose.expose(None, types.uuid_or_name, status_code=204)
|
||||
def delete(self, board_ident):
|
||||
"""Delete a board.
|
||||
|
||||
:param board_ident: UUID or logical name of a board.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
cdict = context.to_policy_values()
|
||||
policy.authorize('iot:board:delete', cdict, cdict)
|
||||
|
||||
rpc_board = api_utils.get_rpc_board(board_ident)
|
||||
pecan.request.rpcapi.destroy_board(pecan.request.context,
|
||||
rpc_board.uuid)
|
||||
|
||||
@expose.expose(Board, types.uuid_or_name, body=Board, status_code=200)
|
||||
def patch(self, board_ident, val_Board):
|
||||
"""Update a board.
|
||||
|
||||
:param board_ident: UUID or logical name of a board.
|
||||
:param Board: values to be changed
|
||||
:return updated_board: updated_board
|
||||
"""
|
||||
|
||||
context = pecan.request.context
|
||||
cdict = context.to_policy_values()
|
||||
policy.authorize('iot:board:update', cdict, cdict)
|
||||
|
||||
board = api_utils.get_rpc_board(board_ident)
|
||||
val_Board = val_Board.as_dict()
|
||||
for key in val_Board:
|
||||
try:
|
||||
board[key] = val_Board[key]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
updated_board = pecan.request.rpcapi.update_board(
|
||||
pecan.request.context,
|
||||
board)
|
||||
return Board.convert_with_links(updated_board)
|
||||
|
||||
@expose.expose(BoardCollection, types.uuid, int, wtypes.text,
|
||||
wtypes.text, types.listtype, wtypes.text)
|
||||
def detail(self, marker=None,
|
||||
limit=None, sort_key='id', sort_dir='asc',
|
||||
fields=None, project=None):
|
||||
"""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.
|
||||
This value cannot be larger than the value of max_limit
|
||||
in the [api] section of the ironic configuration, or only
|
||||
max_limit resources will be returned.
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
:param project: Optional string value to get only boards
|
||||
of the project.
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned.
|
||||
"""
|
||||
|
||||
cdict = pecan.request.context.to_policy_values()
|
||||
policy.authorize('iot:board:get', cdict, cdict)
|
||||
|
||||
# /detail should only work against collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "boards":
|
||||
raise exception.HTTPNotFound()
|
||||
|
||||
return self._get_boards_collection(marker,
|
||||
limit, sort_key, sort_dir,
|
||||
project=project,
|
||||
fields=fields)
|
|
@ -1,323 +0,0 @@
|
|||
# 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.api.controllers import base
|
||||
from iotronic.api.controllers import link
|
||||
from iotronic.api.controllers.v1 import collection
|
||||
from iotronic.api.controllers.v1 import location as loc
|
||||
from iotronic.api.controllers.v1 import types
|
||||
from iotronic.api.controllers.v1 import utils as api_utils
|
||||
from iotronic.api import expose
|
||||
from iotronic.common import exception
|
||||
from iotronic.common import policy
|
||||
from iotronic import objects
|
||||
import pecan
|
||||
from pecan import rest
|
||||
import wsme
|
||||
from wsme import types as wtypes
|
||||
|
||||
_DEFAULT_RETURN_FIELDS = ('name', 'code', 'status', 'uuid', 'session', 'type')
|
||||
|
||||
|
||||
class Node(base.APIBase):
|
||||
"""API representation of a node.
|
||||
|
||||
"""
|
||||
uuid = types.uuid
|
||||
code = wsme.wsattr(wtypes.text)
|
||||
status = wsme.wsattr(wtypes.text)
|
||||
name = wsme.wsattr(wtypes.text)
|
||||
type = wsme.wsattr(wtypes.text)
|
||||
owner = types.uuid
|
||||
session = wsme.wsattr(wtypes.text)
|
||||
project = types.uuid
|
||||
mobile = types.boolean
|
||||
links = wsme.wsattr([link.Link], readonly=True)
|
||||
location = wsme.wsattr([loc.Location])
|
||||
extra = types.jsontype
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = []
|
||||
fields = list(objects.Node.fields)
|
||||
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))
|
||||
|
||||
@staticmethod
|
||||
def _convert_with_links(node, url, fields=None):
|
||||
node_uuid = node.uuid
|
||||
if fields is not None:
|
||||
node.unset_fields_except(fields)
|
||||
|
||||
# rel_name, url, resource, resource_args,
|
||||
# bookmark=False, type=wtypes.Unset
|
||||
node.links = [link.Link.make_link('self', url, 'nodes',
|
||||
node_uuid),
|
||||
link.Link.make_link('bookmark', url, 'nodes',
|
||||
node_uuid, bookmark=True)
|
||||
]
|
||||
return node
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_node, fields=None):
|
||||
node = Node(**rpc_node.as_dict())
|
||||
|
||||
try:
|
||||
session = objects.SessionWP.get_session_by_node_uuid(
|
||||
pecan.request.context, node.uuid)
|
||||
node.session = session.session_id
|
||||
except Exception:
|
||||
node.session = None
|
||||
|
||||
try:
|
||||
list_loc = objects.Location.list_by_node_uuid(
|
||||
pecan.request.context, node.uuid)
|
||||
node.location = loc.Location.convert_with_list(list_loc)
|
||||
except Exception:
|
||||
node.location = []
|
||||
|
||||
# to enable as soon as a better session and location management
|
||||
# is implemented
|
||||
# if fields is not None:
|
||||
# api_utils.check_for_invalid_fields(fields, node_dict)
|
||||
|
||||
return cls._convert_with_links(node,
|
||||
pecan.request.public_url,
|
||||
fields=fields)
|
||||
|
||||
|
||||
class NodeCollection(collection.Collection):
|
||||
"""API representation of a collection of nodes."""
|
||||
|
||||
nodes = [Node]
|
||||
"""A list containing nodes objects"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._type = 'nodes'
|
||||
|
||||
@staticmethod
|
||||
def convert_with_links(nodes, limit, url=None, fields=None, **kwargs):
|
||||
collection = NodeCollection()
|
||||
collection.nodes = [Node.convert_with_links(n, fields=fields)
|
||||
for n in nodes]
|
||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||
return collection
|
||||
|
||||
|
||||
class NodesController(rest.RestController):
|
||||
"""REST controller for Nodes."""
|
||||
|
||||
invalid_sort_key_list = ['extra', 'location']
|
||||
|
||||
_custom_actions = {
|
||||
'detail': ['GET'],
|
||||
}
|
||||
|
||||
def _get_nodes_collection(self, marker, limit,
|
||||
sort_key, sort_dir,
|
||||
project=None,
|
||||
resource_url=None, 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.Node.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 = {}
|
||||
|
||||
# bounding the request to a project
|
||||
if project:
|
||||
if pecan.request.context.is_admin:
|
||||
filters['project_id'] = project
|
||||
else:
|
||||
msg = ("Project parameter can be used only "
|
||||
"by the administrator.")
|
||||
raise wsme.exc.ClientSideError(msg,
|
||||
status_code=400)
|
||||
else:
|
||||
filters['project_id'] = pecan.request.context.project_id
|
||||
|
||||
nodes = objects.Node.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 NodeCollection.convert_with_links(nodes, limit,
|
||||
url=resource_url,
|
||||
fields=fields,
|
||||
**parameters)
|
||||
|
||||
@expose.expose(Node, types.uuid_or_name, types.listtype)
|
||||
def get_one(self, node_ident, fields=None):
|
||||
"""Retrieve information about the given node.
|
||||
|
||||
:param node_ident: UUID or logical name of a node.
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned.
|
||||
"""
|
||||
|
||||
cdict = pecan.request.context.to_policy_values()
|
||||
policy.authorize('iot:node:get', cdict, cdict)
|
||||
|
||||
rpc_node = api_utils.get_rpc_node(node_ident)
|
||||
|
||||
return Node.convert_with_links(rpc_node, fields=fields)
|
||||
|
||||
@expose.expose(NodeCollection, types.uuid, int, wtypes.text,
|
||||
wtypes.text, types.listtype, wtypes.text)
|
||||
def get_all(self, marker=None,
|
||||
limit=None, sort_key='id', sort_dir='asc',
|
||||
fields=None):
|
||||
"""Retrieve a list of nodes.
|
||||
|
||||
:param marker: pagination marker for large data sets.
|
||||
:param limit: maximum number of resources to return in a single result.
|
||||
This value cannot be larger than the value of max_limit
|
||||
in the [api] section of the ironic configuration, or only
|
||||
max_limit resources will be returned.
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned.
|
||||
"""
|
||||
cdict = pecan.request.context.to_policy_values()
|
||||
policy.authorize('iot:node:get', cdict, cdict)
|
||||
|
||||
if fields is None:
|
||||
fields = _DEFAULT_RETURN_FIELDS
|
||||
return self._get_nodes_collection(marker,
|
||||
limit, sort_key, sort_dir,
|
||||
fields=fields)
|
||||
|
||||
@expose.expose(Node, body=Node, status_code=201)
|
||||
def post(self, Node):
|
||||
"""Create a new Node.
|
||||
|
||||
:param Node: a Node within the request body.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
cdict = context.to_policy_values()
|
||||
policy.authorize('iot:node:create', cdict, cdict)
|
||||
|
||||
if not Node.name:
|
||||
raise exception.MissingParameterValue(
|
||||
("Name is not specified."))
|
||||
if not Node.code:
|
||||
raise exception.MissingParameterValue(
|
||||
("Code is not specified."))
|
||||
if not Node.location:
|
||||
raise exception.MissingParameterValue(
|
||||
("Location is not specified."))
|
||||
|
||||
if Node.name:
|
||||
if not api_utils.is_valid_node_name(Node.name):
|
||||
msg = ("Cannot create node with invalid name %(name)s")
|
||||
raise wsme.exc.ClientSideError(msg % {'name': Node.name},
|
||||
status_code=400)
|
||||
|
||||
new_Node = objects.Node(pecan.request.context,
|
||||
**Node.as_dict())
|
||||
|
||||
new_Node.owner = pecan.request.context.user_id
|
||||
new_Node.project = pecan.request.context.project_id
|
||||
|
||||
new_Location = objects.Location(pecan.request.context,
|
||||
**Node.location[0].as_dict())
|
||||
|
||||
new_Node = pecan.request.rpcapi.create_node(pecan.request.context,
|
||||
new_Node, new_Location)
|
||||
|
||||
return Node.convert_with_links(new_Node)
|
||||
|
||||
@expose.expose(None, types.uuid_or_name, status_code=204)
|
||||
def delete(self, node_ident):
|
||||
"""Delete a node.
|
||||
|
||||
:param node_ident: UUID or logical name of a node.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
cdict = context.to_policy_values()
|
||||
policy.authorize('iot:node:delete', cdict, cdict)
|
||||
|
||||
rpc_node = api_utils.get_rpc_node(node_ident)
|
||||
pecan.request.rpcapi.destroy_node(pecan.request.context,
|
||||
rpc_node.uuid)
|
||||
|
||||
@expose.expose(Node, types.uuid_or_name, body=Node, status_code=200)
|
||||
def patch(self, node_ident, val_Node):
|
||||
"""Update a node.
|
||||
|
||||
:param node_ident: UUID or logical name of a node.
|
||||
:param Node: values to be changed
|
||||
:return updated_node: updated_node
|
||||
"""
|
||||
|
||||
context = pecan.request.context
|
||||
cdict = context.to_policy_values()
|
||||
policy.authorize('iot:node:update', cdict, cdict)
|
||||
|
||||
node = api_utils.get_rpc_node(node_ident)
|
||||
val_Node = val_Node.as_dict()
|
||||
for key in val_Node:
|
||||
try:
|
||||
node[key] = val_Node[key]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
updated_node = pecan.request.rpcapi.update_node(pecan.request.context,
|
||||
node)
|
||||
return Node.convert_with_links(updated_node)
|
||||
|
||||
@expose.expose(NodeCollection, types.uuid, int, wtypes.text,
|
||||
wtypes.text, types.listtype, wtypes.text)
|
||||
def detail(self, marker=None,
|
||||
limit=None, sort_key='id', sort_dir='asc',
|
||||
fields=None, project=None):
|
||||
"""Retrieve a list of nodes.
|
||||
|
||||
:param marker: pagination marker for large data sets.
|
||||
:param limit: maximum number of resources to return in a single result.
|
||||
This value cannot be larger than the value of max_limit
|
||||
in the [api] section of the ironic configuration, or only
|
||||
max_limit resources will be returned.
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
:param project: Optional string value to get only nodes of the project.
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned.
|
||||
"""
|
||||
|
||||
cdict = pecan.request.context.to_policy_values()
|
||||
policy.authorize('iot:node:get', cdict, cdict)
|
||||
|
||||
# /detail should only work against collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "nodes":
|
||||
raise exception.HTTPNotFound()
|
||||
|
||||
return self._get_nodes_collection(marker,
|
||||
limit, sort_key, sort_dir,
|
||||
project=project,
|
||||
fields=fields)
|
|
@ -26,7 +26,7 @@ from pecan import rest
|
|||
import wsme
|
||||
from wsme import types as wtypes
|
||||
|
||||
_DEFAULT_RETURN_FIELDS = ('name', 'uuid', 'owner', 'public')
|
||||
_DEFAULT_RETURN_FIELDS = ('name', 'uuid', 'owner', 'public', 'callable')
|
||||
|
||||
|
||||
class Plugin(base.APIBase):
|
||||
|
@ -35,9 +35,10 @@ class Plugin(base.APIBase):
|
|||
"""
|
||||
uuid = types.uuid
|
||||
name = wsme.wsattr(wtypes.text)
|
||||
config = wsme.wsattr(wtypes.text)
|
||||
code = wsme.wsattr(wtypes.text)
|
||||
public = types.boolean
|
||||
owner = types.uuid
|
||||
callable = types.boolean
|
||||
links = wsme.wsattr([link.Link], readonly=True)
|
||||
extra = types.jsontype
|
||||
|
||||
|
@ -254,24 +255,6 @@ class PluginsController(rest.RestController):
|
|||
pecan.request.context, plugin)
|
||||
return Plugin.convert_with_links(updated_plugin)
|
||||
|
||||
@expose.expose(None, types.uuid_or_name, types.uuid_or_name,
|
||||
status_code=200)
|
||||
def put(self, plugin_ident, node_ident):
|
||||
"""inject a plugin into a node.
|
||||
|
||||
:param plugin_ident: UUID or logical name of a plugin.
|
||||
:param node_ident: UUID or logical name of a node.
|
||||
"""
|
||||
|
||||
context = pecan.request.context
|
||||
cdict = context.to_policy_values()
|
||||
policy.authorize('iot:plugin:inject', cdict, cdict)
|
||||
|
||||
rpc_plugin = api_utils.get_rpc_plugin(plugin_ident)
|
||||
rpc_node = api_utils.get_rpc_node(node_ident)
|
||||
pecan.request.rpcapi.inject_plugin(pecan.request.context,
|
||||
rpc_plugin.uuid, rpc_node.uuid)
|
||||
|
||||
@expose.expose(PluginCollection, types.uuid, int, wtypes.text,
|
||||
wtypes.text, types.listtype, types.boolean, types.boolean)
|
||||
def detail(self, marker=None,
|
||||
|
|
|
@ -65,34 +65,34 @@ def get_patch_value(patch, path):
|
|||
return p['value']
|
||||
|
||||
|
||||
def allow_node_logical_names():
|
||||
def allow_board_logical_names():
|
||||
# v1.5 added logical name aliases
|
||||
return pecan.request.version.minor >= 5
|
||||
|
||||
|
||||
def get_rpc_node(node_ident):
|
||||
"""Get the RPC node from the node uuid or logical name.
|
||||
def get_rpc_board(board_ident):
|
||||
"""Get the RPC board from the board uuid or logical name.
|
||||
|
||||
:param node_ident: the UUID or logical name of a node.
|
||||
:param board_ident: the UUID or logical name of a board.
|
||||
|
||||
:returns: The RPC Node.
|
||||
:returns: The RPC Board.
|
||||
:raises: InvalidUuidOrName if the name or uuid provided is not valid.
|
||||
:raises: NodeNotFound if the node is not found.
|
||||
:raises: BoardNotFound if the board is not found.
|
||||
"""
|
||||
# Check to see if the node_ident is a valid UUID. If it is, treat it
|
||||
# Check to see if the board_ident is a valid UUID. If it is, treat it
|
||||
# as a UUID.
|
||||
if uuidutils.is_uuid_like(node_ident):
|
||||
return objects.Node.get_by_uuid(pecan.request.context, node_ident)
|
||||
if uuidutils.is_uuid_like(board_ident):
|
||||
return objects.Board.get_by_uuid(pecan.request.context, board_ident)
|
||||
|
||||
# We can refer to nodes by their name, if the client supports it
|
||||
# if allow_node_logical_names():
|
||||
# if utils.is_hostname_safe(node_ident):
|
||||
# We can refer to boards by their name, if the client supports it
|
||||
# if allow_board_logical_names():
|
||||
# if utils.is_hostname_safe(board_ident):
|
||||
else:
|
||||
return objects.Node.get_by_name(pecan.request.context, node_ident)
|
||||
return objects.Board.get_by_name(pecan.request.context, board_ident)
|
||||
|
||||
raise exception.InvalidUuidOrName(name=node_ident)
|
||||
raise exception.InvalidUuidOrName(name=board_ident)
|
||||
|
||||
raise exception.NodeNotFound(node=node_ident)
|
||||
raise exception.BoardNotFound(board=board_ident)
|
||||
|
||||
|
||||
def get_rpc_plugin(plugin_ident):
|
||||
|
@ -120,12 +120,12 @@ def get_rpc_plugin(plugin_ident):
|
|||
raise exception.PluginNotFound(plugin=plugin_ident)
|
||||
|
||||
|
||||
def is_valid_node_name(name):
|
||||
"""Determine if the provided name is a valid node name.
|
||||
def is_valid_board_name(name):
|
||||
"""Determine if the provided name is a valid board name.
|
||||
|
||||
Check to see that the provided node name is valid, and isn't a UUID.
|
||||
Check to see that the provided board name is valid, and isn't a UUID.
|
||||
|
||||
:param: name: the node name to check.
|
||||
:param: name: the board name to check.
|
||||
:returns: True if the name is valid, False otherwise.
|
||||
"""
|
||||
return utils.is_hostname_safe(name) and (not uuidutils.is_uuid_like(name))
|
||||
|
@ -134,9 +134,9 @@ def is_valid_node_name(name):
|
|||
def is_valid_name(name):
|
||||
"""Determine if the provided name is a valid name.
|
||||
|
||||
Check to see that the provided node name isn't a UUID.
|
||||
Check to see that the provided board name isn't a UUID.
|
||||
|
||||
:param: name: the node name to check.
|
||||
:param: name: the board name to check.
|
||||
:returns: True if the name is valid, False otherwise.
|
||||
"""
|
||||
return not uuidutils.is_uuid_like(name)
|
||||
|
|
|
@ -138,8 +138,8 @@ class InvalidState(Conflict):
|
|||
message = _("Invalid resource state.")
|
||||
|
||||
|
||||
class NodeAlreadyExists(Conflict):
|
||||
message = _("A node with UUID %(uuid)s already exists.")
|
||||
class BoardAlreadyExists(Conflict):
|
||||
message = _("A board with UUID %(uuid)s already exists.")
|
||||
|
||||
|
||||
class MACAlreadyExists(Conflict):
|
||||
|
@ -150,17 +150,12 @@ class PortAlreadyExists(Conflict):
|
|||
message = _("A port with UUID %(uuid)s already exists.")
|
||||
|
||||
|
||||
class InstanceAssociated(Conflict):
|
||||
message = _("Instance %(instance_uuid)s is already associated with a node,"
|
||||
" it cannot be associated with this other node %(node)s")
|
||||
|
||||
|
||||
class DuplicateName(Conflict):
|
||||
message = _("A node with name %(name)s already exists.")
|
||||
message = _("A board with name %(name)s already exists.")
|
||||
|
||||
|
||||
class DuplicateCode(Conflict):
|
||||
message = _("A node with code %(code)s already exists.")
|
||||
message = _("A board with code %(code)s already exists.")
|
||||
|
||||
|
||||
class InvalidUUID(Invalid):
|
||||
|
@ -185,7 +180,7 @@ class InvalidMAC(Invalid):
|
|||
|
||||
class InvalidStateRequested(Invalid):
|
||||
message = _('The requested action "%(action)s" can not be performed '
|
||||
'on node "%(node)s" while it is in state "%(state)s".')
|
||||
'on board "%(board)s" while it is in state "%(state)s".')
|
||||
|
||||
|
||||
class PatchError(Invalid):
|
||||
|
@ -244,16 +239,16 @@ class InstanceNotFound(NotFound):
|
|||
message = _("Instance %(instance)s could not be found.")
|
||||
|
||||
|
||||
class NodeNotFound(NotFound):
|
||||
message = _("Node %(node)s could not be found.")
|
||||
class BoardNotFound(NotFound):
|
||||
message = _("Board %(board)s could not be found.")
|
||||
|
||||
|
||||
class NodeNotConnected(Invalid):
|
||||
message = _("Node %(node)s is not connected.")
|
||||
class BoardNotConnected(Invalid):
|
||||
message = _("Board %(board)s is not connected.")
|
||||
|
||||
|
||||
class NodeAssociated(InvalidState):
|
||||
message = _("Node %(node)s is associated with instance %(instance)s.")
|
||||
class BoardAssociated(InvalidState):
|
||||
message = _("Board %(board)s is associated with instance %(instance)s.")
|
||||
|
||||
|
||||
class PortNotFound(NotFound):
|
||||
|
@ -302,7 +297,7 @@ class WampAgentAlreadyRegistered(IotronicException):
|
|||
|
||||
|
||||
class PowerStateFailure(InvalidState):
|
||||
message = _("Failed to set node power state to %(pstate)s.")
|
||||
message = _("Failed to set board power state to %(pstate)s.")
|
||||
|
||||
|
||||
class ExclusiveLockRequired(NotAuthorized):
|
||||
|
@ -310,18 +305,18 @@ class ExclusiveLockRequired(NotAuthorized):
|
|||
"but the current context has a shared lock.")
|
||||
|
||||
|
||||
class NodeMaintenanceFailure(Invalid):
|
||||
class BoardMaintenanceFailure(Invalid):
|
||||
message = _("Failed to toggle maintenance-mode flag "
|
||||
"for node %(node)s: %(reason)s")
|
||||
"for board %(board)s: %(reason)s")
|
||||
|
||||
|
||||
class NodeConsoleNotEnabled(Invalid):
|
||||
message = _("Console access is not enabled on node %(node)s")
|
||||
class BoardConsoleNotEnabled(Invalid):
|
||||
message = _("Console access is not enabled on board %(board)s")
|
||||
|
||||
|
||||
class NodeInMaintenance(Invalid):
|
||||
message = _("The %(op)s operation can't be performed on node "
|
||||
"%(node)s because it's in maintenance mode.")
|
||||
class BoardInMaintenance(Invalid):
|
||||
message = _("The %(op)s operation can't be performed on board "
|
||||
"%(board)s because it's in maintenance mode.")
|
||||
|
||||
|
||||
class IPMIFailure(IotronicException):
|
||||
|
@ -436,13 +431,13 @@ class ConfigNotFound(IotronicException):
|
|||
message = _("Could not find config at %(path)s")
|
||||
|
||||
|
||||
class NodeLocked(Conflict):
|
||||
message = _("Node %(node)s is locked by host %(host)s, please retry "
|
||||
class BoardLocked(Conflict):
|
||||
message = _("Board %(board)s is locked by host %(host)s, please retry "
|
||||
"after the current operation is completed.")
|
||||
|
||||
|
||||
class NodeNotLocked(Invalid):
|
||||
message = _("Node %(node)s found not to be locked on release")
|
||||
class BoardNotLocked(Invalid):
|
||||
message = _("Board %(board)s found not to be locked on release")
|
||||
|
||||
|
||||
class NoFreeConductorWorker(TemporaryFailure):
|
||||
|
@ -523,12 +518,12 @@ class DracInvalidFilterDialect(IotronicException):
|
|||
|
||||
|
||||
class FailedToGetSensorData(IotronicException):
|
||||
message = _("Failed to get sensor data for node %(node)s. "
|
||||
message = _("Failed to get sensor data for board %(board)s. "
|
||||
"Error: %(error)s")
|
||||
|
||||
|
||||
class FailedToParseSensorData(IotronicException):
|
||||
message = _("Failed to parse sensor data for node %(node)s. "
|
||||
message = _("Failed to parse sensor data for board %(board)s. "
|
||||
"Error: %(error)s")
|
||||
|
||||
|
||||
|
@ -568,8 +563,8 @@ class HardwareInspectionFailure(IotronicException):
|
|||
message = _("Failed to inspect hardware. Reason: %(error)s")
|
||||
|
||||
|
||||
class NodeCleaningFailure(IotronicException):
|
||||
message = _("Failed to clean node %(node)s: %(reason)s")
|
||||
class BoardCleaningFailure(IotronicException):
|
||||
message = _("Failed to clean board %(board)s: %(reason)s")
|
||||
|
||||
|
||||
class PathNotFound(IotronicException):
|
||||
|
@ -582,3 +577,15 @@ class DirectoryNotWritable(IotronicException):
|
|||
|
||||
class PluginNotFound(NotFound):
|
||||
message = _("Plugin %(plugin)s could not be found.")
|
||||
|
||||
|
||||
class InjectionPluginNotFound(NotFound):
|
||||
message = _("InjectionPlugin could not be found.")
|
||||
|
||||
|
||||
class InvalidPluginAction(Invalid):
|
||||
message = _("Invalid Action %(action)s for the plugin.")
|
||||
|
||||
|
||||
class NeedParams(Invalid):
|
||||
message = _("Action %(action)s needs parameters.")
|
||||
|
|
|
@ -70,21 +70,21 @@ default_policies = [
|
|||
# All of these may be overridden by configuration, but we can
|
||||
# depend on their existence throughout the code.
|
||||
|
||||
node_policies = [
|
||||
policy.RuleDefault('iot:node:get',
|
||||
board_policies = [
|
||||
policy.RuleDefault('iot:board:get',
|
||||
'rule:is_admin or rule:is_iot_member',
|
||||
description='Retrieve Node records'),
|
||||
policy.RuleDefault('iot:node:create',
|
||||
description='Retrieve Board records'),
|
||||
policy.RuleDefault('iot:board:create',
|
||||
'rule:is_admin_iot_project',
|
||||
description='Create Node records'),
|
||||
policy.RuleDefault('iot:node:delete',
|
||||
description='Create Board records'),
|
||||
policy.RuleDefault('iot:board:delete',
|
||||
'rule:is_admin or rule:is_admin_iot_project '
|
||||
'or rule:is_manager_iot_project',
|
||||
description='Delete Node records'),
|
||||
policy.RuleDefault('iot:node:update',
|
||||
description='Delete Board records'),
|
||||
policy.RuleDefault('iot:board:update',
|
||||
'rule:is_admin or rule:is_admin_iot_project '
|
||||
'or rule:is_manager_iot_project',
|
||||
description='Update Node records'),
|
||||
description='Update Board records'),
|
||||
|
||||
]
|
||||
|
||||
|
@ -111,7 +111,7 @@ plugin_policies = [
|
|||
|
||||
def list_policies():
|
||||
policies = (default_policies
|
||||
+ node_policies
|
||||
+ board_policies
|
||||
+ plugin_policies
|
||||
)
|
||||
return policies
|
||||
|
|
|
@ -37,9 +37,9 @@ service_opts = [
|
|||
help='Seconds between running periodic tasks.'),
|
||||
cfg.StrOpt('host',
|
||||
default=socket.getfqdn(),
|
||||
help='Name of this node. This can be an opaque identifier. '
|
||||
help='Name of this board. This can be an opaque identifier. '
|
||||
'It is not necessarily a hostname, FQDN, or IP address. '
|
||||
'However, the node name must be valid within '
|
||||
'However, the board name must be valid within '
|
||||
'an AMQP key, and if using ZeroMQ, a valid '
|
||||
'hostname, FQDN, or IP address.'),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# Copyright (c) 2012 NTT DOCOMO, INC.
|
||||
# Copyright 2010 OpenStack Foundation
|
||||
# Copyright 2017 MDSLAB - University of Messina
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -14,288 +13,11 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Mapping of bare metal node states.
|
||||
|
||||
Setting the node `power_state` is handled by the conductor's power
|
||||
synchronization thread. Based on the power state retrieved from the driver
|
||||
for the node, the state is set to POWER_ON or POWER_OFF, accordingly.
|
||||
Should this fail, the `power_state` value is left unchanged, and the node
|
||||
is placed into maintenance mode.
|
||||
# from oslo_log import log as logging
|
||||
|
||||
The `power_state` can also be set manually via the API. A failure to change
|
||||
the state leaves the current state unchanged. The node is NOT placed into
|
||||
maintenance mode in this case.
|
||||
"""
|
||||
# LOG = logging.getLogger(__name__)
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from iotronic.common import fsm
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
#####################
|
||||
# Provisioning states
|
||||
#####################
|
||||
|
||||
# TODO(deva): add add'l state mappings here
|
||||
VERBS = {
|
||||
'active': 'deploy',
|
||||
'deleted': 'delete',
|
||||
'manage': 'manage',
|
||||
'provide': 'provide',
|
||||
'inspect': 'inspect',
|
||||
}
|
||||
""" Mapping of state-changing events that are PUT to the REST API
|
||||
|
||||
This is a mapping of target states which are PUT to the API, eg,
|
||||
PUT /v1/node/states/provision {'target': 'active'}
|
||||
|
||||
The dict format is:
|
||||
{target string used by the API: internal verb}
|
||||
|
||||
This provides a reference set of supported actions, and in the future
|
||||
may be used to support renaming these actions.
|
||||
"""
|
||||
|
||||
NOSTATE = None
|
||||
""" No state information.
|
||||
|
||||
This state is used with power_state to represent a lack of knowledge of
|
||||
power state, and in target_*_state fields when there is no target.
|
||||
"""
|
||||
|
||||
MANAGEABLE = 'manageable'
|
||||
""" Node is in a manageable state.
|
||||
|
||||
This state indicates that Iotronic has verified, at least once, that it had
|
||||
sufficient information to manage the hardware. While in this state, the node
|
||||
is not available for provisioning (it must be in the AVAILABLE state for that).
|
||||
"""
|
||||
|
||||
AVAILABLE = 'available'
|
||||
""" Node is available for use and scheduling.
|
||||
|
||||
This state is replacing the NOSTATE state used prior to Kilo.
|
||||
"""
|
||||
|
||||
ACTIVE = 'active'
|
||||
""" Node is successfully deployed and associated with an instance. """
|
||||
|
||||
DEPLOYWAIT = 'wait call-back'
|
||||
""" Node is waiting to be deployed.
|
||||
|
||||
This will be the node `provision_state` while the node is waiting for
|
||||
the driver to finish deployment.
|
||||
"""
|
||||
|
||||
DEPLOYING = 'deploying'
|
||||
""" Node is ready to receive a deploy request, or is currently being deployed.
|
||||
|
||||
A node will have its `provision_state` set to DEPLOYING briefly before it
|
||||
receives its initial deploy request. It will also move to this state from
|
||||
DEPLOYWAIT after the callback is triggered and deployment is continued
|
||||
(disk partitioning and image copying).
|
||||
"""
|
||||
|
||||
DEPLOYFAIL = 'deploy failed'
|
||||
""" Node deployment failed. """
|
||||
|
||||
DEPLOYDONE = 'deploy complete'
|
||||
""" Node was successfully deployed.
|
||||
|
||||
This is mainly a target provision state used during deployment. A successfully
|
||||
deployed node should go to ACTIVE status.
|
||||
"""
|
||||
|
||||
DELETING = 'deleting'
|
||||
""" Node is actively being torn down. """
|
||||
|
||||
DELETED = 'deleted'
|
||||
""" Node tear down was successful.
|
||||
|
||||
In Juno, target_provision_state was set to this value during node tear down.
|
||||
|
||||
In Kilo, this will be a transitory value of provision_state, and never
|
||||
represented in target_provision_state.
|
||||
"""
|
||||
|
||||
CLEANING = 'cleaning'
|
||||
""" Node is being automatically cleaned to prepare it for provisioning. """
|
||||
|
||||
CLEANFAIL = 'clean failed'
|
||||
""" Node failed cleaning. This requires operator intervention to resolve. """
|
||||
|
||||
ERROR = 'error'
|
||||
""" An error occurred during node processing.
|
||||
|
||||
The `last_error` attribute of the node details should contain an error message.
|
||||
"""
|
||||
|
||||
REBUILD = 'rebuild'
|
||||
""" Node is to be rebuilt.
|
||||
|
||||
This is not used as a state, but rather as a "verb" when changing the node's
|
||||
provision_state via the REST API.
|
||||
"""
|
||||
|
||||
INSPECTING = 'inspecting'
|
||||
""" Node is under inspection.
|
||||
|
||||
This is the provision state used when inspection is started. A successfully
|
||||
inspected node shall transition to MANAGEABLE status.
|
||||
"""
|
||||
|
||||
INSPECTFAIL = 'inspect failed'
|
||||
""" Node inspection failed. """
|
||||
|
||||
UPDATE_ALLOWED_STATES = (DEPLOYFAIL, INSPECTING, INSPECTFAIL, CLEANFAIL)
|
||||
"""Transitional states in which we allow updating a node."""
|
||||
|
||||
# NEW
|
||||
OPERATIVE = 'operative'
|
||||
MAINTENANCE = 'maintenance'
|
||||
OFFLINE = 'offline'
|
||||
REGISTERED = 'registered'
|
||||
|
||||
##############
|
||||
# Power states
|
||||
##############
|
||||
|
||||
POWER_ON = 'power on'
|
||||
""" Node is powered on. """
|
||||
|
||||
POWER_OFF = 'power off'
|
||||
""" Node is powered off. """
|
||||
|
||||
REBOOT = 'rebooting'
|
||||
""" Node is rebooting. """
|
||||
|
||||
|
||||
#####################
|
||||
# State machine model
|
||||
#####################
|
||||
def on_exit(old_state, event):
|
||||
"""Used to log when a state is exited."""
|
||||
LOG.debug("Exiting old state '%s' in response to event '%s'",
|
||||
old_state, event)
|
||||
|
||||
|
||||
def on_enter(new_state, event):
|
||||
"""Used to log when entering a state."""
|
||||
LOG.debug("Entering new state '%s' in response to event '%s'",
|
||||
new_state, event)
|
||||
|
||||
|
||||
watchers = {}
|
||||
watchers['on_exit'] = on_exit
|
||||
watchers['on_enter'] = on_enter
|
||||
|
||||
machine = fsm.FSM()
|
||||
|
||||
# Add stable states
|
||||
machine.add_state(MANAGEABLE, stable=True, **watchers)
|
||||
machine.add_state(AVAILABLE, stable=True, **watchers)
|
||||
machine.add_state(ACTIVE, stable=True, **watchers)
|
||||
machine.add_state(ERROR, stable=True, **watchers)
|
||||
|
||||
# Add deploy* states
|
||||
# NOTE(deva): Juno shows a target_provision_state of DEPLOYDONE
|
||||
# this is changed in Kilo to ACTIVE
|
||||
machine.add_state(DEPLOYING, target=ACTIVE, **watchers)
|
||||
machine.add_state(DEPLOYWAIT, target=ACTIVE, **watchers)
|
||||
machine.add_state(DEPLOYFAIL, target=ACTIVE, **watchers)
|
||||
|
||||
# Add clean* states
|
||||
machine.add_state(CLEANING, target=AVAILABLE, **watchers)
|
||||
machine.add_state(CLEANFAIL, target=AVAILABLE, **watchers)
|
||||
|
||||
# Add delete* states
|
||||
machine.add_state(DELETING, target=AVAILABLE, **watchers)
|
||||
|
||||
# From AVAILABLE, a deployment may be started
|
||||
machine.add_transition(AVAILABLE, DEPLOYING, 'deploy')
|
||||
|
||||
# Add inspect* states.
|
||||
machine.add_state(INSPECTING, target=MANAGEABLE, **watchers)
|
||||
machine.add_state(INSPECTFAIL, target=MANAGEABLE, **watchers)
|
||||
|
||||
# A deployment may fail
|
||||
machine.add_transition(DEPLOYING, DEPLOYFAIL, 'fail')
|
||||
|
||||
# A failed deployment may be retried
|
||||
# iotronic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(DEPLOYFAIL, DEPLOYING, 'rebuild')
|
||||
# NOTE(deva): Juno allows a client to send "active" to initiate a rebuild
|
||||
machine.add_transition(DEPLOYFAIL, DEPLOYING, 'deploy')
|
||||
|
||||
# A deployment may also wait on external callbacks
|
||||
machine.add_transition(DEPLOYING, DEPLOYWAIT, 'wait')
|
||||
machine.add_transition(DEPLOYWAIT, DEPLOYING, 'resume')
|
||||
|
||||
# A deployment waiting on callback may time out
|
||||
machine.add_transition(DEPLOYWAIT, DEPLOYFAIL, 'fail')
|
||||
|
||||
# A deployment may complete
|
||||
machine.add_transition(DEPLOYING, ACTIVE, 'done')
|
||||
|
||||
# An active instance may be re-deployed
|
||||
# iotronic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(ACTIVE, DEPLOYING, 'rebuild')
|
||||
|
||||
# An active instance may be deleted
|
||||
# iotronic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(ACTIVE, DELETING, 'delete')
|
||||
|
||||
# While a deployment is waiting, it may be deleted
|
||||
# iotronic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(DEPLOYWAIT, DELETING, 'delete')
|
||||
|
||||
# A failed deployment may also be deleted
|
||||
# iotronic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(DEPLOYFAIL, DELETING, 'delete')
|
||||
|
||||
# This state can also transition to error
|
||||
machine.add_transition(DELETING, ERROR, 'error')
|
||||
|
||||
# When finished deleting, a node will begin cleaning
|
||||
machine.add_transition(DELETING, CLEANING, 'clean')
|
||||
|
||||
# If cleaning succeeds, it becomes available for scheduling
|
||||
machine.add_transition(CLEANING, AVAILABLE, 'done')
|
||||
|
||||
# If cleaning fails, wait for operator intervention
|
||||
machine.add_transition(CLEANING, CLEANFAIL, 'fail')
|
||||
|
||||
# An operator may want to move a CLEANFAIL node to MANAGEABLE, to perform
|
||||
# other actions like zapping
|
||||
machine.add_transition(CLEANFAIL, MANAGEABLE, 'manage')
|
||||
|
||||
# From MANAGEABLE, a node may move to available after going through cleaning
|
||||
machine.add_transition(MANAGEABLE, CLEANING, 'provide')
|
||||
|
||||
# From AVAILABLE, a node may be made unavailable by managing it
|
||||
machine.add_transition(AVAILABLE, MANAGEABLE, 'manage')
|
||||
|
||||
# An errored instance can be rebuilt
|
||||
# iotronic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(ERROR, DEPLOYING, 'rebuild')
|
||||
# or deleted
|
||||
# iotronic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(ERROR, DELETING, 'delete')
|
||||
|
||||
# Added transitions for inspection.
|
||||
# Initiate inspection.
|
||||
machine.add_transition(MANAGEABLE, INSPECTING, 'inspect')
|
||||
|
||||
# iotronic/conductor/manager.py:inspect_hardware().
|
||||
machine.add_transition(INSPECTING, MANAGEABLE, 'done')
|
||||
|
||||
# Inspection may fail.
|
||||
machine.add_transition(INSPECTING, INSPECTFAIL, 'fail')
|
||||
|
||||
# Move the node to manageable state for any other
|
||||
# action.
|
||||
machine.add_transition(INSPECTFAIL, MANAGEABLE, 'manage')
|
||||
|
||||
# Reinitiate the inspect after inspectfail.
|
||||
machine.add_transition(INSPECTFAIL, INSPECTING, 'inspect')
|
||||
ONLINE = 'online'
|
||||
|
|
|
@ -596,7 +596,7 @@ def is_valid_logical_name(hostname):
|
|||
|
||||
|
||||
def is_hostname_safe(hostname):
|
||||
"""Old check for valid logical node names.
|
||||
"""Old check for valid logical board names.
|
||||
|
||||
Retained for compatibility with REST API < 1.10.
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ from oslo_config import cfg
|
|||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
|
||||
|
||||
import random
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -55,131 +54,138 @@ class ConductorEndpoint(object):
|
|||
LOG.debug('Received registration from %s with session %s',
|
||||
uuid, session_num)
|
||||
try:
|
||||
node = objects.Node.get_by_uuid(ctx, uuid)
|
||||
board = objects.Board.get_by_uuid(ctx, uuid)
|
||||
except Exception as exc:
|
||||
msg = exc.message % {'node': uuid}
|
||||
msg = exc.message % {'board': uuid}
|
||||
LOG.error(msg)
|
||||
wmessage = wm.WampError(msg).serialize()
|
||||
return wmessage
|
||||
return wm.WampError(msg).serialize()
|
||||
|
||||
try:
|
||||
old_ses = objects.SessionWP(ctx)
|
||||
old_ses = old_ses.get_session_by_node_uuid(ctx, node.uuid,
|
||||
valid=True)
|
||||
old_ses = old_ses.get_session_by_board_uuid(ctx, board.uuid,
|
||||
valid=True)
|
||||
old_ses.valid = False
|
||||
old_ses.save()
|
||||
|
||||
except Exception:
|
||||
LOG.debug('valid session for %s not found', node.uuid)
|
||||
LOG.debug('valid session for %s not found', board.uuid)
|
||||
|
||||
node.status = states.REGISTERED
|
||||
node.save()
|
||||
|
||||
session = objects.SessionWP(ctx)
|
||||
session.node_id = node.id
|
||||
session.node_uuid = node.uuid
|
||||
session.session_id = session_num
|
||||
session_data = {'board_id': board.id,
|
||||
'board_uuid': board.uuid,
|
||||
'session_id': session_num}
|
||||
session = objects.SessionWP(ctx, **session_data)
|
||||
session.create()
|
||||
session.save()
|
||||
board.status = states.ONLINE
|
||||
board.save()
|
||||
LOG.debug('Board %s is now %s', board.uuid, states.ONLINE)
|
||||
return wm.WampSuccess('').serialize()
|
||||
|
||||
def registration(self, ctx, code, session_num):
|
||||
LOG.debug('Received registration from %s with session %s',
|
||||
code, session_num)
|
||||
try:
|
||||
node = objects.Node.get_by_code(ctx, code)
|
||||
board = objects.Board.get_by_code(ctx, code)
|
||||
|
||||
except Exception as exc:
|
||||
msg = exc.message % {'node': code}
|
||||
msg = exc.message % {'board': code}
|
||||
LOG.error(msg)
|
||||
wmessage = wm.WampError(msg).serialize()
|
||||
return wmessage
|
||||
return wm.WampError(msg).serialize()
|
||||
|
||||
try:
|
||||
old_ses = objects.SessionWP(ctx)
|
||||
old_ses = old_ses.get_session_by_node_uuid(ctx, node.uuid,
|
||||
valid=True)
|
||||
old_ses = old_ses.get_session_by_board_uuid(ctx, board.uuid,
|
||||
valid=True)
|
||||
old_ses.valid = False
|
||||
old_ses.save()
|
||||
|
||||
except Exception:
|
||||
LOG.debug('valid session for %s not found', node.uuid)
|
||||
LOG.debug('valid session for %s not found', board.uuid)
|
||||
|
||||
session = objects.SessionWP(ctx)
|
||||
session.node_id = node.id
|
||||
session.node_uuid = node.uuid
|
||||
session.session_id = session_num
|
||||
session_data = {'board_id': board.id,
|
||||
'board_uuid': board.uuid,
|
||||
'session_id': session_num}
|
||||
session = objects.SessionWP(ctx, **session_data)
|
||||
session.create()
|
||||
session.save()
|
||||
|
||||
node.agent = get_best_agent(ctx)
|
||||
agent = objects.WampAgent.get_by_hostname(ctx, node.agent)
|
||||
board.agent = get_best_agent(ctx)
|
||||
agent = objects.WampAgent.get_by_hostname(ctx, board.agent)
|
||||
|
||||
prov = Provisioner(node)
|
||||
prov = Provisioner(board)
|
||||
prov.conf_registration_agent(self.ragent.wsurl)
|
||||
|
||||
prov.conf_main_agent(agent.wsurl)
|
||||
node.config = prov.get_config()
|
||||
node.save()
|
||||
board.config = prov.get_config()
|
||||
board.status = states.OFFLINE
|
||||
board.save()
|
||||
|
||||
LOG.debug('sending this conf %s', node.config)
|
||||
LOG.debug('sending this conf %s', board.config)
|
||||
|
||||
wmessage = wm.WampSuccess(node.config)
|
||||
wmessage = wm.WampSuccess(board.config)
|
||||
return wmessage.serialize()
|
||||
|
||||
def destroy_node(self, ctx, node_id):
|
||||
LOG.info('Destroying node with id %s',
|
||||
node_id)
|
||||
node = objects.Node.get_by_uuid(ctx, node_id)
|
||||
def destroy_board(self, ctx, board_id):
|
||||
LOG.info('Destroying board with id %s',
|
||||
board_id)
|
||||
board = objects.Board.get_by_uuid(ctx, board_id)
|
||||
|
||||
prov = Provisioner()
|
||||
prov.conf_clean()
|
||||
p = prov.get_config()
|
||||
LOG.debug('sending this conf %s', p)
|
||||
try:
|
||||
self.execute_on_node(ctx, node_id, 'destroyNode', (p,))
|
||||
result = self.execute_on_board(ctx, board_id, 'destroyBoard', (p,))
|
||||
except Exception:
|
||||
LOG.error('cannot execute remote destroynode on %s. '
|
||||
'Maybe it is OFFLINE', node_id)
|
||||
|
||||
node.destroy()
|
||||
LOG.error('cannot execute remote destroyboard on %s. '
|
||||
'Maybe it is OFFLINE', board_id)
|
||||
|
||||
board.destroy()
|
||||
if result:
|
||||
LOG.debug(result)
|
||||
return result
|
||||
return
|
||||
|
||||
def update_node(self, ctx, node_obj):
|
||||
node = serializer.deserialize_entity(ctx, node_obj)
|
||||
LOG.debug('Updating node %s', node.name)
|
||||
node.save()
|
||||
return serializer.serialize_entity(ctx, node)
|
||||
def update_board(self, ctx, board_obj):
|
||||
board = serializer.deserialize_entity(ctx, board_obj)
|
||||
LOG.debug('Updating board %s', board.name)
|
||||
board.save()
|
||||
return serializer.serialize_entity(ctx, board)
|
||||
|
||||
def create_node(self, ctx, node_obj, location_obj):
|
||||
new_node = serializer.deserialize_entity(ctx, node_obj)
|
||||
LOG.debug('Creating node %s',
|
||||
new_node.name)
|
||||
def create_board(self, ctx, board_obj, location_obj):
|
||||
new_board = serializer.deserialize_entity(ctx, board_obj)
|
||||
LOG.debug('Creating board %s',
|
||||
new_board.name)
|
||||
new_location = serializer.deserialize_entity(ctx, location_obj)
|
||||
new_node.create()
|
||||
new_location.node_id = new_node.id
|
||||
new_board.create()
|
||||
new_location.board_id = new_board.id
|
||||
new_location.create()
|
||||
|
||||
return serializer.serialize_entity(ctx, new_node)
|
||||
return serializer.serialize_entity(ctx, new_board)
|
||||
|
||||
def execute_on_node(self, ctx, node_uuid, wamp_rpc_call, wamp_rpc_args):
|
||||
LOG.debug('Executing \"%s\" on the node: %s', wamp_rpc_call, node_uuid)
|
||||
def execute_on_board(self, ctx, board_uuid, wamp_rpc_call, wamp_rpc_args):
|
||||
LOG.debug('Executing \"%s\" on the board: %s',
|
||||
wamp_rpc_call, board_uuid)
|
||||
|
||||
node = objects.Node.get_by_uuid(ctx, node_uuid)
|
||||
board = objects.Board.get_by_uuid(ctx, board_uuid)
|
||||
|
||||
# check the session; it rise an excpetion if session miss
|
||||
# session = objects.SessionWP.get_session_by_node_uuid(node_uuid)
|
||||
objects.SessionWP.get_session_by_board_uuid(ctx, board_uuid)
|
||||
|
||||
s4t_topic = 's4t_invoke_wamp'
|
||||
full_topic = node.agent + '.' + s4t_topic
|
||||
full_topic = board.agent + '.' + s4t_topic
|
||||
|
||||
self.target.topic = full_topic
|
||||
|
||||
full_wamp_call = 'iotronic.' + node.uuid + "." + wamp_rpc_call
|
||||
full_wamp_call = 'iotronic.' + board.uuid + "." + wamp_rpc_call
|
||||
|
||||
return self.wamp_agent_client.call(ctx, full_topic,
|
||||
wamp_rpc_call=full_wamp_call,
|
||||
data=wamp_rpc_args)
|
||||
res = self.wamp_agent_client.call(ctx, full_topic,
|
||||
wamp_rpc_call=full_wamp_call,
|
||||
data=wamp_rpc_args)
|
||||
res = wm.deserialize(res)
|
||||
|
||||
if res.result == wm.SUCCESS:
|
||||
return res.message
|
||||
elif res.result == wm.ERROR:
|
||||
raise Exception
|
||||
|
||||
def destroy_plugin(self, ctx, plugin_id):
|
||||
LOG.info('Destroying plugin with id %s',
|
||||
|
@ -198,19 +204,80 @@ class ConductorEndpoint(object):
|
|||
new_plugin = serializer.deserialize_entity(ctx, plugin_obj)
|
||||
LOG.debug('Creating plugin %s',
|
||||
new_plugin.name)
|
||||
new_plugin.config = cpickle.dumps(new_plugin.config, 0)
|
||||
new_plugin.code = cpickle.dumps(new_plugin.code, 0)
|
||||
new_plugin.create()
|
||||
return serializer.serialize_entity(ctx, new_plugin)
|
||||
|
||||
def inject_plugin(self, ctx, plugin_uuid, node_uuid):
|
||||
LOG.info('Injecting plugin with id %s into the node %s',
|
||||
plugin_uuid, node_uuid)
|
||||
def inject_plugin(self, ctx, plugin_uuid, board_uuid, onboot):
|
||||
LOG.info('Injecting plugin with id %s into the board %s',
|
||||
plugin_uuid, board_uuid)
|
||||
|
||||
plugin = objects.Plugin.get(ctx, plugin_uuid)
|
||||
|
||||
result = self.execute_on_board(ctx,
|
||||
board_uuid,
|
||||
'PluginInject',
|
||||
(plugin, onboot))
|
||||
|
||||
injection = None
|
||||
try:
|
||||
injection = objects.InjectionPlugin.get(ctx,
|
||||
board_uuid,
|
||||
plugin_uuid)
|
||||
except Exception:
|
||||
pass
|
||||
if injection:
|
||||
injection.status = 'updated'
|
||||
injection.save()
|
||||
else:
|
||||
inj_data = {
|
||||
'board_uuid': board_uuid,
|
||||
'plugin_uuid': plugin_uuid,
|
||||
'onboot': onboot,
|
||||
'status': 'injected'
|
||||
}
|
||||
injection = objects.InjectionPlugin(ctx, **inj_data)
|
||||
injection.create()
|
||||
|
||||
LOG.debug(result)
|
||||
return result
|
||||
|
||||
def remove_plugin(self, ctx, plugin_uuid, board_uuid):
|
||||
LOG.info('Removing plugin with id %s into the board %s',
|
||||
plugin_uuid, board_uuid)
|
||||
|
||||
plugin = objects.Plugin.get_by_uuid(ctx, plugin_uuid)
|
||||
|
||||
injection = objects.InjectionPlugin.get(ctx, board_uuid, plugin_uuid)
|
||||
|
||||
try:
|
||||
self.execute_on_node(ctx, node_uuid, 'PluginInject',
|
||||
(plugin.name, plugin.config))
|
||||
result = self.execute_on_board(ctx, board_uuid, 'PluginRemove',
|
||||
(plugin.uuid,))
|
||||
except Exception:
|
||||
LOG.error('cannot execute remote injection on %s. '
|
||||
'Maybe it is OFFLINE', node_uuid)
|
||||
return
|
||||
LOG.error('cannot execute a plugin remove on %s. ', Exception)
|
||||
return Exception
|
||||
|
||||
LOG.debug(result)
|
||||
injection.destroy()
|
||||
return result
|
||||
|
||||
def action_plugin(self, ctx, plugin_uuid, board_uuid, action, params):
|
||||
LOG.info('Calling plugin with id %s into the board %s',
|
||||
plugin_uuid, board_uuid)
|
||||
plugin = objects.Plugin.get(ctx, plugin_uuid)
|
||||
|
||||
objects.plugin.is_valid_action(action)
|
||||
|
||||
try:
|
||||
if objects.plugin.want_params(action):
|
||||
result = self.execute_on_board(ctx, board_uuid, action,
|
||||
(plugin.uuid, params))
|
||||
else:
|
||||
result = self.execute_on_board(ctx, board_uuid, action,
|
||||
(plugin.uuid,))
|
||||
except Exception:
|
||||
LOG.error('cannot execute a plugin remove on %s. ', Exception)
|
||||
return Exception
|
||||
|
||||
LOG.debug(result)
|
||||
return result
|
||||
|
|
|
@ -18,23 +18,23 @@ serializer = objects_base.IotronicObjectSerializer()
|
|||
|
||||
|
||||
class Provisioner(object):
|
||||
def __init__(self, node=None):
|
||||
if not node:
|
||||
def __init__(self, board=None):
|
||||
if not board:
|
||||
self.config = {"iotronic": {"extra": {}}}
|
||||
else:
|
||||
self.config = node.config
|
||||
self.config = board.config
|
||||
if 'iotronic' not in self.config:
|
||||
self.config = {"iotronic": {"extra": {}}}
|
||||
if 'node' not in self.config['iotronic']:
|
||||
self.config['iotronic']['node'] = {}
|
||||
self.config['iotronic']['node'] = node.as_dict()
|
||||
self.config['iotronic']['node']['created_at'] = \
|
||||
node._attr_to_primitive('created_at')
|
||||
self.config['iotronic']['node']['updated_at'] = \
|
||||
node._attr_to_primitive('updated_at')
|
||||
if 'board' not in self.config['iotronic']:
|
||||
self.config['iotronic']['board'] = {}
|
||||
self.config['iotronic']['board'] = board.as_dict()
|
||||
self.config['iotronic']['board']['created_at'] = \
|
||||
board._attr_to_primitive('created_at')
|
||||
self.config['iotronic']['board']['updated_at'] = \
|
||||
board._attr_to_primitive('updated_at')
|
||||
|
||||
try:
|
||||
del self.config['iotronic']['node']['config']
|
||||
del self.config['iotronic']['board']['config']
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
@ -72,6 +72,6 @@ class Provisioner(object):
|
|||
|
||||
def conf_clean(self):
|
||||
self.conf_registration_agent()
|
||||
if 'node' not in self.config['iotronic']:
|
||||
self.config['iotronic']['node'] = {}
|
||||
self.config['iotronic']['node']['token'] = "<REGISTRATION-TOKEN>"
|
||||
if 'board' not in self.config['iotronic']:
|
||||
self.config['iotronic']['board'] = {}
|
||||
self.config['iotronic']['board']['token'] = "<REGISTRATION-TOKEN>"
|
||||
|
|
|
@ -46,14 +46,14 @@ class ConductorAPI(object):
|
|||
"""Test
|
||||
|
||||
:param context: request context.
|
||||
:param data: node id or uuid.
|
||||
:param data: board id or uuid.
|
||||
:param topic: RPC topic. Defaults to self.topic.
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||
return cctxt.call(context, 'echo', data=data)
|
||||
|
||||
def registration(self, context, code, session_num, topic=None):
|
||||
"""Registration of a node.
|
||||
"""Registration of a board.
|
||||
|
||||
:param context: request context.
|
||||
:param code: token used for the first registration
|
||||
|
@ -65,10 +65,10 @@ class ConductorAPI(object):
|
|||
code=code, session_num=session_num)
|
||||
|
||||
def connection(self, context, uuid, session_num, topic=None):
|
||||
"""Connection of a node.
|
||||
"""Connection of a board.
|
||||
|
||||
:param context: request context.
|
||||
:param uuid: uuid node
|
||||
:param uuid: uuid board
|
||||
:param session_num: wamp session number
|
||||
:param topic: RPC topic. Defaults to self.topic.
|
||||
"""
|
||||
|
@ -76,54 +76,55 @@ class ConductorAPI(object):
|
|||
return cctxt.call(context, 'connection',
|
||||
uuid=uuid, session_num=session_num)
|
||||
|
||||
def create_node(self, context, node_obj, location_obj, topic=None):
|
||||
"""Add a node on the cloud
|
||||
def create_board(self, context, board_obj, location_obj, topic=None):
|
||||
"""Add a board on the cloud
|
||||
|
||||
:param context: request context.
|
||||
:param node_obj: a changed (but not saved) node object.
|
||||
:param board_obj: a changed (but not saved) board object.
|
||||
:param topic: RPC topic. Defaults to self.topic.
|
||||
:returns: created node object
|
||||
:returns: created board object
|
||||
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||
return cctxt.call(context, 'create_node',
|
||||
node_obj=node_obj, location_obj=location_obj)
|
||||
return cctxt.call(context, 'create_board',
|
||||
board_obj=board_obj, location_obj=location_obj)
|
||||
|
||||
def update_node(self, context, node_obj, topic=None):
|
||||
"""Synchronously, have a conductor update the node's information.
|
||||
def update_board(self, context, board_obj, topic=None):
|
||||
"""Synchronously, have a conductor update the board's information.
|
||||
|
||||
Update the node's information in the database and return a node object.
|
||||
Update the board's information in the database and return
|
||||
a board object.
|
||||
|
||||
Note that power_state should not be passed via this method.
|
||||
Use change_node_power_state for initiating driver actions.
|
||||
Use change_board_power_state for initiating driver actions.
|
||||
|
||||
:param context: request context.
|
||||
:param node_obj: a changed (but not saved) node object.
|
||||
:param board_obj: a changed (but not saved) board object.
|
||||
:param topic: RPC topic. Defaults to self.topic.
|
||||
:returns: updated node object, including all fields.
|
||||
:returns: updated board object, including all fields.
|
||||
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||
return cctxt.call(context, 'update_node', node_obj=node_obj)
|
||||
return cctxt.call(context, 'update_board', board_obj=board_obj)
|
||||
|
||||
def destroy_node(self, context, node_id, topic=None):
|
||||
"""Delete a node.
|
||||
def destroy_board(self, context, board_id, topic=None):
|
||||
"""Delete a board.
|
||||
|
||||
:param context: request context.
|
||||
:param node_id: node id or uuid.
|
||||
:raises: NodeLocked if node is locked by another conductor.
|
||||
:raises: NodeAssociated if the node contains an instance
|
||||
:param board_id: board id or uuid.
|
||||
:raises: BoardLocked if board is locked by another conductor.
|
||||
:raises: BoardAssociated if the board contains an instance
|
||||
associated with it.
|
||||
:raises: InvalidState if the node is in the wrong provision
|
||||
:raises: InvalidState if the board is in the wrong provision
|
||||
state to perform deletion.
|
||||
"""
|
||||
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||
return cctxt.call(context, 'destroy_node', node_id=node_id)
|
||||
return cctxt.call(context, 'destroy_board', board_id=board_id)
|
||||
|
||||
def execute_on_node(self, context, node_uuid, wamp_rpc_call,
|
||||
wamp_rpc_args=None, topic=None):
|
||||
def execute_on_board(self, context, board_uuid, wamp_rpc_call,
|
||||
wamp_rpc_args=None, topic=None):
|
||||
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||
return cctxt.call(context, 'execute_on_node', node_uuid=node_uuid,
|
||||
return cctxt.call(context, 'execute_on_board', board_uuid=board_uuid,
|
||||
wamp_rpc_call=wamp_rpc_call,
|
||||
wamp_rpc_args=wamp_rpc_args)
|
||||
|
||||
|
@ -169,14 +170,40 @@ class ConductorAPI(object):
|
|||
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||
return cctxt.call(context, 'destroy_plugin', plugin_id=plugin_id)
|
||||
|
||||
def inject_plugin(self, context, plugin_uuid, node_uuid, topic=None):
|
||||
"""inject a plugin into a node.
|
||||
def inject_plugin(self, context, plugin_uuid,
|
||||
board_uuid, onboot=False, topic=None):
|
||||
"""inject a plugin into a board.
|
||||
|
||||
:param context: request context.
|
||||
:param plugin_uuid: plugin id or uuid.
|
||||
:param ndoe_uuid: node 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, 'inject_plugin', plugin_uuid=plugin_uuid,
|
||||
node_uuid=node_uuid)
|
||||
board_uuid=board_uuid, onboot=onboot)
|
||||
|
||||
def remove_plugin(self, context, plugin_uuid, board_uuid, topic=None):
|
||||
"""inject a plugin into a board.
|
||||
|
||||
:param context: request context.
|
||||
:param plugin_uuid: plugin 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, 'remove_plugin', plugin_uuid=plugin_uuid,
|
||||
board_uuid=board_uuid)
|
||||
|
||||
def action_plugin(self, context, plugin_uuid,
|
||||
board_uuid, action, params=None, topic=None):
|
||||
"""Action on a plugin into a board.
|
||||
|
||||
:param context: request context.
|
||||
:param plugin_uuid: plugin 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_plugin', plugin_uuid=plugin_uuid,
|
||||
board_uuid=board_uuid, action=action, params=params)
|
||||
|
|
|
@ -43,11 +43,11 @@ class Connection(object):
|
|||
"""Constructor."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_nodeinfo_list(self, columns=None, filters=None, limit=None,
|
||||
marker=None, sort_key=None, sort_dir=None):
|
||||
"""Get specific columns for matching nodes.
|
||||
def get_boardinfo_list(self, columns=None, filters=None, limit=None,
|
||||
marker=None, sort_key=None, sort_dir=None):
|
||||
"""Get specific columns for matching boards.
|
||||
|
||||
Return a list of the specified columns for all nodes that match the
|
||||
Return a list of the specified columns for all boards that match the
|
||||
specified filters.
|
||||
|
||||
:param columns: List of column names to return.
|
||||
|
@ -57,11 +57,11 @@ class Connection(object):
|
|||
:associated: True | False
|
||||
:reserved: True | False
|
||||
:maintenance: True | False
|
||||
:provision_state: provision state of node
|
||||
:provision_state: provision state of board
|
||||
:provisioned_before:
|
||||
nodes with provision_updated_at field before this
|
||||
boards with provision_updated_at field before this
|
||||
interval in seconds
|
||||
:param limit: Maximum number of nodes to return.
|
||||
:param limit: Maximum number of boards to return.
|
||||
:param marker: the last item of the previous page; we return the next
|
||||
result set.
|
||||
:param sort_key: Attribute by which results should be sorted.
|
||||
|
@ -71,20 +71,20 @@ class Connection(object):
|
|||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_node_list(self, filters=None, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of nodes.
|
||||
def get_board_list(self, filters=None, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of boards.
|
||||
|
||||
:param filters: Filters to apply. Defaults to None.
|
||||
|
||||
:associated: True | False
|
||||
:reserved: True | False
|
||||
:maintenance: True | False
|
||||
:provision_state: provision state of node
|
||||
:provision_state: provision state of board
|
||||
:provisioned_before:
|
||||
nodes with provision_updated_at field before this
|
||||
boards with provision_updated_at field before this
|
||||
interval in seconds
|
||||
:param limit: Maximum number of nodes to return.
|
||||
:param limit: Maximum number of boards to return.
|
||||
:param marker: the last item of the previous page; we return the next
|
||||
result set.
|
||||
:param sort_key: Attribute by which results should be sorted.
|
||||
|
@ -93,12 +93,12 @@ class Connection(object):
|
|||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_node(self, values):
|
||||
"""Create a new node.
|
||||
def create_board(self, values):
|
||||
"""Create a new board.
|
||||
|
||||
:param values: A dict containing several items used to identify
|
||||
and track the node, and several dicts which are passed
|
||||
into the Drivers when managing this node. For example:
|
||||
and track the board, and several dicts which are passed
|
||||
into the Drivers when managing this board. For example:
|
||||
|
||||
::
|
||||
|
||||
|
@ -110,65 +110,65 @@ class Connection(object):
|
|||
'properties': { ... },
|
||||
'extra': { ... },
|
||||
}
|
||||
:returns: A node.
|
||||
:returns: A board.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_node_by_id(self, node_id):
|
||||
"""Return a node.
|
||||
def get_board_by_id(self, board_id):
|
||||
"""Return a board.
|
||||
|
||||
:param node_id: The id of a node.
|
||||
:returns: A node.
|
||||
:param board_id: The id of a board.
|
||||
:returns: A board.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_node_by_uuid(self, node_uuid):
|
||||
"""Return a node.
|
||||
def get_board_by_uuid(self, board_uuid):
|
||||
"""Return a board.
|
||||
|
||||
:param node_uuid: The uuid of a node.
|
||||
:returns: A node.
|
||||
:param board_uuid: The uuid of a board.
|
||||
:returns: A board.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_node_id_by_uuid(self, node_uuid):
|
||||
"""Return a node id.
|
||||
def get_board_id_by_uuid(self, board_uuid):
|
||||
"""Return a board id.
|
||||
|
||||
:param node_uuid: The uuid of a node.
|
||||
# :returns: A node.id.
|
||||
:param board_uuid: The uuid of a board.
|
||||
# :returns: A board.id.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_node_by_name(self, node_name):
|
||||
"""Return a node.
|
||||
def get_board_by_name(self, board_name):
|
||||
"""Return a board.
|
||||
|
||||
:param node_name: The logical name of a node.
|
||||
:returns: A node.
|
||||
:param board_name: The logical name of a board.
|
||||
:returns: A board.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_node_by_code(self, instance):
|
||||
"""Return a node.
|
||||
def get_board_by_code(self, instance):
|
||||
"""Return a board.
|
||||
|
||||
:param instance: The instance code or uuid to search for.
|
||||
:returns: A node.
|
||||
:returns: A board.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def destroy_node(self, node_id):
|
||||
"""Destroy a node and all associated interfaces.
|
||||
def destroy_board(self, board_id):
|
||||
"""Destroy a board and all associated interfaces.
|
||||
|
||||
:param node_id: The id or uuid of a node.
|
||||
:param board_id: The id or uuid of a board.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_node(self, node_id, values):
|
||||
"""Update properties of a node.
|
||||
def update_board(self, board_id, values):
|
||||
"""Update properties of a board.
|
||||
|
||||
:param node_id: The id or uuid of a node.
|
||||
:param board_id: The id or uuid of a board.
|
||||
:param values: Dict of values to update.
|
||||
:returns: A node.
|
||||
:raises: NodeAssociated
|
||||
:raises: NodeNotFound
|
||||
:returns: A board.
|
||||
:raises: BoardAssociated
|
||||
:raises: BoardNotFound
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -213,10 +213,10 @@ class Connection(object):
|
|||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_session_by_node_uuid(self, node_uuid, valid):
|
||||
"""Return a Wamp session of a Node
|
||||
def get_session_by_board_uuid(self, board_uuid, valid):
|
||||
"""Return a Wamp session of a Board
|
||||
|
||||
:param node_uuid: Filters to apply. Defaults to None.
|
||||
:param board_uuid: Filters to apply. Defaults to None.
|
||||
:param valid: is valid
|
||||
:returns: A session.
|
||||
"""
|
||||
|
@ -253,11 +253,11 @@ class Connection(object):
|
|||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_locations_by_node_id(self, node_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""List all the locations for a given node.
|
||||
def get_locations_by_board_id(self, board_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""List all the locations for a given board.
|
||||
|
||||
:param node_id: The integer node ID.
|
||||
:param board_id: The integer board ID.
|
||||
:param limit: Maximum number of locations to return.
|
||||
:param marker: the last item of the previous page; we return the next
|
||||
result set.
|
||||
|
@ -364,3 +364,58 @@ class Connection(object):
|
|||
:raises: PluginAssociated
|
||||
:raises: PluginNotFound
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_injection_plugin_by_board_uuid(self, board_uuid):
|
||||
"""get an injection of a plugin using a board_uuid
|
||||
|
||||
:param board_uuid: The id or uuid of a board.
|
||||
:returns: An injection_plugin.
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_injection_plugin_by_uuids(self, board_uuid, plugin_uuid):
|
||||
"""get an injection of a plugin using a board_uuid and plugin_uuid
|
||||
|
||||
:param board_uuid: The id or uuid of a board.
|
||||
:param plugin_uuid: The id or uuid of a plugin.
|
||||
:returns: An injection_plugin.
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_injection_plugin(self, values):
|
||||
"""Create a new injection_plugin.
|
||||
|
||||
:param values: A dict containing several items used to identify
|
||||
and track the plugin
|
||||
:returns: An injection plugin.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def destroy_injection_plugin(self, injection_plugin_id):
|
||||
"""Destroy an injection plugin and all associated interfaces.
|
||||
|
||||
:param injection_plugin_id: The id or uuid of a plugin.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_injection_plugin(self, plugin_injection_id, values):
|
||||
"""Update properties of a plugin.
|
||||
|
||||
:param plugin_id: The id or uuid of a plugin.
|
||||
:param values: Dict of values to update.
|
||||
:returns: A plugin.
|
||||
:raises: PluginAssociated
|
||||
:raises: PluginNotFound
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_injection_plugin_list(self, board_uuid):
|
||||
"""Return a list of injection_plugins.
|
||||
|
||||
:param board_uuid: The id or uuid of a plugin.
|
||||
:returns: A list of InjectionPlugins on the board.
|
||||
|
||||
"""
|
||||
|
|
|
@ -26,14 +26,12 @@ from oslo_utils import uuidutils
|
|||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
|
||||
|
||||
from iotronic.common import exception
|
||||
from iotronic.common.i18n import _
|
||||
from iotronic.common import states
|
||||
from iotronic.db import api
|
||||
from iotronic.db.sqlalchemy import models
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('heartbeat_timeout',
|
||||
'iotronic.conductor.manager',
|
||||
|
@ -117,20 +115,20 @@ class Connection(api.Connection):
|
|||
def __init__(self):
|
||||
pass
|
||||
|
||||
def _add_location_filter_by_node(self, query, value):
|
||||
def _add_location_filter_by_board(self, query, value):
|
||||
if strutils.is_int_like(value):
|
||||
return query.filter_by(node_id=value)
|
||||
return query.filter_by(board_id=value)
|
||||
else:
|
||||
query = query.join(models.Node,
|
||||
models.Location.node_id == models.Node.id)
|
||||
return query.filter(models.Node.uuid == value)
|
||||
query = query.join(models.Board,
|
||||
models.Location.board_id == models.Board.id)
|
||||
return query.filter(models.Board.uuid == value)
|
||||
|
||||
def _add_nodes_filters(self, query, filters):
|
||||
def _add_boards_filters(self, query, filters):
|
||||
if filters is None:
|
||||
filters = []
|
||||
|
||||
if 'project_id' in filters:
|
||||
query = query.filter(models.Node.project == filters['project_id'])
|
||||
query = query.filter(models.Board.project == filters['project_id'])
|
||||
|
||||
return query
|
||||
|
||||
|
@ -168,139 +166,157 @@ class Connection(api.Connection):
|
|||
|
||||
return query
|
||||
|
||||
def _do_update_node(self, node_id, values):
|
||||
def _do_update_board(self, board_id, values):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(models.Node, session=session)
|
||||
query = add_identity_filter(query, node_id)
|
||||
query = model_query(models.Board, session=session)
|
||||
query = add_identity_filter(query, board_id)
|
||||
try:
|
||||
ref = query.with_lockmode('update').one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotFound(node=node_id)
|
||||
|
||||
# Prevent instance_uuid overwriting
|
||||
if values.get("instance_uuid") and ref.instance_uuid:
|
||||
raise exception.NodeAssociated(
|
||||
node=node_id, instance=ref.instance_uuid)
|
||||
raise exception.BoardNotFound(board=board_id)
|
||||
|
||||
ref.update(values)
|
||||
return ref
|
||||
|
||||
# NODE api
|
||||
def _do_update_plugin(self, plugin_id, values):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(models.Plugin, session=session)
|
||||
query = add_identity_filter(query, plugin_id)
|
||||
try:
|
||||
ref = query.with_lockmode('update').one()
|
||||
except NoResultFound:
|
||||
raise exception.PluginNotFound(plugin=plugin_id)
|
||||
|
||||
def get_nodeinfo_list(self, columns=None, filters=None, limit=None,
|
||||
marker=None, sort_key=None, sort_dir=None):
|
||||
ref.update(values)
|
||||
return ref
|
||||
|
||||
def _do_update_injection_plugin(self, injection_plugin_id, values):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(models.InjectionPlugin, session=session)
|
||||
query = add_identity_filter(query, injection_plugin_id)
|
||||
try:
|
||||
ref = query.with_lockmode('update').one()
|
||||
except NoResultFound:
|
||||
raise exception.InjectionPluginNotFound(
|
||||
injection_plugin=injection_plugin_id)
|
||||
|
||||
ref.update(values)
|
||||
return ref
|
||||
|
||||
# BOARD api
|
||||
|
||||
def get_boardinfo_list(self, columns=None, filters=None, limit=None,
|
||||
marker=None, sort_key=None, sort_dir=None):
|
||||
# list-ify columns default values because it is bad form
|
||||
# to include a mutable list in function definitions.
|
||||
if columns is None:
|
||||
columns = [models.Node.id]
|
||||
columns = [models.Board.id]
|
||||
else:
|
||||
columns = [getattr(models.Node, c) for c in columns]
|
||||
columns = [getattr(models.Board, c) for c in columns]
|
||||
|
||||
query = model_query(*columns, base_model=models.Node)
|
||||
query = self._add_nodes_filters(query, filters)
|
||||
return _paginate_query(models.Node, limit, marker,
|
||||
query = model_query(*columns, base_model=models.Board)
|
||||
query = self._add_boards_filters(query, filters)
|
||||
return _paginate_query(models.Board, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
def get_node_list(self, filters=None, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
query = model_query(models.Node)
|
||||
query = self._add_nodes_filters(query, filters)
|
||||
return _paginate_query(models.Node, limit, marker,
|
||||
def get_board_list(self, filters=None, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
query = model_query(models.Board)
|
||||
query = self._add_boards_filters(query, filters)
|
||||
return _paginate_query(models.Board, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
def create_node(self, values):
|
||||
# ensure defaults are present for new nodes
|
||||
def create_board(self, values):
|
||||
# ensure defaults are present for new boards
|
||||
if 'uuid' not in values:
|
||||
values['uuid'] = uuidutils.generate_uuid()
|
||||
if 'status' not in values:
|
||||
values['status'] = states.REGISTERED
|
||||
|
||||
node = models.Node()
|
||||
node.update(values)
|
||||
board = models.Board()
|
||||
board.update(values)
|
||||
try:
|
||||
node.save()
|
||||
board.save()
|
||||
except db_exc.DBDuplicateEntry as exc:
|
||||
if 'code' in exc.columns:
|
||||
raise exception.DuplicateCode(code=values['code'])
|
||||
raise exception.NodeAlreadyExists(uuid=values['uuid'])
|
||||
return node
|
||||
raise exception.BoardAlreadyExists(uuid=values['uuid'])
|
||||
return board
|
||||
|
||||
def get_node_by_id(self, node_id):
|
||||
query = model_query(models.Node).filter_by(id=node_id)
|
||||
def get_board_by_id(self, board_id):
|
||||
query = model_query(models.Board).filter_by(id=board_id)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotFound(node=node_id)
|
||||
raise exception.BoardNotFound(board=board_id)
|
||||
|
||||
def get_node_id_by_uuid(self, node_uuid):
|
||||
query = model_query(models.Node.id).filter_by(uuid=node_uuid)
|
||||
def get_board_id_by_uuid(self, board_uuid):
|
||||
query = model_query(models.Board.id).filter_by(uuid=board_uuid)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotFound(node=node_uuid)
|
||||
raise exception.BoardNotFound(board=board_uuid)
|
||||
|
||||
def get_node_by_uuid(self, node_uuid):
|
||||
query = model_query(models.Node).filter_by(uuid=node_uuid)
|
||||
def get_board_by_uuid(self, board_uuid):
|
||||
query = model_query(models.Board).filter_by(uuid=board_uuid)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotFound(node=node_uuid)
|
||||
raise exception.BoardNotFound(board=board_uuid)
|
||||
|
||||
def get_node_by_name(self, node_name):
|
||||
query = model_query(models.Node).filter_by(name=node_name)
|
||||
def get_board_by_name(self, board_name):
|
||||
query = model_query(models.Board).filter_by(name=board_name)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotFound(node=node_name)
|
||||
raise exception.BoardNotFound(board=board_name)
|
||||
|
||||
def get_node_by_code(self, node_code):
|
||||
query = model_query(models.Node).filter_by(code=node_code)
|
||||
def get_board_by_code(self, board_code):
|
||||
query = model_query(models.Board).filter_by(code=board_code)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotFound(node=node_code)
|
||||
raise exception.BoardNotFound(board=board_code)
|
||||
|
||||
def destroy_node(self, node_id):
|
||||
def destroy_board(self, board_id):
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(models.Node, session=session)
|
||||
query = add_identity_filter(query, node_id)
|
||||
query = model_query(models.Board, session=session)
|
||||
query = add_identity_filter(query, board_id)
|
||||
try:
|
||||
node_ref = query.one()
|
||||
board_ref = query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotFound(node=node_id)
|
||||
raise exception.BoardNotFound(board=board_id)
|
||||
|
||||
# Get node ID, if an UUID was supplied. The ID is
|
||||
# required for deleting all ports, attached to the node.
|
||||
if uuidutils.is_uuid_like(node_id):
|
||||
node_id = node_ref['id']
|
||||
# Get board ID, if an UUID was supplied. The ID is
|
||||
# required for deleting all ports, attached to the board.
|
||||
if uuidutils.is_uuid_like(board_id):
|
||||
board_id = board_ref['id']
|
||||
|
||||
location_query = model_query(models.Location, session=session)
|
||||
location_query = self._add_location_filter_by_node(
|
||||
location_query, node_id)
|
||||
location_query = self._add_location_filter_by_board(
|
||||
location_query, board_id)
|
||||
location_query.delete()
|
||||
|
||||
query.delete()
|
||||
|
||||
def update_node(self, node_id, values):
|
||||
def update_board(self, board_id, values):
|
||||
# NOTE(dtantsur): this can lead to very strange errors
|
||||
if 'uuid' in values:
|
||||
msg = _("Cannot overwrite UUID for an existing Node.")
|
||||
msg = _("Cannot overwrite UUID for an existing Board.")
|
||||
raise exception.InvalidParameterValue(err=msg)
|
||||
|
||||
try:
|
||||
return self._do_update_node(node_id, values)
|
||||
return self._do_update_board(board_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.NodeAlreadyExists(uuid=values['uuid'])
|
||||
elif 'instance_uuid' in e.columns:
|
||||
raise exception.InstanceAssociated(
|
||||
instance_uuid=values['instance_uuid'],
|
||||
node=node_id)
|
||||
raise exception.BoardAlreadyExists(uuid=values['uuid'])
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
@ -385,10 +401,10 @@ class Connection(api.Connection):
|
|||
if count == 0:
|
||||
raise exception.LocationNotFound(location=location_id)
|
||||
|
||||
def get_locations_by_node_id(self, node_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
def get_locations_by_board_id(self, board_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
query = model_query(models.Location)
|
||||
query = query.filter_by(node_id=node_id)
|
||||
query = query.filter_by(board_id=board_id)
|
||||
return _paginate_query(models.Location, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
|
@ -413,15 +429,15 @@ class Connection(api.Connection):
|
|||
raise exception.SessionWPNotFound(ses=ses_id)
|
||||
return ref
|
||||
|
||||
def get_session_by_node_uuid(self, node_uuid, valid):
|
||||
def get_session_by_board_uuid(self, board_uuid, valid):
|
||||
query = model_query(
|
||||
models.SessionWP).filter_by(
|
||||
node_uuid=node_uuid).filter_by(
|
||||
board_uuid=board_uuid).filter_by(
|
||||
valid=valid)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotConnected(node=node_uuid)
|
||||
raise exception.BoardNotConnected(board=board_uuid)
|
||||
|
||||
def get_session_by_id(self, session_id):
|
||||
query = model_query(models.SessionWP).filter_by(session_id=session_id)
|
||||
|
@ -550,10 +566,6 @@ class Connection(api.Connection):
|
|||
raise exception.DuplicateName(name=values['name'])
|
||||
elif 'uuid' in e.columns:
|
||||
raise exception.PluginAlreadyExists(uuid=values['uuid'])
|
||||
elif 'instance_uuid' in e.columns:
|
||||
raise exception.InstanceAssociated(
|
||||
instance_uuid=values['instance_uuid'],
|
||||
plugin=plugin_id)
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
@ -575,3 +587,71 @@ class Connection(api.Connection):
|
|||
query = self._add_plugins_filters(query, filters)
|
||||
return _paginate_query(models.Plugin, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
# INJECTION PLUGIN api
|
||||
|
||||
def get_injection_plugin_by_board_uuid(self, board_uuid):
|
||||
query = model_query(
|
||||
models.InjectionPlugin).filter_by(
|
||||
board_uuid=board_uuid)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.InjectionPluginNotFound()
|
||||
|
||||
def create_injection_plugin(self, values):
|
||||
# ensure defaults are present for new plugins
|
||||
if 'uuid' not in values:
|
||||
values['uuid'] = uuidutils.generate_uuid()
|
||||
inj_plug = models.InjectionPlugin()
|
||||
inj_plug.update(values)
|
||||
try:
|
||||
inj_plug.save()
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exception.PluginAlreadyExists(uuid=values['uuid'])
|
||||
return inj_plug
|
||||
|
||||
def update_injection_plugin(self, plugin_injection_id, values):
|
||||
|
||||
if 'uuid' in values:
|
||||
msg = _("Cannot overwrite UUID for an existing Plugin.")
|
||||
raise exception.InvalidParameterValue(err=msg)
|
||||
try:
|
||||
return self._do_update_injection_plugin(
|
||||
plugin_injection_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.PluginAlreadyExists(uuid=values['uuid'])
|
||||
else:
|
||||
raise e
|
||||
|
||||
def get_injection_plugin_by_uuids(self, board_uuid, plugin_uuid):
|
||||
query = model_query(
|
||||
models.InjectionPlugin).filter_by(
|
||||
board_uuid=board_uuid).filter_by(
|
||||
plugin_uuid=plugin_uuid)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.InjectionPluginNotFound()
|
||||
|
||||
def destroy_injection_plugin(self, injection_plugin_id):
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(models.InjectionPlugin, session=session)
|
||||
query = add_identity_filter(query, injection_plugin_id)
|
||||
try:
|
||||
query.delete()
|
||||
|
||||
except NoResultFound:
|
||||
raise exception.InjectionPluginNotFound()
|
||||
|
||||
def get_injection_plugin_list(self, board_uuid):
|
||||
query = model_query(
|
||||
models.InjectionPlugin).filter_by(
|
||||
board_uuid=board_uuid)
|
||||
return query.all()
|
||||
|
|
|
@ -138,14 +138,14 @@ class WampAgent(Base):
|
|||
ragent = Column(Boolean, default=False)
|
||||
|
||||
|
||||
class Node(Base):
|
||||
"""Represents a Node."""
|
||||
class Board(Base):
|
||||
"""Represents a Board."""
|
||||
|
||||
__tablename__ = 'nodes'
|
||||
__tablename__ = 'boards'
|
||||
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint('uuid', name='uniq_nodes0uuid'),
|
||||
schema.UniqueConstraint('code', name='uniq_nodes0code'),
|
||||
schema.UniqueConstraint('uuid', name='uniq_boards0uuid'),
|
||||
schema.UniqueConstraint('code', name='uniq_boards0code'),
|
||||
table_args())
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36))
|
||||
|
@ -162,7 +162,7 @@ class Node(Base):
|
|||
|
||||
|
||||
class Location(Base):
|
||||
"""Represents a location of a node."""
|
||||
"""Represents a location of a board."""
|
||||
|
||||
__tablename__ = 'locations'
|
||||
__table_args__ = (
|
||||
|
@ -171,11 +171,11 @@ class Location(Base):
|
|||
longitude = Column(String(18), nullable=True)
|
||||
latitude = Column(String(18), nullable=True)
|
||||
altitude = Column(String(18), nullable=True)
|
||||
node_id = Column(Integer, ForeignKey('nodes.id'))
|
||||
board_id = Column(Integer, ForeignKey('boards.id'))
|
||||
|
||||
|
||||
class SessionWP(Base):
|
||||
"""Represents a session of a node."""
|
||||
"""Represents a session of a board."""
|
||||
|
||||
__tablename__ = 'sessions'
|
||||
__table_args__ = (
|
||||
|
@ -183,14 +183,14 @@ class SessionWP(Base):
|
|||
'session_id',
|
||||
name='uniq_session_id0session_id'),
|
||||
schema.UniqueConstraint(
|
||||
'node_uuid',
|
||||
name='uniq_node_uuid0node_uuid'),
|
||||
'board_uuid',
|
||||
name='uniq_board_uuid0board_uuid'),
|
||||
table_args())
|
||||
id = Column(Integer, primary_key=True)
|
||||
valid = Column(Boolean, default=True)
|
||||
session_id = Column(String(15))
|
||||
node_uuid = Column(String(36))
|
||||
node_id = Column(Integer, ForeignKey('nodes.id'))
|
||||
board_uuid = Column(String(36))
|
||||
board_id = Column(Integer, ForeignKey('boards.id'))
|
||||
|
||||
|
||||
class Plugin(Base):
|
||||
|
@ -205,19 +205,19 @@ class Plugin(Base):
|
|||
name = Column(String(36))
|
||||
owner = Column(String(36))
|
||||
public = Column(Boolean, default=False)
|
||||
config = Column(TEXT)
|
||||
code = Column(TEXT)
|
||||
callable = Column(Boolean)
|
||||
extra = Column(JSONEncodedDict)
|
||||
|
||||
|
||||
class Injected_Plugin(Base):
|
||||
class InjectionPlugin(Base):
|
||||
"""Represents an plugin injection on board."""
|
||||
|
||||
__tablename__ = 'injected_plugins'
|
||||
__tablename__ = 'injection_plugins'
|
||||
__table_args__ = (
|
||||
table_args())
|
||||
id = Column(Integer, primary_key=True)
|
||||
node_uuid = Column(String(36))
|
||||
node_id = Column(Integer, ForeignKey('nodes.id'))
|
||||
plugin_uuid = Column(String(36))
|
||||
plugin_id = Column(Integer, ForeignKey('plugins.id'))
|
||||
board_uuid = Column(String(36), ForeignKey('boards.uuid'))
|
||||
plugin_uuid = Column(String(36), ForeignKey('plugins.uuid'))
|
||||
onboot = Column(Boolean, default=False)
|
||||
status = Column(String(15))
|
||||
|
|
|
@ -12,25 +12,28 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from iotronic.objects import board
|
||||
from iotronic.objects import conductor
|
||||
from iotronic.objects import injectionplugin
|
||||
from iotronic.objects import location
|
||||
from iotronic.objects import node
|
||||
from iotronic.objects import plugin
|
||||
from iotronic.objects import sessionwp
|
||||
from iotronic.objects import wampagent
|
||||
|
||||
Conductor = conductor.Conductor
|
||||
Node = node.Node
|
||||
Board = board.Board
|
||||
Location = location.Location
|
||||
Plugin = plugin.Plugin
|
||||
InjectionPlugin = injectionplugin.InjectionPlugin
|
||||
SessionWP = sessionwp.SessionWP
|
||||
WampAgent = wampagent.WampAgent
|
||||
|
||||
__all__ = (
|
||||
Conductor,
|
||||
Node,
|
||||
Board,
|
||||
Location,
|
||||
SessionWP,
|
||||
WampAgent,
|
||||
Plugin,
|
||||
InjectionPlugin,
|
||||
)
|
||||
|
|
|
@ -22,7 +22,7 @@ from iotronic.objects import base
|
|||
from iotronic.objects import utils as obj_utils
|
||||
|
||||
|
||||
class Node(base.IotronicObject):
|
||||
class Board(base.IotronicObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
|
@ -44,75 +44,75 @@ class Node(base.IotronicObject):
|
|||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(node, db_node):
|
||||
def _from_db_object(board, db_board):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in node.fields:
|
||||
node[field] = db_node[field]
|
||||
node.obj_reset_changes()
|
||||
return node
|
||||
for field in board.fields:
|
||||
board[field] = db_board[field]
|
||||
board.obj_reset_changes()
|
||||
return board
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get(cls, context, node_id):
|
||||
"""Find a node based on its id or uuid and return a Node object.
|
||||
def get(cls, context, board_id):
|
||||
"""Find a board based on its id or uuid and return a Board object.
|
||||
|
||||
:param node_id: the id *or* uuid of a node.
|
||||
:returns: a :class:`Node` object.
|
||||
:param board_id: the id *or* uuid of a board.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
if strutils.is_int_like(node_id):
|
||||
return cls.get_by_id(context, node_id)
|
||||
elif uuidutils.is_uuid_like(node_id):
|
||||
return cls.get_by_uuid(context, node_id)
|
||||
if strutils.is_int_like(board_id):
|
||||
return cls.get_by_id(context, board_id)
|
||||
elif uuidutils.is_uuid_like(board_id):
|
||||
return cls.get_by_uuid(context, board_id)
|
||||
else:
|
||||
raise exception.InvalidIdentity(identity=node_id)
|
||||
raise exception.InvalidIdentity(identity=board_id)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, node_id):
|
||||
"""Find a node based on its integer id and return a Node object.
|
||||
def get_by_id(cls, context, board_id):
|
||||
"""Find a board based on its integer id and return a Board object.
|
||||
|
||||
:param node_id: the id of a node.
|
||||
:returns: a :class:`Node` object.
|
||||
:param board_id: the id of a board.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
db_node = cls.dbapi.get_node_by_id(node_id)
|
||||
node = Node._from_db_object(cls(context), db_node)
|
||||
return node
|
||||
db_board = cls.dbapi.get_board_by_id(board_id)
|
||||
board = Board._from_db_object(cls(context), db_board)
|
||||
return board
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
"""Find a node based on uuid and return a Node object.
|
||||
"""Find a board based on uuid and return a Board object.
|
||||
|
||||
:param uuid: the uuid of a node.
|
||||
:returns: a :class:`Node` object.
|
||||
:param uuid: the uuid of a board.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
db_node = cls.dbapi.get_node_by_uuid(uuid)
|
||||
node = Node._from_db_object(cls(context), db_node)
|
||||
return node
|
||||
db_board = cls.dbapi.get_board_by_uuid(uuid)
|
||||
board = Board._from_db_object(cls(context), db_board)
|
||||
return board
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_code(cls, context, code):
|
||||
"""Find a node based on name and return a Node object.
|
||||
"""Find a board based on name and return a Board object.
|
||||
|
||||
:param name: the logical name of a node.
|
||||
:returns: a :class:`Node` object.
|
||||
:param name: the logical name of a board.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
db_node = cls.dbapi.get_node_by_code(code)
|
||||
node = Node._from_db_object(cls(context), db_node)
|
||||
return node
|
||||
db_board = cls.dbapi.get_board_by_code(code)
|
||||
board = Board._from_db_object(cls(context), db_board)
|
||||
return board
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_name(cls, context, name):
|
||||
"""Find a node based on name and return a Node object.
|
||||
"""Find a board based on name and return a Board object.
|
||||
|
||||
:param name: the logical name of a node.
|
||||
:returns: a :class:`Node` object.
|
||||
:param name: the logical name of a board.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
db_node = cls.dbapi.get_node_by_name(name)
|
||||
node = Node._from_db_object(cls(context), db_node)
|
||||
return node
|
||||
db_board = cls.dbapi.get_board_by_name(name)
|
||||
board = Board._from_db_object(cls(context), db_board)
|
||||
return board
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list(cls, context, limit=None, marker=None, sort_key=None,
|
||||
sort_dir=None, filters=None):
|
||||
"""Return a list of Node objects.
|
||||
"""Return a list of Board objects.
|
||||
|
||||
:param context: Security context.
|
||||
:param limit: maximum number of resources to return in a single result.
|
||||
|
@ -120,97 +120,97 @@ class Node(base.IotronicObject):
|
|||
: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:`Node` object.
|
||||
:returns: a list of :class:`Board` object.
|
||||
|
||||
"""
|
||||
db_nodes = cls.dbapi.get_node_list(filters=filters, limit=limit,
|
||||
marker=marker, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
return [Node._from_db_object(cls(context), obj) for obj in db_nodes]
|
||||
db_boards = cls.dbapi.get_board_list(filters=filters, limit=limit,
|
||||
marker=marker, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
return [Board._from_db_object(cls(context), obj) for obj in db_boards]
|
||||
|
||||
@base.remotable_classmethod
|
||||
def reserve(cls, context, tag, node_id):
|
||||
"""Get and reserve a node.
|
||||
def reserve(cls, context, tag, board_id):
|
||||
"""Get and reserve a board.
|
||||
|
||||
To prevent other ManagerServices from manipulating the given
|
||||
Node while a Task is performed, mark it reserved by this host.
|
||||
Board while a Task is performed, mark it reserved by this host.
|
||||
|
||||
:param context: Security context.
|
||||
:param tag: A string uniquely identifying the reservation holder.
|
||||
:param node_id: A node id or uuid.
|
||||
:raises: NodeNotFound if the node is not found.
|
||||
:returns: a :class:`Node` object.
|
||||
:param board_id: A board id or uuid.
|
||||
:raises: BoardNotFound if the board is not found.
|
||||
:returns: a :class:`Board` object.
|
||||
|
||||
"""
|
||||
db_node = cls.dbapi.reserve_node(tag, node_id)
|
||||
node = Node._from_db_object(cls(context), db_node)
|
||||
return node
|
||||
db_board = cls.dbapi.reserve_board(tag, board_id)
|
||||
board = Board._from_db_object(cls(context), db_board)
|
||||
return board
|
||||
|
||||
@base.remotable_classmethod
|
||||
def release(cls, context, tag, node_id):
|
||||
"""Release the reservation on a node.
|
||||
def release(cls, context, tag, board_id):
|
||||
"""Release the reservation on a board.
|
||||
|
||||
:param context: Security context.
|
||||
:param tag: A string uniquely identifying the reservation holder.
|
||||
:param node_id: A node id or uuid.
|
||||
:raises: NodeNotFound if the node is not found.
|
||||
:param board_id: A board id or uuid.
|
||||
:raises: BoardNotFound if the board is not found.
|
||||
|
||||
"""
|
||||
cls.dbapi.release_node(tag, node_id)
|
||||
cls.dbapi.release_board(tag, board_id)
|
||||
|
||||
@base.remotable
|
||||
def create(self, context=None):
|
||||
"""Create a Node record in the DB.
|
||||
"""Create a Board 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
|
||||
node before updates are made.
|
||||
board 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.: Node(context)
|
||||
object, e.g.: Board(context)
|
||||
|
||||
"""
|
||||
values = self.obj_get_changes()
|
||||
db_node = self.dbapi.create_node(values)
|
||||
self._from_db_object(self, db_node)
|
||||
db_board = self.dbapi.create_board(values)
|
||||
self._from_db_object(self, db_board)
|
||||
|
||||
@base.remotable
|
||||
def destroy(self, context=None):
|
||||
"""Delete the Node from the DB.
|
||||
"""Delete the Board 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.: Node(context)
|
||||
object, e.g.: Board(context)
|
||||
"""
|
||||
self.dbapi.destroy_node(self.uuid)
|
||||
self.dbapi.destroy_board(self.uuid)
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def save(self, context=None):
|
||||
"""Save updates to this Node.
|
||||
"""Save updates to this Board.
|
||||
|
||||
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
|
||||
node before updates are made.
|
||||
board 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.: Node(context)
|
||||
object, e.g.: Board(context)
|
||||
"""
|
||||
updates = self.obj_get_changes()
|
||||
self.dbapi.update_node(self.uuid, updates)
|
||||
self.dbapi.update_board(self.uuid, updates)
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
|
@ -222,7 +222,7 @@ class Node(base.IotronicObject):
|
|||
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.: Node(context)
|
||||
object, e.g.: Board(context)
|
||||
"""
|
||||
current = self.__class__.get_by_uuid(self._context, self.uuid)
|
||||
for field in self.fields:
|
|
@ -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 InjectionPlugin(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,
|
||||
'plugin_uuid': obj_utils.str_or_none,
|
||||
'onboot': bool,
|
||||
'status': obj_utils.str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(injection_plugin, db_injection_plugin):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in injection_plugin.fields:
|
||||
injection_plugin[field] = db_injection_plugin[field]
|
||||
injection_plugin.obj_reset_changes()
|
||||
return injection_plugin
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, injection_plugin_id):
|
||||
"""Find a injection_plugin based on its integer id and return a Board object.
|
||||
|
||||
:param injection_plugin_id: the id of a injection_plugin.
|
||||
:returns: a :class:`injection_plugin` object.
|
||||
"""
|
||||
db_inj_plugin = cls.dbapi.get_injection_plugin_by_id(
|
||||
injection_plugin_id)
|
||||
inj_plugin = InjectionPlugin._from_db_object(cls(context),
|
||||
db_inj_plugin)
|
||||
return inj_plugin
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_board_uuid(cls, context, board_uuid):
|
||||
"""Find a injection_plugin based on uuid and return a Board object.
|
||||
|
||||
:param board_uuid: the uuid of a injection_plugin.
|
||||
:returns: a :class:`injection_plugin` object.
|
||||
"""
|
||||
db_inj_plugin = cls.dbapi.get_injection_plugin_by_board_uuid(
|
||||
board_uuid)
|
||||
inj_plugin = InjectionPlugin._from_db_object(cls(context),
|
||||
db_inj_plugin)
|
||||
return inj_plugin
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_plugin_uuid(cls, context, plugin_uuid):
|
||||
"""Find a injection_plugin based on uuid and return a Board object.
|
||||
|
||||
:param plugin_uuid: the uuid of a injection_plugin.
|
||||
:returns: a :class:`injection_plugin` object.
|
||||
"""
|
||||
db_inj_plugin = cls.dbapi.get_injection_plugin_by_plugin_uuid(
|
||||
plugin_uuid)
|
||||
inj_plugin = InjectionPlugin._from_db_object(cls(context),
|
||||
db_inj_plugin)
|
||||
return inj_plugin
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get(cls, context, board_uuid, plugin_uuid):
|
||||
"""Find a injection_plugin based on uuid and return a Board object.
|
||||
|
||||
:param board_uuid: the uuid of a injection_plugin.
|
||||
:returns: a :class:`injection_plugin` object.
|
||||
"""
|
||||
db_inj_plugin = cls.dbapi.get_injection_plugin_by_uuids(board_uuid,
|
||||
plugin_uuid)
|
||||
inj_plugin = InjectionPlugin._from_db_object(cls(context),
|
||||
db_inj_plugin)
|
||||
return inj_plugin
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list(cls, context, board_uuid):
|
||||
"""Return a list of InjectionPlugin 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:`InjectionPlugin` object.
|
||||
|
||||
"""
|
||||
db_injs = cls.dbapi.get_injection_plugin_list(board_uuid)
|
||||
return [InjectionPlugin._from_db_object(cls(context), obj)
|
||||
for obj in db_injs]
|
||||
|
||||
@base.remotable
|
||||
def create(self, context=None):
|
||||
"""Create a InjectionPlugin 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
|
||||
injection_plugin 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.: InjectionPlugin(context)
|
||||
|
||||
"""
|
||||
values = self.obj_get_changes()
|
||||
db_injection_plugin = self.dbapi.create_injection_plugin(values)
|
||||
self._from_db_object(self, db_injection_plugin)
|
||||
|
||||
@base.remotable
|
||||
def destroy(self, context=None):
|
||||
"""Delete the InjectionPlugin 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.: InjectionPlugin(context)
|
||||
"""
|
||||
self.dbapi.destroy_injection_plugin(self.id)
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def save(self, context=None):
|
||||
"""Save updates to this InjectionPlugin.
|
||||
|
||||
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
|
||||
injection_plugin 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.: InjectionPlugin(context)
|
||||
"""
|
||||
updates = self.obj_get_changes()
|
||||
self.dbapi.update_injection_plugin(self.id, updates)
|
||||
self.obj_reset_changes()
|
|
@ -29,7 +29,7 @@ class Location(base.IotronicObject):
|
|||
|
||||
fields = {
|
||||
'id': int,
|
||||
'node_id': obj_utils.int_or_none,
|
||||
'board_id': obj_utils.int_or_none,
|
||||
'longitude': obj_utils.str_or_none,
|
||||
'latitude': obj_utils.str_or_none,
|
||||
'altitude': obj_utils.str_or_none,
|
||||
|
@ -109,12 +109,12 @@ class Location(base.IotronicObject):
|
|||
return Location._from_db_object_list(db_locations, cls, context)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list_by_node_uuid(cls, context, node_uuid, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of Location objects associated with a given node ID.
|
||||
def list_by_board_uuid(cls, context, board_uuid, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of Location objects associated with a given board ID.
|
||||
|
||||
:param context: Security context.
|
||||
:param node_id: the ID of the node.
|
||||
:param board_id: the ID of the board.
|
||||
: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.
|
||||
|
@ -122,20 +122,21 @@ class Location(base.IotronicObject):
|
|||
:returns: a list of :class:`Location` object.
|
||||
|
||||
"""
|
||||
node_id = cls.dbapi.get_node_id_by_uuid(node_uuid)[0]
|
||||
db_locations = cls.dbapi.get_locations_by_node_id(node_id, limit=limit,
|
||||
marker=marker,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
return Location._from_db_object_list(db_locations, cls, context)
|
||||
board_id = cls.dbapi.get_board_id_by_uuid(board_uuid)[0]
|
||||
db_loc = cls.dbapi.get_locations_by_board_id(board_id,
|
||||
limit=limit,
|
||||
marker=marker,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
return Location._from_db_object_list(db_loc, cls, context)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list_by_node_id(cls, context, node_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of Location objects associated with a given node ID.
|
||||
def list_by_board_id(cls, context, board_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of Location objects associated with a given board ID.
|
||||
|
||||
:param context: Security context.
|
||||
:param node_id: the ID of the node.
|
||||
:param board_id: the ID of the board.
|
||||
: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.
|
||||
|
@ -143,11 +144,12 @@ class Location(base.IotronicObject):
|
|||
:returns: a list of :class:`Location` object.
|
||||
|
||||
"""
|
||||
db_locations = cls.dbapi.get_locations_by_node_id(node_id, limit=limit,
|
||||
marker=marker,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
return Location._from_db_object_list(db_locations, cls, context)
|
||||
db_loc = cls.dbapi.get_locations_by_board_id(board_id,
|
||||
limit=limit,
|
||||
marker=marker,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
return Location._from_db_object_list(db_loc, cls, context)
|
||||
|
||||
@base.remotable
|
||||
def create(self, context=None):
|
||||
|
|
|
@ -21,6 +21,20 @@ from iotronic.db import api as db_api
|
|||
from iotronic.objects import base
|
||||
from iotronic.objects import utils as obj_utils
|
||||
|
||||
ACTIONS = ['PluginCall', 'PluginStop', 'PluginStart',
|
||||
'PluginStatus', 'PluginReboot']
|
||||
NO_PARAMS = ['PluginStatus', 'PluginReboot']
|
||||
|
||||
|
||||
def is_valid_action(action):
|
||||
if action not in ACTIONS:
|
||||
raise exception.InvalidPluginAction(action=action)
|
||||
return True
|
||||
|
||||
|
||||
def want_params(action):
|
||||
return False if action in NO_PARAMS else True
|
||||
|
||||
|
||||
class Plugin(base.IotronicObject):
|
||||
# Version 1.0: Initial version
|
||||
|
@ -34,7 +48,8 @@ class Plugin(base.IotronicObject):
|
|||
'name': obj_utils.str_or_none,
|
||||
'owner': obj_utils.str_or_none,
|
||||
'public': bool,
|
||||
'config': obj_utils.str_or_none,
|
||||
'code': obj_utils.str_or_none,
|
||||
'callable': bool,
|
||||
'extra': obj_utils.dict_or_none,
|
||||
}
|
||||
|
||||
|
@ -48,10 +63,10 @@ class Plugin(base.IotronicObject):
|
|||
|
||||
@base.remotable_classmethod
|
||||
def get(cls, context, plugin_id):
|
||||
"""Find a plugin based on its id or uuid and return a Node object.
|
||||
"""Find a plugin based on its id or uuid and return a Board object.
|
||||
|
||||
:param plugin_id: the id *or* uuid of a plugin.
|
||||
:returns: a :class:`Node` object.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
if strutils.is_int_like(plugin_id):
|
||||
return cls.get_by_id(context, plugin_id)
|
||||
|
@ -62,10 +77,10 @@ class Plugin(base.IotronicObject):
|
|||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, plugin_id):
|
||||
"""Find a plugin based on its integer id and return a Node object.
|
||||
"""Find a plugin based on its integer id and return a Board object.
|
||||
|
||||
:param plugin_id: the id of a plugin.
|
||||
:returns: a :class:`Node` object.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
db_plugin = cls.dbapi.get_plugin_by_id(plugin_id)
|
||||
plugin = Plugin._from_db_object(cls(context), db_plugin)
|
||||
|
@ -73,10 +88,10 @@ class Plugin(base.IotronicObject):
|
|||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
"""Find a plugin based on uuid and return a Node object.
|
||||
"""Find a plugin based on uuid and return a Board object.
|
||||
|
||||
:param uuid: the uuid of a plugin.
|
||||
:returns: a :class:`Node` object.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
db_plugin = cls.dbapi.get_plugin_by_uuid(uuid)
|
||||
plugin = Plugin._from_db_object(cls(context), db_plugin)
|
||||
|
@ -84,10 +99,10 @@ class Plugin(base.IotronicObject):
|
|||
|
||||
@base.remotable_classmethod
|
||||
def get_by_name(cls, context, name):
|
||||
"""Find a plugin based on name and return a Node object.
|
||||
"""Find a plugin based on name and return a Board object.
|
||||
|
||||
:param name: the logical name of a plugin.
|
||||
:returns: a :class:`Node` object.
|
||||
:returns: a :class:`Board` object.
|
||||
"""
|
||||
db_plugin = cls.dbapi.get_plugin_by_name(name)
|
||||
plugin = Plugin._from_db_object(cls(context), db_plugin)
|
||||
|
@ -184,6 +199,6 @@ class Plugin(base.IotronicObject):
|
|||
current = self.__class__.get_by_uuid(self._context, self.uuid)
|
||||
for field in self.fields:
|
||||
if (hasattr(
|
||||
self, base.get_attrname(field)) and
|
||||
self[field] != current[field]):
|
||||
self[field] = current[field]
|
||||
self, base.get_attrname(field))
|
||||
and self[field] != current[field]):
|
||||
self[field] = current[field]
|
||||
|
|
|
@ -18,6 +18,7 @@ from iotronic.db import api as dbapi
|
|||
from iotronic.objects import base
|
||||
from iotronic.objects import utils as obj_utils
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
|
||||
class SessionWP(base.IotronicObject):
|
||||
|
@ -27,9 +28,9 @@ class SessionWP(base.IotronicObject):
|
|||
|
||||
fields = {
|
||||
'id': int,
|
||||
'node_uuid': obj_utils.str_or_none,
|
||||
'board_uuid': obj_utils.str_or_none,
|
||||
'session_id': obj_utils.str_or_none,
|
||||
'node_id': obj_utils.int_or_none,
|
||||
'board_id': obj_utils.int_or_none,
|
||||
'valid': bool,
|
||||
}
|
||||
|
||||
|
@ -51,16 +52,18 @@ class SessionWP(base.IotronicObject):
|
|||
obj) for obj in db_objects]
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get(cls, context, session_id):
|
||||
def get(cls, context, session_or_board_uuid):
|
||||
"""Find a session based on its id or uuid and return a SessionWP object.
|
||||
|
||||
:param session_id: the id *or* uuid of a session.
|
||||
:returns: a :class:`SessionWP` object.
|
||||
"""
|
||||
if strutils.is_int_like(session_id):
|
||||
return cls.get_by_id(context, session_id)
|
||||
if strutils.is_int_like(session_or_board_uuid):
|
||||
return cls.get_by_id(context, session_or_board_uuid)
|
||||
elif uuidutils.is_uuid_like(session_or_board_uuid):
|
||||
return cls.get_by_uuid(context, session_or_board_uuid)
|
||||
else:
|
||||
raise exception.InvalidIdentity(identity=session_id)
|
||||
raise exception.InvalidIdentity(identity=session_or_board_uuid)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, ses_id):
|
||||
|
@ -74,14 +77,15 @@ class SessionWP(base.IotronicObject):
|
|||
return session
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_session_by_node_uuid(cls, context, node_uuid, valid=True):
|
||||
def get_session_by_board_uuid(cls, context, board_uuid, valid=True):
|
||||
"""Find a session based on uuid and return a :class:`SessionWP` object.
|
||||
|
||||
:param node_uuid: the uuid of a node.
|
||||
:param board_uuid: the uuid of a board.
|
||||
:param context: Security context
|
||||
:returns: a :class:`SessionWP` object.
|
||||
"""
|
||||
db_session = cls.dbapi.get_session_by_node_uuid(node_uuid, valid)
|
||||
|
||||
db_session = cls.dbapi.get_session_by_board_uuid(board_uuid, valid)
|
||||
session = SessionWP._from_db_object(cls(context), db_session)
|
||||
return session
|
||||
|
||||
|
@ -174,4 +178,4 @@ class SessionWP(base.IotronicObject):
|
|||
if (hasattr(
|
||||
self, base.get_attrname(field))
|
||||
and self[field] != current[field]):
|
||||
self[field] = current[field]
|
||||
self[field] = current[field]
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Generic Node base class for all workers that run on hosts."""
|
||||
"""Generic Board base class for all workers that run on hosts."""
|
||||
|
||||
import errno
|
||||
import os
|
||||
|
|
|
@ -106,8 +106,9 @@ class WampFrontend(wamp.ApplicationSession):
|
|||
|
||||
import iotronic.wamp.functions as fun
|
||||
|
||||
self.subscribe(fun.node_on_leave, 'wamp.session.on_leave')
|
||||
self.subscribe(fun.node_on_join, 'wamp.session.on_join')
|
||||
self.subscribe(fun.board_on_leave, 'wamp.session.on_leave')
|
||||
self.subscribe(fun.board_on_join, 'wamp.session.on_join')
|
||||
# self.subscribe(fun.on_board_connect, 'board.connection')
|
||||
|
||||
try:
|
||||
if CONF.wamp.register_agent:
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# under the License.
|
||||
|
||||
from iotronic.common import rpc
|
||||
from iotronic.common import states
|
||||
from iotronic.conductor import rpcapi
|
||||
from iotronic import objects
|
||||
from oslo_config import cfg
|
||||
|
@ -43,16 +44,38 @@ def echo(data):
|
|||
return data
|
||||
|
||||
|
||||
def node_on_leave(session_id):
|
||||
LOG.debug('A node with %s disconnectd', session_id)
|
||||
def board_on_leave(session_id):
|
||||
LOG.debug('A board with %s disconnectd', session_id)
|
||||
|
||||
try:
|
||||
old_session = objects.SessionWP({}).get_by_session_id({}, session_id)
|
||||
old_session = objects.SessionWP.get(ctxt, session_id)
|
||||
old_session.valid = False
|
||||
old_session.save()
|
||||
LOG.debug('Session %s deleted', session_id)
|
||||
except Exception:
|
||||
LOG.debug('session %s not found', session_id)
|
||||
|
||||
board = objects.Board.get_by_uuid(ctxt, old_session.board_uuid)
|
||||
board.status = states.OFFLINE
|
||||
board.save()
|
||||
LOG.debug('Board %s is now %s', old_session.uuid, states.OFFLINE)
|
||||
|
||||
|
||||
def on_board_connect(board_uuid, session_id, msg):
|
||||
if msg == 'connection':
|
||||
try:
|
||||
board = objects.Board.get_by_uuid(ctxt, board_uuid)
|
||||
board.status = states.ONLINE
|
||||
session_data = {'board_id': board.id,
|
||||
'board_uuid': board.uuid,
|
||||
'session_id': session_id}
|
||||
session = objects.SessionWP(ctxt, **session_data)
|
||||
session.create()
|
||||
board.save()
|
||||
LOG.debug('Board %s is now %s', board_uuid, states.ONLINE)
|
||||
except Exception:
|
||||
LOG.debug(Exception.message)
|
||||
|
||||
|
||||
def connection(uuid, session):
|
||||
return c.connection(ctxt, uuid, session)
|
||||
|
@ -62,5 +85,5 @@ def registration(code, session):
|
|||
return c.registration(ctxt, code, session)
|
||||
|
||||
|
||||
def node_on_join(session_id):
|
||||
LOG.debug('A node with %s joined', session_id)
|
||||
def board_on_join(session_id):
|
||||
LOG.debug('A board with %s joined', session_id['session'])
|
||||
|
|
|
@ -21,6 +21,11 @@ ERROR = 'ERROR'
|
|||
WARNING = 'WARNING'
|
||||
|
||||
|
||||
def deserialize(received):
|
||||
m = json.loads(received)
|
||||
return WampMessage(**m)
|
||||
|
||||
|
||||
class WampMessage(object):
|
||||
def __init__(self, message=None, result=None):
|
||||
self.message = message
|
||||
|
@ -29,10 +34,6 @@ class WampMessage(object):
|
|||
def serialize(self):
|
||||
return json.dumps(self, default=lambda o: o.__dict__)
|
||||
|
||||
def deserialize(self, received):
|
||||
self.__dict__ = json.loads(received)
|
||||
return self
|
||||
|
||||
|
||||
class WampSuccess(WampMessage):
|
||||
def __init__(self, msg=None):
|
||||
|
|
|
@ -56,11 +56,11 @@ DEFAULT CHARACTER SET = utf8;
|
|||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `iotronic`.`nodes`
|
||||
-- Table `iotronic`.`boards`
|
||||
-- -----------------------------------------------------
|
||||
DROP TABLE IF EXISTS `iotronic`.`nodes` ;
|
||||
DROP TABLE IF EXISTS `iotronic`.`boards` ;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `iotronic`.`nodes` (
|
||||
CREATE TABLE IF NOT EXISTS `iotronic`.`boards` (
|
||||
`created_at` DATETIME NULL DEFAULT NULL,
|
||||
`updated_at` DATETIME NULL DEFAULT NULL,
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
|
@ -95,12 +95,12 @@ CREATE TABLE IF NOT EXISTS `iotronic`.`locations` (
|
|||
`longitude` VARCHAR(18) NULL DEFAULT NULL,
|
||||
`latitude` VARCHAR(18) NULL DEFAULT NULL,
|
||||
`altitude` VARCHAR(18) NULL DEFAULT NULL,
|
||||
`node_id` INT(11) NOT NULL,
|
||||
`board_id` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `node_id` (`node_id` ASC),
|
||||
INDEX `board_id` (`board_id` ASC),
|
||||
CONSTRAINT `location_ibfk_1`
|
||||
FOREIGN KEY (`node_id`)
|
||||
REFERENCES `iotronic`.`nodes` (`id`)
|
||||
FOREIGN KEY (`board_id`)
|
||||
REFERENCES `iotronic`.`boards` (`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB
|
||||
|
@ -119,14 +119,14 @@ CREATE TABLE IF NOT EXISTS `iotronic`.`sessions` (
|
|||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`valid` TINYINT(1) NOT NULL DEFAULT '1',
|
||||
`session_id` VARCHAR(18) NOT NULL,
|
||||
`node_uuid` VARCHAR(36) NOT NULL,
|
||||
`node_id` INT(11) NOT NULL,
|
||||
`board_uuid` VARCHAR(36) NOT NULL,
|
||||
`board_id` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `session_id` (`session_id` ASC),
|
||||
INDEX `session_node_id` (`node_id` ASC),
|
||||
CONSTRAINT `session_node_id`
|
||||
FOREIGN KEY (`node_id`)
|
||||
REFERENCES `iotronic`.`nodes` (`id`)
|
||||
INDEX `session_board_id` (`board_id` ASC),
|
||||
CONSTRAINT `session_board_id`
|
||||
FOREIGN KEY (`board_id`)
|
||||
REFERENCES `iotronic`.`boards` (`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB
|
||||
|
@ -145,7 +145,8 @@ CREATE TABLE IF NOT EXISTS `iotronic`.`plugins` (
|
|||
`uuid` VARCHAR(36) NOT NULL,
|
||||
`name` VARCHAR(255) NULL DEFAULT NULL,
|
||||
`public` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`config` TEXT NULL DEFAULT NULL,
|
||||
`code` TEXT NULL DEFAULT NULL,
|
||||
`callable` TINYINT(1) NOT NULL,
|
||||
`extra` TEXT NULL DEFAULT NULL,
|
||||
`owner` VARCHAR(36) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
|
@ -157,28 +158,27 @@ DEFAULT CHARACTER SET = utf8;
|
|||
-- -----------------------------------------------------
|
||||
-- Table `iotronic`.`injected_plugins`
|
||||
-- -----------------------------------------------------
|
||||
DROP TABLE IF EXISTS `iotronic`.`injected_plugins` ;
|
||||
DROP TABLE IF EXISTS `iotronic`.`injection_plugins` ;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `iotronic`.`injected_plugins` (
|
||||
CREATE TABLE IF NOT EXISTS `iotronic`.`injection_plugins` (
|
||||
`created_at` DATETIME NULL DEFAULT NULL,
|
||||
`updated_at` DATETIME NULL DEFAULT NULL,
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`node_uuid` VARCHAR(36) NOT NULL,
|
||||
`node_id` INT(11) NOT NULL,
|
||||
`board_uuid` VARCHAR(36) NOT NULL,
|
||||
`plugin_uuid` VARCHAR(36) NOT NULL,
|
||||
`plugin_id` INT(11) NOT NULL,
|
||||
`status` VARCHAR(15) NOT NULL DEFAULT 'injected',
|
||||
`onboot` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `node_id` (`node_id` ASC),
|
||||
CONSTRAINT `node_id`
|
||||
FOREIGN KEY (`node_id`)
|
||||
REFERENCES `iotronic`.`nodes` (`id`)
|
||||
INDEX `board_uuid` (`board_uuid` ASC),
|
||||
CONSTRAINT `board_uuid`
|
||||
FOREIGN KEY (`board_uuid`)
|
||||
REFERENCES `iotronic`.`boards` (`uuid`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
INDEX `plugin_id` (`plugin_id` ASC),
|
||||
CONSTRAINT `plugin_id`
|
||||
FOREIGN KEY (`plugin_id`)
|
||||
REFERENCES `iotronic`.`plugins` (`id`)
|
||||
INDEX `plugin_uuid` (`plugin_uuid` ASC),
|
||||
CONSTRAINT `plugin_uuid`
|
||||
FOREIGN KEY (`plugin_uuid`)
|
||||
REFERENCES `iotronic`.`plugins` (`uuid`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE)
|
||||
ENGINE = InnoDB
|
||||
|
@ -191,12 +191,19 @@ SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
|
|||
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
|
||||
|
||||
|
||||
-- insert testing nodes
|
||||
INSERT INTO `nodes` VALUES
|
||||
('2017-02-20 10:38:26',NULL,132,'f3961f7a-c937-4359-8848-fb64aa8eeaaa','12345','registered','node','server',NULL,'eee383360cc14c44b9bf21e1e003a4f3','4adfe95d49ad41398e00ecda80257d21',0,'{}','{}'),
|
||||
-- insert testing boards
|
||||
INSERT INTO `boards` VALUES
|
||||
('2017-02-20 10:38:26',NULL,132,'f3961f7a-c937-4359-8848-fb64aa8eeaaa','12345','registered','laptop-14','server',NULL,'eee383360cc14c44b9bf21e1e003a4f3','4adfe95d49ad41398e00ecda80257d21',0,'{}','{}'),
|
||||
('2017-02-20 10:38:45',NULL,133,'ba1efce9-cad9-4ae1-a5d1-d90a8d203d3b','yunyun','registered','yun22','yun',NULL,'eee383360cc14c44b9bf21e1e003a4f3','4adfe95d49ad41398e00ecda80257d21',0,'{}','{}'),
|
||||
('2017-02-20 10:39:08',NULL,134,'65f9db36-9786-4803-b66f-51dcdb60066e','test','registered','test','server',NULL,'eee383360cc14c44b9bf21e1e003a4f3','4adfe95d49ad41398e00ecda80257d21',0,'{}','{}');
|
||||
INSERT INTO `locations` VALUES
|
||||
('2017-02-20 10:38:26',NULL,6,'2','1','3',132),
|
||||
('2017-02-20 10:38:45',NULL,7,'2','1','3',133),
|
||||
('2017-02-20 10:39:08',NULL,8,'2','1','3',134)
|
||||
('2017-02-20 10:39:08',NULL,8,'2','1','3',134);
|
||||
INSERT INTO `plugins` VALUES
|
||||
('2017-02-20 10:38:26',NULL,132,'edff22cd-9148-4ad8-b35b-51dcdb60066e','runner','0','V# Copyright 2017 MDSLAB - University of Messina\u000a# All Rights Reserved.\u000a#\u000a# Licensed under the Apache License, Version 2.0 (the "License"); you may\u000a# not use this file except in compliance with the License. You may obtain\u000a# a copy of the License at\u000a#\u000a# http://www.apache.org/licenses/LICENSE-2.0\u000a#\u000a# Unless required by applicable law or agreed to in writing, software\u000a# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\u000a# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\u000a# License for the specific language governing permissions and limitations\u000a# under the License.\u000a\u000afrom iotronic_lightningrod.plugins import Plugin\u000a\u000afrom oslo_log import log as logging\u000aLOG = logging.getLogger(__name__)\u000a\u000a# User imports\u000aimport time\u000a\u000a\u000a\u000aclass Worker(Plugin.Plugin):\u000a def __init__(self, name, th_result, plugin_conf=None):\u000a super(Worker, self).__init__(name, th_result, plugin_conf)\u000a\u000a def run(self):\u000a LOG.info("Plugin " + self.name + " starting...")\u000a while(self._is_running):\u000a print(self.plugin_conf[''message''])\u000a time.sleep(1) \u000a
|
||||
p1
|
||||
.',0,'{}','eee383360cc14c44b9bf21e1e003a4f3')
|
||||
('2017-02-20 10:38:26',NULL,133,'edff22cd-9148-4ad8-b35b-c0c80abf1e8a','zero','0','Vfrom iotronic_lightningrod.plugins import Plugin\u000a\u000afrom oslo_log import log as logging\u000a\u000aLOG = logging.getLogger(__name__)\u000a\u000a\u000a# User imports\u000a\u000a\u000aclass Worker(Plugin.Plugin):\u000a def __init__(self, name, is_running):\u000a super(Worker, self).__init__(name, is_running)\u000a\u000a def run(self):\u000a LOG.info("Plugin process completed!")\u000a #self.Done()
|
||||
p1
|
||||
.',1,'{}','eee383360cc14c44b9bf21e1e003a4f3');
|
|
@ -1,195 +0,0 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
|
||||
import sys
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
|
||||
token = None
|
||||
iotronic_url = "http://192.168.17.102:1288/v1"
|
||||
|
||||
try:
|
||||
os.environ['OS_AUTH_URL']
|
||||
except Exception:
|
||||
print("load the rc")
|
||||
sys.exit(1)
|
||||
|
||||
url = os.environ['OS_AUTH_URL'] + "/auth/tokens"
|
||||
|
||||
|
||||
def get_token(user, project, psw):
|
||||
payload = {"auth": {
|
||||
"identity": {
|
||||
"methods": ["password"],
|
||||
"password": {
|
||||
"user": {
|
||||
"name": user,
|
||||
"domain": {"id": "default"},
|
||||
"password": psw
|
||||
}
|
||||
}
|
||||
},
|
||||
"scope": {
|
||||
"project": {
|
||||
"name": project,
|
||||
"domain": {"id": "default"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headers = {
|
||||
'content-type': "application/json",
|
||||
}
|
||||
|
||||
response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
|
||||
token=response.headers.get('X-Subject-Token')
|
||||
if token:
|
||||
r=json.loads(response.text)['token']['roles']
|
||||
roles=[str(x['name']) for x in r]
|
||||
|
||||
print(user + " in " + project + ' with roles: '+ " ".join(roles) )
|
||||
|
||||
return token
|
||||
|
||||
|
||||
def node_manager(argv):
|
||||
actions = ['show', 'list', 'create', 'delete', 'update']
|
||||
if argv[0] not in actions or len(argv) == 0:
|
||||
print("node list|create|delete|show|update")
|
||||
sys.exit()
|
||||
|
||||
global iotronic_url, token
|
||||
if not token:
|
||||
token = get_token(os.environ['OS_USERNAME'], os.environ['OS_PROJECT_NAME'], os.environ['OS_PASSWORD'])
|
||||
|
||||
# print(token)
|
||||
|
||||
headers = {'content-type': "application/json", 'x-auth-token': token, }
|
||||
|
||||
if argv[0] == 'list':
|
||||
url = iotronic_url + "/nodes"
|
||||
response = requests.request("GET", url, headers=headers)
|
||||
|
||||
elif argv[0] == 'create':
|
||||
code = argv[1]
|
||||
name = argv[2]
|
||||
lat = argv[3]
|
||||
lon = argv[4]
|
||||
alt = argv[5]
|
||||
typ = argv[6]
|
||||
|
||||
url = iotronic_url + "/nodes"
|
||||
payload = {
|
||||
"code": code,
|
||||
"mobile": False,
|
||||
"location": [
|
||||
{
|
||||
"latitude": lat,
|
||||
"altitude": alt,
|
||||
"longitude": lon
|
||||
}
|
||||
],
|
||||
"type": typ,
|
||||
"name": name
|
||||
}
|
||||
|
||||
response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
|
||||
|
||||
elif argv[0] == 'delete':
|
||||
node = argv[1]
|
||||
url = iotronic_url + "/nodes/" + node
|
||||
response = requests.request("DELETE", url, headers=headers)
|
||||
|
||||
elif argv[0] == 'show':
|
||||
node = argv[1]
|
||||
url = iotronic_url + "/nodes/" + node
|
||||
response = requests.request("GET", url, headers=headers)
|
||||
|
||||
elif argv[0] == 'update':
|
||||
node = argv[1]
|
||||
values = {}
|
||||
for opt in argv[2:]:
|
||||
key, val = opt.split(':')
|
||||
values[key] = val
|
||||
url = iotronic_url + "/nodes/" + node
|
||||
payload = values
|
||||
response = requests.request("PATCH", url, data=json.dumps(payload), headers=headers)
|
||||
|
||||
else:
|
||||
print ("node list|create|delete|show|update")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
print(json.dumps(response.json(), indent=2))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def plugin_manager(argv):
|
||||
actions = ['show', 'list', 'create', 'delete', 'update', 'inject']
|
||||
if argv[0] not in actions or len(argv) == 0:
|
||||
print("plugin list|create|delete|show|update|inject")
|
||||
sys.exit()
|
||||
|
||||
global iotronic_url, token
|
||||
if not token:
|
||||
token = get_token(os.environ['OS_USERNAME'], os.environ['OS_PROJECT_NAME'], os.environ['OS_PASSWORD'])
|
||||
|
||||
headers = {'content-type': "application/json", 'x-auth-token': token, }
|
||||
|
||||
if argv[0] == 'list':
|
||||
url = iotronic_url + "/plugins"
|
||||
response = requests.request("GET", url, headers=headers)
|
||||
|
||||
elif argv[0] == 'create':
|
||||
f=argv[1]
|
||||
with open(f, 'r') as fil:
|
||||
t = fil.read()
|
||||
url = iotronic_url + "/plugins"
|
||||
payload={"name": f, "config": t}
|
||||
response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
|
||||
|
||||
elif argv[0] == 'delete':
|
||||
plugin = argv[1]
|
||||
url = iotronic_url + "/plugins/" + plugin
|
||||
response = requests.request("DELETE", url, headers=headers)
|
||||
|
||||
elif argv[0] == 'show':
|
||||
plugin = argv[1]
|
||||
url = iotronic_url + "/plugins/" + plugin
|
||||
response = requests.request("GET", url, headers=headers)
|
||||
|
||||
elif argv[0] == 'update':
|
||||
plugin = argv[1]
|
||||
values = {}
|
||||
for opt in argv[2:]:
|
||||
key, val = opt.split(':')
|
||||
values[key] = val
|
||||
url = iotronic_url + "/plugins/" + plugin
|
||||
payload = values
|
||||
response = requests.request("PATCH", url, data=json.dumps(payload), headers=headers)
|
||||
|
||||
else:
|
||||
print ("node list|create|delete|show|update")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
print(json.dumps(response.json(), indent=2))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
argv = sys.argv
|
||||
if len(argv) <= 2:
|
||||
print("USAGE: iotronic node|plugin [OPTIONS]")
|
||||
sys.exit()
|
||||
|
||||
if argv[1] == 'node':
|
||||
node_manager(argv[2:])
|
||||
elif argv[1] == 'plugin':
|
||||
plugin_manager(argv[2:])
|
||||
else:
|
||||
print("USAGE: iotronic node|plugin [OPTIONS]")
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2017 MDSLAB - University of Messina
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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_lightningrod.plugins import Plugin
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# User imports
|
||||
import time
|
||||
|
||||
|
||||
class Worker(Plugin.Plugin):
|
||||
def __init__(self, uuid, name, q_result=None, params=None):
|
||||
super(Worker, self).__init__(uuid, name, q_result, params)
|
||||
|
||||
def run(self):
|
||||
LOG.info("Plugin " + self.name + " starting...")
|
||||
LOG.info(self.params)
|
||||
|
||||
while (self._is_running):
|
||||
print(self.params['message'])
|
||||
time.sleep(1)
|
|
@ -0,0 +1,33 @@
|
|||
# Copyright 2017 MDSLAB - University of Messina
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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_lightningrod.plugins import Plugin
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# User imports
|
||||
|
||||
|
||||
class Worker(Plugin.Plugin):
|
||||
def __init__(self, uuid, name, q_result, params=None):
|
||||
super(Worker, self).__init__(uuid, name, q_result, params)
|
||||
|
||||
def run(self):
|
||||
LOG.info("Input parameters: " + str(self.params))
|
||||
LOG.info("Plugin " + self.name + " process completed!")
|
||||
self.q_result.put("ZERO RESULT")
|
14
utils/zero
14
utils/zero
|
@ -1,14 +0,0 @@
|
|||
from iotronic_lightningrod.plugins import Plugin
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# User imports
|
||||
|
||||
class Worker(Plugin.Plugin):
|
||||
def __init__(self, name, is_running):
|
||||
super(Worker, self).__init__(name, is_running)
|
||||
|
||||
def run(self):
|
||||
LOG.info("Plugin process completed!")
|
||||
#self.Done()
|
Loading…
Reference in New Issue