2013-09-04 15:44:28 -04:00
|
|
|
# concept here that mapping from the resource tree and arguments go to
|
|
|
|
# specific python class signatures. The intent is to require
|
|
|
|
# plugin authors to come here if they *really* think they need new 'commands'
|
|
|
|
# and hopefully curtail deviation by each plugin author
|
|
|
|
|
|
|
|
# have to specify a standard place for cfg selection of *which* plugin
|
|
|
|
# as well a standard to map api requests to python funcitons
|
2014-02-06 13:13:16 -05:00
|
|
|
# e.g. <nodeelement>/power/state maps to some plugin
|
|
|
|
# HardwareManager.get_power/set_power selected by hardwaremanagement.method
|
2013-09-04 15:44:28 -04:00
|
|
|
# plugins can advertise a set of names if there is a desire for readable things
|
|
|
|
# exceptions to handle os images
|
|
|
|
# endpoints point to a class... usually, the class should have:
|
|
|
|
# -create
|
|
|
|
# -retrieve
|
|
|
|
# -update
|
|
|
|
# -delete
|
|
|
|
# functions. Console is special and just get's passed through
|
|
|
|
# see API.txt
|
|
|
|
|
2013-11-02 13:25:56 -04:00
|
|
|
import confluent.interface.console as console
|
2013-11-02 17:32:48 -04:00
|
|
|
import confluent.exceptions as exc
|
2013-11-03 09:52:43 -05:00
|
|
|
import confluent.messages as msg
|
2013-09-04 15:44:28 -04:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
|
|
|
|
pluginmap = {}
|
|
|
|
|
2014-02-06 13:13:16 -05:00
|
|
|
|
2013-11-07 14:39:34 -05:00
|
|
|
def nested_lookup(nestdict, key):
|
|
|
|
return reduce(dict.__getitem__, key, nestdict)
|
2013-09-04 15:44:28 -04:00
|
|
|
|
2014-02-06 13:13:16 -05:00
|
|
|
|
2013-09-04 15:44:28 -04:00
|
|
|
def load_plugins():
|
|
|
|
# To know our plugins directory, we get the parent path of 'bin'
|
2014-02-06 13:13:16 -05:00
|
|
|
path = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
plugintop = os.path.realpath(os.path.join(path, '..', 'plugins'))
|
2013-09-04 15:44:28 -04:00
|
|
|
plugins = set()
|
2013-11-02 17:45:55 -04:00
|
|
|
for plugindir in os.listdir(plugintop):
|
2014-02-06 13:13:16 -05:00
|
|
|
plugindir = os.path.join(plugintop, plugindir)
|
2013-11-02 17:45:55 -04:00
|
|
|
if not os.path.isdir(plugindir):
|
2013-09-04 15:44:28 -04:00
|
|
|
continue
|
2013-11-02 17:45:55 -04:00
|
|
|
sys.path.append(plugindir)
|
|
|
|
#two passes, to avoid adding both py and pyc files
|
|
|
|
for plugin in os.listdir(plugindir):
|
|
|
|
plugin = os.path.splitext(plugin)[0]
|
|
|
|
plugins.add(plugin)
|
|
|
|
for plugin in plugins:
|
|
|
|
if plugin.startswith('.'):
|
|
|
|
continue
|
|
|
|
tmpmod = __import__(plugin)
|
|
|
|
if 'plugin_names' in tmpmod.__dict__:
|
|
|
|
for name in tmpmod.plugin_names:
|
|
|
|
pluginmap[name] = tmpmod
|
|
|
|
else:
|
|
|
|
pluginmap[plugin] = tmpmod
|
2013-09-04 15:44:28 -04:00
|
|
|
|
2013-11-03 13:16:28 -05:00
|
|
|
|
2014-02-06 13:13:16 -05:00
|
|
|
rootcollections = ['node/', 'nodegroup/']
|
|
|
|
|
2013-11-07 14:39:34 -05:00
|
|
|
|
|
|
|
class PluginRoute(object):
|
|
|
|
def __init__(self, routedict):
|
|
|
|
self.routeinfo = routedict
|
|
|
|
# _ prefix indicates internal use (e.g. special console scheme) and should not
|
|
|
|
# be enumerated in any collection
|
|
|
|
noderesources = {
|
|
|
|
'_console': {
|
|
|
|
'session': PluginRoute({
|
2014-02-06 13:13:16 -05:00
|
|
|
'pluginattrs': ['console.method', 'hardwaremanagement.method'],
|
2013-11-07 14:39:34 -05:00
|
|
|
}),
|
|
|
|
},
|
|
|
|
'console': {
|
|
|
|
#this is a dummy value, http or socket must handle special
|
|
|
|
'session': PluginRoute({}),
|
|
|
|
},
|
|
|
|
'power': {
|
|
|
|
'state': PluginRoute({
|
|
|
|
'pluginattrs': ['hardwaremanagement.method'],
|
|
|
|
'default': 'ipmi',
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
'boot': {
|
|
|
|
'device': PluginRoute({
|
|
|
|
'pluginattrs': ['hardwaremanagement.method'],
|
|
|
|
'default': 'ipmi',
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
'attributes': {
|
2014-02-06 13:13:16 -05:00
|
|
|
'all': PluginRoute({'handler': 'attributes'}),
|
|
|
|
'current': PluginRoute({'handler': 'attributes'}),
|
2013-11-07 14:39:34 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2014-02-06 13:13:16 -05:00
|
|
|
|
2013-11-02 13:06:48 -04:00
|
|
|
def stripnode(iterablersp, node):
|
|
|
|
for i in iterablersp:
|
|
|
|
i.strip_node(node)
|
|
|
|
yield i
|
|
|
|
|
2014-02-06 13:13:16 -05:00
|
|
|
|
2013-11-03 14:57:58 -05:00
|
|
|
def iterate_collections(iterable):
|
|
|
|
for coll in iterable:
|
|
|
|
if coll[-1] != '/':
|
|
|
|
coll = coll + '/'
|
2014-01-28 11:18:00 -05:00
|
|
|
yield msg.ChildCollection(coll, candelete=True)
|
2013-11-03 14:57:58 -05:00
|
|
|
|
2014-02-06 13:13:16 -05:00
|
|
|
|
2013-11-07 14:39:34 -05:00
|
|
|
def iterate_resources(fancydict):
|
|
|
|
for resource in fancydict.iterkeys():
|
|
|
|
if resource.startswith("_"):
|
|
|
|
continue
|
|
|
|
if not isinstance(fancydict[resource], PluginRoute): # a resource
|
|
|
|
resource += '/'
|
|
|
|
yield msg.ChildCollection(resource)
|
|
|
|
|
2014-02-06 13:13:16 -05:00
|
|
|
|
2014-02-13 16:57:03 -05:00
|
|
|
def delete_nodegroup_collection(collectionpath, configmanager):
|
|
|
|
if len(collectionpath) == 2: # just the nodegroup
|
|
|
|
group = collectionpath[-1]
|
|
|
|
configmanager.del_groups([group])
|
|
|
|
yield msg.DeletedResource(group)
|
|
|
|
else:
|
|
|
|
raise Exception("Not implemented")
|
|
|
|
|
2014-01-28 11:18:00 -05:00
|
|
|
def delete_node_collection(collectionpath, configmanager):
|
2014-02-06 13:13:16 -05:00
|
|
|
if len(collectionpath) == 2: # just node
|
2014-01-28 11:18:00 -05:00
|
|
|
node = collectionpath[-1]
|
2014-02-13 17:07:05 -05:00
|
|
|
configmanager.del_nodes([node])
|
2014-02-13 16:57:03 -05:00
|
|
|
yield msg.DeletedResource(node)
|
2014-01-28 11:18:00 -05:00
|
|
|
else:
|
|
|
|
raise Exception("Not implemented")
|
|
|
|
|
2014-02-06 13:13:16 -05:00
|
|
|
|
2013-11-07 14:39:34 -05:00
|
|
|
def enumerate_node_collection(collectionpath, configmanager):
|
2014-02-06 13:13:16 -05:00
|
|
|
if collectionpath == ['node']: # it is just '/node/', need a list of nodes
|
2013-11-03 14:57:58 -05:00
|
|
|
return iterate_collections(configmanager.get_nodes())
|
2014-02-09 19:30:46 -05:00
|
|
|
node = collectionpath[1]
|
|
|
|
if not configmanager.is_node(node):
|
|
|
|
raise exc.NotFoundException("Invalid element requested")
|
2013-11-07 14:39:34 -05:00
|
|
|
del collectionpath[0:2]
|
|
|
|
collection = nested_lookup(noderesources, collectionpath)
|
|
|
|
return iterate_resources(collection)
|
2013-11-03 13:16:28 -05:00
|
|
|
|
|
|
|
|
2014-02-14 16:43:00 -05:00
|
|
|
def create_group(inputdata, configmanager):
|
|
|
|
try:
|
|
|
|
groupname = inputdata['name']
|
|
|
|
del inputdata['name']
|
|
|
|
attribmap = {groupname: inputdata}
|
|
|
|
except KeyError:
|
|
|
|
raise exc.InvalidArgumenTException()
|
|
|
|
configmanager.set_group_attributes(attribmap)
|
|
|
|
|
|
|
|
|
2014-01-28 11:18:00 -05:00
|
|
|
def create_node(inputdata, configmanager):
|
|
|
|
try:
|
|
|
|
nodename = inputdata['name']
|
|
|
|
del inputdata['name']
|
2014-02-06 13:13:16 -05:00
|
|
|
attribmap = {nodename: inputdata}
|
2014-01-28 11:18:00 -05:00
|
|
|
except KeyError:
|
|
|
|
raise exc.InvalidArgumentException()
|
|
|
|
configmanager.set_node_attributes(attribmap)
|
|
|
|
|
|
|
|
|
2013-11-03 13:16:28 -05:00
|
|
|
def enumerate_collections(collections):
|
2013-11-11 09:15:17 -05:00
|
|
|
for collection in collections:
|
2013-11-03 13:16:28 -05:00
|
|
|
yield msg.ChildCollection(collection)
|
|
|
|
|
2014-01-28 11:18:00 -05:00
|
|
|
|
2013-11-02 19:45:10 -04:00
|
|
|
def handle_path(path, operation, configmanager, inputdata=None):
|
2013-09-04 15:44:28 -04:00
|
|
|
'''Given a full path request, return an object.
|
|
|
|
|
|
|
|
The plugins should generally return some sort of iterator.
|
|
|
|
An exception is made for console/session, which should return
|
2013-10-12 21:13:22 -04:00
|
|
|
a class with connect(), read(), write(bytes), and close()
|
2013-09-04 15:44:28 -04:00
|
|
|
'''
|
2013-11-07 14:39:34 -05:00
|
|
|
iscollection = False
|
|
|
|
pathcomponents = path.split('/')
|
|
|
|
del pathcomponents[0] # discard the value from leading /
|
|
|
|
if pathcomponents[-1] == '':
|
|
|
|
iscollection = True
|
|
|
|
del pathcomponents[-1]
|
2014-02-06 13:13:16 -05:00
|
|
|
if not pathcomponents: # root collection list
|
2013-11-03 13:16:28 -05:00
|
|
|
return enumerate_collections(rootcollections)
|
2013-11-13 15:12:57 -05:00
|
|
|
elif pathcomponents[0] == 'nodegroup':
|
|
|
|
try:
|
2014-02-13 16:26:52 -05:00
|
|
|
group = pathcomponents[1]
|
2013-11-13 15:12:57 -05:00
|
|
|
except IndexError:
|
2014-02-14 16:43:00 -05:00
|
|
|
if operation == "create":
|
|
|
|
create_group(inputdata, configmanager)
|
2013-11-13 15:12:57 -05:00
|
|
|
return iterate_collections(configmanager.get_groups())
|
2014-02-13 16:57:03 -05:00
|
|
|
if iscollection:
|
|
|
|
if operation == "delete":
|
|
|
|
return delete_nodegroup_collection(pathcomponents, configmanager)
|
|
|
|
else:
|
|
|
|
raise Exception("TODO")
|
2013-11-07 14:39:34 -05:00
|
|
|
elif pathcomponents[0] in ('node', 'system', 'vm'):
|
|
|
|
#single node request of some sort
|
|
|
|
try:
|
|
|
|
node = pathcomponents[1]
|
|
|
|
except IndexError: # doesn't actually have a long enough path
|
2014-01-28 11:18:00 -05:00
|
|
|
# this is enumerating a list of nodes
|
|
|
|
if operation == "delete":
|
|
|
|
raise exc.InvalidArgumentException()
|
|
|
|
if operation == "create":
|
|
|
|
create_node(inputdata, configmanager)
|
2013-11-07 14:39:34 -05:00
|
|
|
return iterate_collections(configmanager.get_nodes())
|
|
|
|
if iscollection:
|
2014-01-28 11:18:00 -05:00
|
|
|
if operation == "delete":
|
|
|
|
return delete_node_collection(pathcomponents, configmanager)
|
|
|
|
elif operation == "retrieve":
|
|
|
|
return enumerate_node_collection(pathcomponents, configmanager)
|
|
|
|
else:
|
|
|
|
raise Exception("TODO here")
|
2013-11-07 14:39:34 -05:00
|
|
|
del pathcomponents[0:2]
|
|
|
|
try:
|
|
|
|
plugroute = nested_lookup(noderesources, pathcomponents).routeinfo
|
|
|
|
except KeyError:
|
2013-11-02 18:24:04 -04:00
|
|
|
raise exc.NotFoundException("Invalid element requested")
|
2014-02-06 13:13:16 -05:00
|
|
|
inputdata = msg.get_input_message(
|
|
|
|
pathcomponents, operation, inputdata, (node,))
|
|
|
|
if 'handler' in plugroute: # fixed handler definition
|
2013-11-02 18:24:04 -04:00
|
|
|
passvalue = pluginmap[plugroute['handler']].__dict__[operation](
|
2013-11-07 14:39:34 -05:00
|
|
|
nodes=(node,), element=pathcomponents,
|
2013-11-02 19:45:10 -04:00
|
|
|
configmanager=configmanager,
|
|
|
|
inputdata=inputdata)
|
2013-11-02 18:24:04 -04:00
|
|
|
elif 'pluginattrs' in plugroute:
|
2013-09-04 15:44:28 -04:00
|
|
|
nodeattr = configmanager.get_node_attributes(
|
|
|
|
[node], plugroute['pluginattrs'])
|
2014-02-06 13:13:16 -05:00
|
|
|
plugpath = None
|
|
|
|
if 'default' in plugroute:
|
|
|
|
plugpath = plugroute['default']
|
2013-09-04 15:44:28 -04:00
|
|
|
for attrname in plugroute['pluginattrs']:
|
2013-09-09 13:42:13 -04:00
|
|
|
if attrname in nodeattr[node]:
|
2014-02-06 13:13:16 -05:00
|
|
|
plugpath = nodeattr[node][attrname]['value']
|
|
|
|
if plugpath is not None:
|
|
|
|
passvalue = pluginmap[plugpath].__dict__[operation](
|
|
|
|
nodes=(node,), element=pathcomponents,
|
|
|
|
configmanager=configmanager,
|
2013-11-02 19:45:10 -04:00
|
|
|
inputdata=inputdata)
|
2013-11-02 13:06:48 -04:00
|
|
|
if isinstance(passvalue, console.Console):
|
|
|
|
return passvalue
|
|
|
|
else:
|
2013-11-02 13:40:53 -04:00
|
|
|
return stripnode(passvalue, node)
|
2013-11-02 13:06:48 -04:00
|
|
|
else:
|
2013-11-02 17:32:48 -04:00
|
|
|
raise exc.NotFoundException()
|