create, destroy, list and show commands for plugin have been implemented
Change-Id: I5a65c940edb676150a83adf62a57cb689fc0591e
This commit is contained in:
parent
ac70869ee9
commit
7f717e995b
@ -3,6 +3,7 @@
|
|||||||
function build_install {
|
function build_install {
|
||||||
python setup.py build
|
python setup.py build
|
||||||
python setup.py install
|
python setup.py install
|
||||||
|
cp utils/iotronic_curl_client /usr/bin/iotronic
|
||||||
}
|
}
|
||||||
|
|
||||||
function restart_apache {
|
function restart_apache {
|
||||||
|
@ -31,6 +31,7 @@ app = {
|
|||||||
'/',
|
'/',
|
||||||
'/v1',
|
'/v1',
|
||||||
'/v1/nodes/[a-z0-9\-]',
|
'/v1/nodes/[a-z0-9\-]',
|
||||||
|
'/v1/plugins/[a-z0-9\-]',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ Version 1 of the Iotronic API
|
|||||||
from iotronic.api.controllers import base
|
from iotronic.api.controllers import base
|
||||||
from iotronic.api.controllers import link
|
from iotronic.api.controllers import link
|
||||||
from iotronic.api.controllers.v1 import node
|
from iotronic.api.controllers.v1 import node
|
||||||
|
from iotronic.api.controllers.v1 import plugin
|
||||||
from iotronic.api import expose
|
from iotronic.api import expose
|
||||||
from iotronic.common.i18n import _
|
from iotronic.common.i18n import _
|
||||||
import pecan
|
import pecan
|
||||||
@ -52,6 +53,8 @@ class V1(base.APIBase):
|
|||||||
nodes = [link.Link]
|
nodes = [link.Link]
|
||||||
"""Links to the nodes resource"""
|
"""Links to the nodes resource"""
|
||||||
|
|
||||||
|
plugins = [link.Link]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def convert():
|
def convert():
|
||||||
v1 = V1()
|
v1 = V1()
|
||||||
@ -65,6 +68,14 @@ class V1(base.APIBase):
|
|||||||
bookmark=True)
|
bookmark=True)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
v1.plugins = [link.Link.make_link('self', pecan.request.host_url,
|
||||||
|
'plugins', ''),
|
||||||
|
link.Link.make_link('bookmark',
|
||||||
|
pecan.request.host_url,
|
||||||
|
'plugins', '',
|
||||||
|
bookmark=True)
|
||||||
|
]
|
||||||
|
|
||||||
'''
|
'''
|
||||||
v1.links = [link.Link.make_link('self', pecan.request.host_url,
|
v1.links = [link.Link.make_link('self', pecan.request.host_url,
|
||||||
'v1', '', bookmark=True),
|
'v1', '', bookmark=True),
|
||||||
@ -82,6 +93,7 @@ class Controller(rest.RestController):
|
|||||||
"""Version 1 API controller root."""
|
"""Version 1 API controller root."""
|
||||||
|
|
||||||
nodes = node.NodesController()
|
nodes = node.NodesController()
|
||||||
|
plugins = plugin.PluginsController()
|
||||||
|
|
||||||
@expose.expose(V1)
|
@expose.expose(V1)
|
||||||
def get(self):
|
def get(self):
|
||||||
|
168
iotronic/api/controllers/v1/plugin.py
Normal file
168
iotronic/api/controllers/v1/plugin.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
# 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.v1 import collection
|
||||||
|
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 import objects
|
||||||
|
import pecan
|
||||||
|
from pecan import rest
|
||||||
|
import wsme
|
||||||
|
from wsme import types as wtypes
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(base.APIBase):
|
||||||
|
"""API representation of a plugin.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
uuid = types.uuid
|
||||||
|
name = wsme.wsattr(wtypes.text)
|
||||||
|
config = wsme.wsattr(wtypes.text)
|
||||||
|
extra = types.jsontype
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _convert(plugin, url, expand=True, show_password=True):
|
||||||
|
if not expand:
|
||||||
|
except_list = ['name', 'code', 'status', 'uuid', 'session', 'type']
|
||||||
|
plugin.unset_fields_except(except_list)
|
||||||
|
return plugin
|
||||||
|
return plugin
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def convert(cls, rpc_plugin, expand=True):
|
||||||
|
plugin = Plugin(**rpc_plugin.as_dict())
|
||||||
|
# plugin.id = rpc_plugin.id
|
||||||
|
return cls._convert(plugin, pecan.request.host_url,
|
||||||
|
expand,
|
||||||
|
pecan.request.context.show_password)
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.fields = []
|
||||||
|
fields = list(objects.Plugin.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))
|
||||||
|
|
||||||
|
|
||||||
|
class PluginCollection(collection.Collection):
|
||||||
|
"""API representation of a collection of plugins."""
|
||||||
|
|
||||||
|
plugins = [Plugin]
|
||||||
|
"""A list containing plugins objects"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self._type = 'plugins'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def convert(plugins, limit, url=None, expand=False, **kwargs):
|
||||||
|
collection = PluginCollection()
|
||||||
|
collection.plugins = [
|
||||||
|
Plugin.convert(
|
||||||
|
n, expand) for n in plugins]
|
||||||
|
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||||
|
return collection
|
||||||
|
|
||||||
|
|
||||||
|
class PluginsController(rest.RestController):
|
||||||
|
invalid_sort_key_list = []
|
||||||
|
|
||||||
|
def _get_plugins_collection(self, marker, limit, sort_key, sort_dir,
|
||||||
|
expand=False, resource_url=None):
|
||||||
|
|
||||||
|
limit = api_utils.validate_limit(limit)
|
||||||
|
sort_dir = api_utils.validate_sort_dir(sort_dir)
|
||||||
|
|
||||||
|
marker_obj = None
|
||||||
|
if marker:
|
||||||
|
marker_obj = objects.Plugin.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 = {}
|
||||||
|
plugins = objects.Plugin.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 PluginCollection.convert(plugins, limit,
|
||||||
|
url=resource_url,
|
||||||
|
expand=expand,
|
||||||
|
**parameters)
|
||||||
|
|
||||||
|
@expose.expose(PluginCollection, types.uuid, int, wtypes.text, wtypes.text)
|
||||||
|
def get_all(self, marker=None, limit=None, sort_key='id',
|
||||||
|
sort_dir='asc'):
|
||||||
|
"""Retrieve a list of plugins.
|
||||||
|
|
||||||
|
:param marker: pagination marker for large data sets.
|
||||||
|
:param limit: maximum number of resources to return in a single result.
|
||||||
|
:param sort_key: column to sort results by. Default: id.
|
||||||
|
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||||
|
"""
|
||||||
|
return self._get_plugins_collection(marker,
|
||||||
|
limit, sort_key, sort_dir)
|
||||||
|
|
||||||
|
@expose.expose(Plugin, types.uuid_or_name)
|
||||||
|
def get(self, plugin_ident):
|
||||||
|
"""Retrieve information about the given plugin.
|
||||||
|
|
||||||
|
:param plugin_ident: UUID or logical name of a plugin.
|
||||||
|
"""
|
||||||
|
rpc_plugin = api_utils.get_rpc_plugin(plugin_ident)
|
||||||
|
plugin = Plugin(**rpc_plugin.as_dict())
|
||||||
|
plugin.id = rpc_plugin.id
|
||||||
|
return Plugin.convert(plugin)
|
||||||
|
|
||||||
|
@expose.expose(Plugin, body=Plugin, status_code=201)
|
||||||
|
def post(self, Plugin):
|
||||||
|
"""Create a new Plugin.
|
||||||
|
|
||||||
|
:param Plugin: a Plugin within the request body.
|
||||||
|
"""
|
||||||
|
if not Plugin.name:
|
||||||
|
raise exception.MissingParameterValue(
|
||||||
|
("Name is not specified."))
|
||||||
|
|
||||||
|
if Plugin.name:
|
||||||
|
if not api_utils.is_valid_name(Plugin.name):
|
||||||
|
msg = ("Cannot create plugin with invalid name %(name)s")
|
||||||
|
raise wsme.exc.ClientSideError(msg % {'name': Plugin.name},
|
||||||
|
status_code=400)
|
||||||
|
|
||||||
|
new_Plugin = objects.Plugin(pecan.request.context,
|
||||||
|
**Plugin.as_dict())
|
||||||
|
|
||||||
|
new_Plugin = pecan.request.rpcapi.create_plugin(pecan.request.context,
|
||||||
|
new_Plugin)
|
||||||
|
return Plugin.convert(new_Plugin)
|
||||||
|
|
||||||
|
@expose.expose(None, types.uuid_or_name, status_code=204)
|
||||||
|
def delete(self, plugin_ident):
|
||||||
|
"""Delete a plugin.
|
||||||
|
|
||||||
|
:param plugin_ident: UUID or logical name of a plugin.
|
||||||
|
"""
|
||||||
|
rpc_plugin = api_utils.get_rpc_plugin(plugin_ident)
|
||||||
|
pecan.request.rpcapi.destroy_plugin(pecan.request.context,
|
||||||
|
rpc_plugin.uuid)
|
@ -92,10 +92,34 @@ def get_rpc_node(node_ident):
|
|||||||
|
|
||||||
raise exception.InvalidUuidOrName(name=node_ident)
|
raise exception.InvalidUuidOrName(name=node_ident)
|
||||||
|
|
||||||
# Ensure we raise the same exception as we did for the Juno release
|
|
||||||
raise exception.NodeNotFound(node=node_ident)
|
raise exception.NodeNotFound(node=node_ident)
|
||||||
|
|
||||||
|
|
||||||
|
def get_rpc_plugin(plugin_ident):
|
||||||
|
"""Get the RPC plugin from the plugin uuid or logical name.
|
||||||
|
|
||||||
|
:param plugin_ident: the UUID or logical name of a plugin.
|
||||||
|
|
||||||
|
:returns: The RPC Plugin.
|
||||||
|
:raises: InvalidUuidOrName if the name or uuid provided is not valid.
|
||||||
|
:raises: PluginNotFound if the plugin is not found.
|
||||||
|
"""
|
||||||
|
# Check to see if the plugin_ident is a valid UUID. If it is, treat it
|
||||||
|
# as a UUID.
|
||||||
|
if uuidutils.is_uuid_like(plugin_ident):
|
||||||
|
return objects.Plugin.get_by_uuid(pecan.request.context, plugin_ident)
|
||||||
|
|
||||||
|
# We can refer to plugins by their name, if the client supports it
|
||||||
|
# if allow_plugin_logical_names():
|
||||||
|
# if utils.is_hostname_safe(plugin_ident):
|
||||||
|
else:
|
||||||
|
return objects.Plugin.get_by_name(pecan.request.context, plugin_ident)
|
||||||
|
|
||||||
|
raise exception.InvalidUuidOrName(name=plugin_ident)
|
||||||
|
|
||||||
|
raise exception.PluginNotFound(plugin=plugin_ident)
|
||||||
|
|
||||||
|
|
||||||
def is_valid_node_name(name):
|
def is_valid_node_name(name):
|
||||||
"""Determine if the provided name is a valid node name.
|
"""Determine if the provided name is a valid node name.
|
||||||
|
|
||||||
@ -105,3 +129,14 @@ def is_valid_node_name(name):
|
|||||||
:returns: True if the name is valid, False otherwise.
|
:returns: True if the name is valid, False otherwise.
|
||||||
"""
|
"""
|
||||||
return utils.is_hostname_safe(name) and (not uuidutils.is_uuid_like(name))
|
return utils.is_hostname_safe(name) and (not uuidutils.is_uuid_like(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.
|
||||||
|
|
||||||
|
:param: name: the node name to check.
|
||||||
|
:returns: True if the name is valid, False otherwise.
|
||||||
|
"""
|
||||||
|
return not uuidutils.is_uuid_like(name)
|
||||||
|
@ -578,3 +578,7 @@ class PathNotFound(IotronicException):
|
|||||||
|
|
||||||
class DirectoryNotWritable(IotronicException):
|
class DirectoryNotWritable(IotronicException):
|
||||||
message = _("Directory %(dir)s is not writable.")
|
message = _("Directory %(dir)s is not writable.")
|
||||||
|
|
||||||
|
|
||||||
|
class PluginNotFound(NotFound):
|
||||||
|
message = _("Plugin %(plugin)s could not be found.")
|
||||||
|
@ -120,7 +120,11 @@ class ConductorEndpoint(object):
|
|||||||
prov.conf_clean()
|
prov.conf_clean()
|
||||||
p = prov.get_config()
|
p = prov.get_config()
|
||||||
LOG.debug('sending this conf %s', p)
|
LOG.debug('sending this conf %s', p)
|
||||||
self.execute_on_node(ctx, node_id, 'destroyNode', (p,))
|
try:
|
||||||
|
self.execute_on_node(ctx, node_id, 'destroyNode', (p,))
|
||||||
|
except Exception:
|
||||||
|
LOG.error('cannot execute remote destroynode on %s. '
|
||||||
|
'Maybe it is OFFLINE', node_id)
|
||||||
|
|
||||||
node.destroy()
|
node.destroy()
|
||||||
|
|
||||||
@ -161,3 +165,23 @@ class ConductorEndpoint(object):
|
|||||||
return self.wamp_agent_client.call(ctx, full_topic,
|
return self.wamp_agent_client.call(ctx, full_topic,
|
||||||
wamp_rpc_call=full_wamp_call,
|
wamp_rpc_call=full_wamp_call,
|
||||||
data=wamp_rpc_args)
|
data=wamp_rpc_args)
|
||||||
|
|
||||||
|
def destroy_plugin(self, ctx, plugin_id):
|
||||||
|
LOG.info('Destroying plugin with id %s',
|
||||||
|
plugin_id)
|
||||||
|
plugin = objects.Plugin.get_by_uuid(ctx, plugin_id)
|
||||||
|
plugin.destroy()
|
||||||
|
return
|
||||||
|
|
||||||
|
def update_plugin(self, ctx, plugin_obj):
|
||||||
|
plugin = serializer.deserialize_entity(ctx, plugin_obj)
|
||||||
|
LOG.debug('Updating plugin %s', plugin.name)
|
||||||
|
plugin.save()
|
||||||
|
return serializer.serialize_entity(ctx, plugin)
|
||||||
|
|
||||||
|
def create_plugin(self, ctx, plugin_obj):
|
||||||
|
new_plugin = serializer.deserialize_entity(ctx, plugin_obj)
|
||||||
|
LOG.debug('Creating plugin %s',
|
||||||
|
new_plugin.name)
|
||||||
|
new_plugin.create()
|
||||||
|
return serializer.serialize_entity(ctx, new_plugin)
|
||||||
|
@ -93,10 +93,6 @@ class ConductorAPI(object):
|
|||||||
"""Synchronously, have a conductor update the node's information.
|
"""Synchronously, have a conductor update the node's information.
|
||||||
|
|
||||||
Update the node's information in the database and return a node object.
|
Update the node's information in the database and return a node object.
|
||||||
The conductor will lock the node while it validates the supplied
|
|
||||||
information. If driver_info is passed, it will be validated by
|
|
||||||
the core drivers. If instance_uuid is passed, it will be set or unset
|
|
||||||
only if the node is properly configured.
|
|
||||||
|
|
||||||
Note that power_state should not be passed via this method.
|
Note that power_state should not be passed via this method.
|
||||||
Use change_node_power_state for initiating driver actions.
|
Use change_node_power_state for initiating driver actions.
|
||||||
@ -130,3 +126,45 @@ class ConductorAPI(object):
|
|||||||
return cctxt.call(context, 'execute_on_node', node_uuid=node_uuid,
|
return cctxt.call(context, 'execute_on_node', node_uuid=node_uuid,
|
||||||
wamp_rpc_call=wamp_rpc_call,
|
wamp_rpc_call=wamp_rpc_call,
|
||||||
wamp_rpc_args=wamp_rpc_args)
|
wamp_rpc_args=wamp_rpc_args)
|
||||||
|
|
||||||
|
def create_plugin(self, context, plugin_obj, topic=None):
|
||||||
|
"""Add a plugin on the cloud
|
||||||
|
|
||||||
|
:param context: request context.
|
||||||
|
:param plugin_obj: a changed (but not saved) plugin object.
|
||||||
|
:param topic: RPC topic. Defaults to self.topic.
|
||||||
|
:returns: created plugin object
|
||||||
|
|
||||||
|
"""
|
||||||
|
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||||
|
return cctxt.call(context, 'create_plugin',
|
||||||
|
plugin_obj=plugin_obj)
|
||||||
|
|
||||||
|
def update_plugin(self, context, plugin_obj, topic=None):
|
||||||
|
"""Synchronously, have a conductor update the plugin's information.
|
||||||
|
|
||||||
|
Update the plugin's information in the database and
|
||||||
|
return a plugin object.
|
||||||
|
|
||||||
|
:param context: request context.
|
||||||
|
:param plugin_obj: a changed (but not saved) plugin object.
|
||||||
|
:param topic: RPC topic. Defaults to self.topic.
|
||||||
|
:returns: updated plugin object, including all fields.
|
||||||
|
|
||||||
|
"""
|
||||||
|
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
|
||||||
|
return cctxt.call(context, 'update_plugin', plugin_obj=plugin_obj)
|
||||||
|
|
||||||
|
def destroy_plugin(self, context, plugin_id, topic=None):
|
||||||
|
"""Delete a plugin.
|
||||||
|
|
||||||
|
:param context: request context.
|
||||||
|
:param plugin_id: plugin id or uuid.
|
||||||
|
:raises: PluginLocked if plugin is locked by another conductor.
|
||||||
|
:raises: PluginAssociated if the plugin contains an instance
|
||||||
|
associated with it.
|
||||||
|
:raises: InvalidState if the plugin 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_plugin', plugin_id=plugin_id)
|
||||||
|
@ -204,6 +204,20 @@ class Connection(object):
|
|||||||
:returns: A session.
|
:returns: A session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_session_by_node_uuid(self, filters=None, limit=None, marker=None,
|
||||||
|
sort_key=None, sort_dir=None):
|
||||||
|
"""Return a Wamp session of a Node
|
||||||
|
|
||||||
|
:param filters: Filters to apply. Defaults to None.
|
||||||
|
:param limit: Maximum number of wampagents 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.
|
||||||
|
:param sort_dir: direction in which results should be sorted.
|
||||||
|
(asc, desc)
|
||||||
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def create_location(self, values):
|
def create_location(self, values):
|
||||||
"""Create a new location.
|
"""Create a new location.
|
||||||
@ -290,15 +304,52 @@ class Connection(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_session_by_node_uuid(self, filters=None, limit=None, marker=None,
|
def get_plugin_by_id(self, plugin_id):
|
||||||
sort_key=None, sort_dir=None):
|
"""Return a plugin.
|
||||||
"""Return a Wamp session of a Node
|
|
||||||
|
|
||||||
:param filters: Filters to apply. Defaults to None.
|
:param plugin_id: The id of a plugin.
|
||||||
:param limit: Maximum number of wampagents to return.
|
:returns: A plugin.
|
||||||
: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.
|
@abc.abstractmethod
|
||||||
:param sort_dir: direction in which results should be sorted.
|
def get_plugin_by_uuid(self, plugin_uuid):
|
||||||
(asc, desc)
|
"""Return a plugin.
|
||||||
|
|
||||||
|
:param plugin_uuid: The uuid of a plugin.
|
||||||
|
:returns: A plugin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_plugin_by_name(self, plugin_name):
|
||||||
|
"""Return a plugin.
|
||||||
|
|
||||||
|
:param plugin_name: The logical name of a plugin.
|
||||||
|
:returns: A plugin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def create_plugin(self, values):
|
||||||
|
"""Create a new plugin.
|
||||||
|
|
||||||
|
:param values: A dict containing several items used to identify
|
||||||
|
and track the plugin
|
||||||
|
:returns: A plugin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def destroy_plugin(self, plugin_id):
|
||||||
|
"""Destroy a plugin and all associated interfaces.
|
||||||
|
|
||||||
|
:param plugin_id: The id or uuid of a plugin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def update_plugin(self, plugin_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
|
||||||
"""
|
"""
|
||||||
|
@ -108,21 +108,20 @@ def _paginate_query(model, limit=None, marker=None, sort_key=None,
|
|||||||
return query.all()
|
return query.all()
|
||||||
|
|
||||||
|
|
||||||
def add_location_filter_by_node(query, value):
|
|
||||||
if strutils.is_int_like(value):
|
|
||||||
return query.filter_by(node_id=value)
|
|
||||||
else:
|
|
||||||
query = query.join(models.Node,
|
|
||||||
models.Location.node_id == models.Node.id)
|
|
||||||
return query.filter(models.Node.uuid == value)
|
|
||||||
|
|
||||||
|
|
||||||
class Connection(api.Connection):
|
class Connection(api.Connection):
|
||||||
"""SqlAlchemy connection."""
|
"""SqlAlchemy connection."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _add_location_filter_by_node(self, query, value):
|
||||||
|
if strutils.is_int_like(value):
|
||||||
|
return query.filter_by(node_id=value)
|
||||||
|
else:
|
||||||
|
query = query.join(models.Node,
|
||||||
|
models.Location.node_id == models.Node.id)
|
||||||
|
return query.filter(models.Node.uuid == value)
|
||||||
|
|
||||||
def _add_nodes_filters(self, query, filters):
|
def _add_nodes_filters(self, query, filters):
|
||||||
if filters is None:
|
if filters is None:
|
||||||
filters = []
|
filters = []
|
||||||
@ -135,6 +134,13 @@ class Connection(api.Connection):
|
|||||||
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
def _add_plugins_filters(self, query, filters):
|
||||||
|
if filters is None:
|
||||||
|
filters = []
|
||||||
|
# TBD
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
def _add_wampagents_filters(self, query, filters):
|
def _add_wampagents_filters(self, query, filters):
|
||||||
if filters is None:
|
if filters is None:
|
||||||
filters = []
|
filters = []
|
||||||
@ -145,8 +151,34 @@ class Connection(api.Connection):
|
|||||||
else:
|
else:
|
||||||
query = query.filter(models.WampAgent.online == 0)
|
query = query.filter(models.WampAgent.online == 0)
|
||||||
|
|
||||||
|
if 'no_ragent' in filters:
|
||||||
|
if filters['no_ragent']:
|
||||||
|
query = query.filter(models.WampAgent.ragent == 0)
|
||||||
|
else:
|
||||||
|
query = query.filter(models.WampAgent.ragent == 1)
|
||||||
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
def _do_update_node(self, node_id, values):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.Node, session=session)
|
||||||
|
query = add_identity_filter(query, node_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)
|
||||||
|
|
||||||
|
ref.update(values)
|
||||||
|
return ref
|
||||||
|
|
||||||
|
# NODE api
|
||||||
|
|
||||||
def get_nodeinfo_list(self, columns=None, filters=None, limit=None,
|
def get_nodeinfo_list(self, columns=None, filters=None, limit=None,
|
||||||
marker=None, sort_key=None, sort_dir=None):
|
marker=None, sort_key=None, sort_dir=None):
|
||||||
# list-ify columns default values because it is bad form
|
# list-ify columns default values because it is bad form
|
||||||
@ -182,7 +214,7 @@ class Connection(api.Connection):
|
|||||||
except db_exc.DBDuplicateEntry as exc:
|
except db_exc.DBDuplicateEntry as exc:
|
||||||
if 'code' in exc.columns:
|
if 'code' in exc.columns:
|
||||||
raise exception.DuplicateCode(code=values['code'])
|
raise exception.DuplicateCode(code=values['code'])
|
||||||
raise exception.BoardAlreadyExists(uuid=values['uuid'])
|
raise exception.NodeAlreadyExists(uuid=values['uuid'])
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def get_node_by_id(self, node_id):
|
def get_node_by_id(self, node_id):
|
||||||
@ -230,7 +262,7 @@ class Connection(api.Connection):
|
|||||||
node_id = node_ref['id']
|
node_id = node_ref['id']
|
||||||
|
|
||||||
location_query = model_query(models.Location, session=session)
|
location_query = model_query(models.Location, session=session)
|
||||||
location_query = add_location_filter_by_node(
|
location_query = self._add_location_filter_by_node(
|
||||||
location_query, node_id)
|
location_query, node_id)
|
||||||
location_query.delete()
|
location_query.delete()
|
||||||
|
|
||||||
@ -256,23 +288,7 @@ class Connection(api.Connection):
|
|||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def _do_update_node(self, node_id, values):
|
# CONDUCTOR api
|
||||||
session = get_session()
|
|
||||||
with session.begin():
|
|
||||||
query = model_query(models.Node, session=session)
|
|
||||||
query = add_identity_filter(query, node_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)
|
|
||||||
|
|
||||||
ref.update(values)
|
|
||||||
return ref
|
|
||||||
|
|
||||||
def register_conductor(self, values, update_existing=False):
|
def register_conductor(self, values, update_existing=False):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
@ -323,24 +339,7 @@ class Connection(api.Connection):
|
|||||||
if count == 0:
|
if count == 0:
|
||||||
raise exception.ConductorNotFound(conductor=hostname)
|
raise exception.ConductorNotFound(conductor=hostname)
|
||||||
|
|
||||||
def create_session(self, values):
|
# LOCATION api
|
||||||
session = models.SessionWP()
|
|
||||||
session.update(values)
|
|
||||||
session.save()
|
|
||||||
return session
|
|
||||||
|
|
||||||
def update_session(self, ses_id, values):
|
|
||||||
# NOTE(dtantsur): this can lead to very strange errors
|
|
||||||
session = get_session()
|
|
||||||
try:
|
|
||||||
with session.begin():
|
|
||||||
query = model_query(models.SessionWP, session=session)
|
|
||||||
query = add_identity_filter(query, ses_id)
|
|
||||||
ref = query.one()
|
|
||||||
ref.update(values)
|
|
||||||
except NoResultFound:
|
|
||||||
raise exception.SessionWPNotFound(ses=ses_id)
|
|
||||||
return ref
|
|
||||||
|
|
||||||
def create_location(self, values):
|
def create_location(self, values):
|
||||||
location = models.Location()
|
location = models.Location()
|
||||||
@ -377,6 +376,27 @@ class Connection(api.Connection):
|
|||||||
return _paginate_query(models.Location, limit, marker,
|
return _paginate_query(models.Location, limit, marker,
|
||||||
sort_key, sort_dir, query)
|
sort_key, sort_dir, query)
|
||||||
|
|
||||||
|
# SESSION api
|
||||||
|
|
||||||
|
def create_session(self, values):
|
||||||
|
session = models.SessionWP()
|
||||||
|
session.update(values)
|
||||||
|
session.save()
|
||||||
|
return session
|
||||||
|
|
||||||
|
def update_session(self, ses_id, values):
|
||||||
|
# NOTE(dtantsur): this can lead to very strange errors
|
||||||
|
session = get_session()
|
||||||
|
try:
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.SessionWP, session=session)
|
||||||
|
query = add_identity_filter(query, ses_id)
|
||||||
|
ref = query.one()
|
||||||
|
ref.update(values)
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.SessionWPNotFound(ses=ses_id)
|
||||||
|
return ref
|
||||||
|
|
||||||
def get_session_by_node_uuid(self, node_uuid, valid):
|
def get_session_by_node_uuid(self, node_uuid, valid):
|
||||||
query = model_query(
|
query = model_query(
|
||||||
models.SessionWP).filter_by(
|
models.SessionWP).filter_by(
|
||||||
@ -394,6 +414,8 @@ class Connection(api.Connection):
|
|||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# WAMPAGENT api
|
||||||
|
|
||||||
def register_wampagent(self, values, update_existing=False):
|
def register_wampagent(self, values, update_existing=False):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
with session.begin():
|
with session.begin():
|
||||||
@ -457,3 +479,83 @@ class Connection(api.Connection):
|
|||||||
query = self._add_wampagents_filters(query, filters)
|
query = self._add_wampagents_filters(query, filters)
|
||||||
return _paginate_query(models.WampAgent, limit, marker,
|
return _paginate_query(models.WampAgent, limit, marker,
|
||||||
sort_key, sort_dir, query)
|
sort_key, sort_dir, query)
|
||||||
|
|
||||||
|
# PLUGIN api
|
||||||
|
|
||||||
|
def get_plugin_by_id(self, plugin_id):
|
||||||
|
query = model_query(models.Plugin).filter_by(id=plugin_id)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.PluginNotFound(plugin=plugin_id)
|
||||||
|
|
||||||
|
def get_plugin_by_uuid(self, plugin_uuid):
|
||||||
|
query = model_query(models.Plugin).filter_by(uuid=plugin_uuid)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.PluginNotFound(plugin=plugin_uuid)
|
||||||
|
|
||||||
|
def get_plugin_by_name(self, plugin_name):
|
||||||
|
query = model_query(models.Plugin).filter_by(name=plugin_name)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.PluginNotFound(plugin=plugin_name)
|
||||||
|
|
||||||
|
def destroy_plugin(self, plugin_id):
|
||||||
|
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.Plugin, session=session)
|
||||||
|
query = add_identity_filter(query, plugin_id)
|
||||||
|
try:
|
||||||
|
plugin_ref = query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.PluginNotFound(plugin=plugin_id)
|
||||||
|
|
||||||
|
# Get plugin ID, if an UUID was supplied. The ID is
|
||||||
|
# required for deleting all ports, attached to the plugin.
|
||||||
|
if uuidutils.is_uuid_like(plugin_id):
|
||||||
|
plugin_id = plugin_ref['id']
|
||||||
|
|
||||||
|
query.delete()
|
||||||
|
|
||||||
|
def update_plugin(self, plugin_id, values):
|
||||||
|
# NOTE(dtantsur): this can lead to very strange errors
|
||||||
|
if 'uuid' in values:
|
||||||
|
msg = _("Cannot overwrite UUID for an existing Plugin.")
|
||||||
|
raise exception.InvalidParameterValue(err=msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self._do_update_plugin(plugin_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'])
|
||||||
|
elif 'instance_uuid' in e.columns:
|
||||||
|
raise exception.InstanceAssociated(
|
||||||
|
instance_uuid=values['instance_uuid'],
|
||||||
|
plugin=plugin_id)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def create_plugin(self, values):
|
||||||
|
# ensure defaults are present for new plugins
|
||||||
|
if 'uuid' not in values:
|
||||||
|
values['uuid'] = uuidutils.generate_uuid()
|
||||||
|
plugin = models.Plugin()
|
||||||
|
plugin.update(values)
|
||||||
|
try:
|
||||||
|
plugin.save()
|
||||||
|
except db_exc.DBDuplicateEntry:
|
||||||
|
raise exception.PluginAlreadyExists(uuid=values['uuid'])
|
||||||
|
return plugin
|
||||||
|
|
||||||
|
def get_plugin_list(self, filters=None, limit=None, marker=None,
|
||||||
|
sort_key=None, sort_dir=None):
|
||||||
|
query = model_query(models.Plugin)
|
||||||
|
query = self._add_plugins_filters(query, filters)
|
||||||
|
return _paginate_query(models.Plugin, limit, marker,
|
||||||
|
sort_key, sort_dir, query)
|
||||||
|
@ -190,3 +190,31 @@ class SessionWP(Base):
|
|||||||
session_id = Column(String(15))
|
session_id = Column(String(15))
|
||||||
node_uuid = Column(String(36))
|
node_uuid = Column(String(36))
|
||||||
node_id = Column(Integer, ForeignKey('nodes.id'))
|
node_id = Column(Integer, ForeignKey('nodes.id'))
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(Base):
|
||||||
|
"""Represents a plugin."""
|
||||||
|
|
||||||
|
__tablename__ = 'plugins'
|
||||||
|
__table_args__ = (
|
||||||
|
schema.UniqueConstraint('uuid', name='uniq_plugins0uuid'),
|
||||||
|
table_args())
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
uuid = Column(String(36))
|
||||||
|
name = Column(String(36))
|
||||||
|
config = Column(TEXT)
|
||||||
|
extra = Column(JSONEncodedDict)
|
||||||
|
|
||||||
|
|
||||||
|
class Injected_Plugin(Base):
|
||||||
|
"""Represents an plugin injection on board."""
|
||||||
|
|
||||||
|
__tablename__ = 'injected_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'))
|
||||||
|
status = Column(String(15))
|
||||||
|
@ -15,12 +15,14 @@
|
|||||||
from iotronic.objects import conductor
|
from iotronic.objects import conductor
|
||||||
from iotronic.objects import location
|
from iotronic.objects import location
|
||||||
from iotronic.objects import node
|
from iotronic.objects import node
|
||||||
|
from iotronic.objects import plugin
|
||||||
from iotronic.objects import sessionwp
|
from iotronic.objects import sessionwp
|
||||||
from iotronic.objects import wampagent
|
from iotronic.objects import wampagent
|
||||||
|
|
||||||
Conductor = conductor.Conductor
|
Conductor = conductor.Conductor
|
||||||
Node = node.Node
|
Node = node.Node
|
||||||
Location = location.Location
|
Location = location.Location
|
||||||
|
Plugin = plugin.Plugin
|
||||||
SessionWP = sessionwp.SessionWP
|
SessionWP = sessionwp.SessionWP
|
||||||
WampAgent = wampagent.WampAgent
|
WampAgent = wampagent.WampAgent
|
||||||
|
|
||||||
@ -30,4 +32,5 @@ __all__ = (
|
|||||||
Location,
|
Location,
|
||||||
SessionWP,
|
SessionWP,
|
||||||
WampAgent,
|
WampAgent,
|
||||||
|
Plugin,
|
||||||
)
|
)
|
||||||
|
187
iotronic/objects/plugin.py
Normal file
187
iotronic/objects/plugin.py
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
# 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 oslo_utils import strutils
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from iotronic.common import exception
|
||||||
|
from iotronic.db import api as db_api
|
||||||
|
from iotronic.objects import base
|
||||||
|
from iotronic.objects import utils as obj_utils
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(base.IotronicObject):
|
||||||
|
# Version 1.0: Initial version
|
||||||
|
VERSION = '1.0'
|
||||||
|
|
||||||
|
dbapi = db_api.get_instance()
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
'id': int,
|
||||||
|
'uuid': obj_utils.str_or_none,
|
||||||
|
'name': obj_utils.str_or_none,
|
||||||
|
'config': obj_utils.str_or_none,
|
||||||
|
'extra': obj_utils.dict_or_none,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _from_db_object(plugin, db_plugin):
|
||||||
|
"""Converts a database entity to a formal object."""
|
||||||
|
for field in plugin.fields:
|
||||||
|
plugin[field] = db_plugin[field]
|
||||||
|
plugin.obj_reset_changes()
|
||||||
|
return plugin
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get(cls, context, plugin_id):
|
||||||
|
"""Find a plugin based on its id or uuid and return a Node object.
|
||||||
|
|
||||||
|
:param plugin_id: the id *or* uuid of a plugin.
|
||||||
|
:returns: a :class:`Node` object.
|
||||||
|
"""
|
||||||
|
if strutils.is_int_like(plugin_id):
|
||||||
|
return cls.get_by_id(context, plugin_id)
|
||||||
|
elif uuidutils.is_uuid_like(plugin_id):
|
||||||
|
return cls.get_by_uuid(context, plugin_id)
|
||||||
|
else:
|
||||||
|
raise exception.InvalidIdentity(identity=plugin_id)
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get_by_id(cls, context, plugin_id):
|
||||||
|
"""Find a plugin based on its integer id and return a Node object.
|
||||||
|
|
||||||
|
:param plugin_id: the id of a plugin.
|
||||||
|
:returns: a :class:`Node` object.
|
||||||
|
"""
|
||||||
|
db_plugin = cls.dbapi.get_plugin_by_id(plugin_id)
|
||||||
|
plugin = Plugin._from_db_object(cls(context), db_plugin)
|
||||||
|
return plugin
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get_by_uuid(cls, context, uuid):
|
||||||
|
"""Find a plugin based on uuid and return a Node object.
|
||||||
|
|
||||||
|
:param uuid: the uuid of a plugin.
|
||||||
|
:returns: a :class:`Node` object.
|
||||||
|
"""
|
||||||
|
db_plugin = cls.dbapi.get_plugin_by_uuid(uuid)
|
||||||
|
plugin = Plugin._from_db_object(cls(context), db_plugin)
|
||||||
|
return plugin
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get_by_name(cls, context, name):
|
||||||
|
"""Find a plugin based on name and return a Node object.
|
||||||
|
|
||||||
|
:param name: the logical name of a plugin.
|
||||||
|
:returns: a :class:`Node` object.
|
||||||
|
"""
|
||||||
|
db_plugin = cls.dbapi.get_plugin_by_name(name)
|
||||||
|
plugin = Plugin._from_db_object(cls(context), db_plugin)
|
||||||
|
return plugin
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def list(cls, context, limit=None, marker=None, sort_key=None,
|
||||||
|
sort_dir=None, filters=None):
|
||||||
|
"""Return a list of Plugin 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:`Plugin` object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_plugins = cls.dbapi.get_plugin_list(filters=filters,
|
||||||
|
limit=limit,
|
||||||
|
marker=marker,
|
||||||
|
sort_key=sort_key,
|
||||||
|
sort_dir=sort_dir)
|
||||||
|
return [Plugin._from_db_object(cls(context), obj)
|
||||||
|
for obj in db_plugins]
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def create(self, context=None):
|
||||||
|
"""Create a Plugin 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
|
||||||
|
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.: Plugin(context)
|
||||||
|
|
||||||
|
"""
|
||||||
|
values = self.obj_get_changes()
|
||||||
|
db_plugin = self.dbapi.create_plugin(values)
|
||||||
|
self._from_db_object(self, db_plugin)
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def destroy(self, context=None):
|
||||||
|
"""Delete the Plugin 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.: Plugin(context)
|
||||||
|
"""
|
||||||
|
self.dbapi.destroy_plugin(self.uuid)
|
||||||
|
self.obj_reset_changes()
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def save(self, context=None):
|
||||||
|
"""Save updates to this Plugin.
|
||||||
|
|
||||||
|
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
|
||||||
|
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.: Plugin(context)
|
||||||
|
"""
|
||||||
|
updates = self.obj_get_changes()
|
||||||
|
self.dbapi.update_plugin(self.uuid, updates)
|
||||||
|
self.obj_reset_changes()
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def refresh(self, context=None):
|
||||||
|
"""Refresh the object by re-fetching 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.: Plugin(context)
|
||||||
|
"""
|
||||||
|
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]
|
@ -132,6 +132,57 @@ ENGINE = InnoDB
|
|||||||
AUTO_INCREMENT = 10
|
AUTO_INCREMENT = 10
|
||||||
DEFAULT CHARACTER SET = utf8;
|
DEFAULT CHARACTER SET = utf8;
|
||||||
|
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `iotronic`.`plugins`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
DROP TABLE IF EXISTS `iotronic`.`plugins` ;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `iotronic`.`plugins` (
|
||||||
|
`created_at` DATETIME NULL DEFAULT NULL,
|
||||||
|
`updated_at` DATETIME NULL DEFAULT NULL,
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`uuid` VARCHAR(36) NOT NULL,
|
||||||
|
`name` VARCHAR(255) NULL DEFAULT NULL,
|
||||||
|
`config` TEXT NULL DEFAULT NULL,
|
||||||
|
`extra` TEXT NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE INDEX `uuid` (`uuid` ASC))
|
||||||
|
ENGINE = InnoDB
|
||||||
|
AUTO_INCREMENT = 132
|
||||||
|
DEFAULT CHARACTER SET = utf8;
|
||||||
|
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `iotronic`.`injected_plugins`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
DROP TABLE IF EXISTS `iotronic`.`injected_plugins` ;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `iotronic`.`injected_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,
|
||||||
|
`plugin_uuid` VARCHAR(36) NOT NULL,
|
||||||
|
`plugin_id` INT(11) NOT NULL,
|
||||||
|
`status` VARCHAR(15) NOT NULL DEFAULT 'injected',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `node_id` (`node_id` ASC),
|
||||||
|
CONSTRAINT `node_id`
|
||||||
|
FOREIGN KEY (`node_id`)
|
||||||
|
REFERENCES `iotronic`.`nodes` (`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE,
|
||||||
|
INDEX `plugin_id` (`plugin_id` ASC),
|
||||||
|
CONSTRAINT `plugin_id`
|
||||||
|
FOREIGN KEY (`plugin_id`)
|
||||||
|
REFERENCES `iotronic`.`plugins` (`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE)
|
||||||
|
ENGINE = InnoDB
|
||||||
|
AUTO_INCREMENT = 132
|
||||||
|
DEFAULT CHARACTER SET = utf8;
|
||||||
|
|
||||||
|
|
||||||
SET SQL_MODE=@OLD_SQL_MODE;
|
SET SQL_MODE=@OLD_SQL_MODE;
|
||||||
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
|
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
|
||||||
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
|
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
|
||||||
|
60
utils/iotronic_curl_client
Executable file
60
utils/iotronic_curl_client
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
HOST='localhost'
|
||||||
|
PORT='1288'
|
||||||
|
VERSION='v1'
|
||||||
|
BASE=http://$HOST:$PORT/$VERSION
|
||||||
|
|
||||||
|
function node_manager() {
|
||||||
|
case "$1" in
|
||||||
|
list) curl -sS $BASE/nodes/ | python -m json.tool
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
create) curl -sS -H "Content-Type: application/json" -X POST $BASE/nodes/ \
|
||||||
|
-d '{"type":"'"$7"'","code":"'"$2"'","name":"'"$3"'","location":[{"latitude":"'"$4"'","longitude":"'"$5"'","altitude":"'"$6"'"}]}' | python -m json.tool
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
delete) curl -sS -X DELETE $BASE/nodes/$2 | python -m json.tool
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
show) curl -sS $BASE/nodes/$2 | python -m json.tool
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
*) echo "node list|create|delete|show"
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function plugin_manager() {
|
||||||
|
case "$1" in
|
||||||
|
list) curl -sS $BASE/plugins/ | python -m json.tool
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
create) echo "TBI"
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
delete) echo "TBI"
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
show) curl -sS $BASE/plugins/$2 | python -m json.tool
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
*) echo "plugin list|create|delete|show"
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]
|
||||||
|
then
|
||||||
|
echo "USAGE: iotronic node|plugin [OPTIONS]"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
node) node_manager "${@:2}";
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
plugin) plugin_manager "${@:2}"
|
||||||
|
echo "";
|
||||||
|
;;
|
||||||
|
*) echo "USAGE: iotronic node|plugin [OPTIONS]"
|
||||||
|
esac
|
||||||
|
|
Loading…
Reference in New Issue
Block a user