mirror of
https://github.com/xcat2/confluent.git
synced 2025-05-31 01:56:40 +00:00
325 lines
11 KiB
Python
325 lines
11 KiB
Python
#Copyright 2013 IBM All rights reserved
|
|
|
|
# This module implements client/server messages emitted from plugins.
|
|
# Things are defined here to 'encourage' developers to coordinate information
|
|
# format. This is also how different data formats are supported
|
|
import confluent.exceptions as exc
|
|
import json
|
|
|
|
|
|
class ConfluentMessage(object):
|
|
defaultvalue = ''
|
|
defaulttype = 'text'
|
|
|
|
def __init__(self):
|
|
raise NotImplementedError("Must be subclassed!")
|
|
|
|
def json(self):
|
|
# This will create the canonical json representation of this message
|
|
jsonsnippet = json.dumps(self.kvpairs, separators=(',', ':'))[1:-1]
|
|
return jsonsnippet
|
|
|
|
def raw(self):
|
|
"""Return pythonic representation of the response.
|
|
|
|
Used by httpapi while assembling data prior to json serialization"""
|
|
return self.kvpairs
|
|
|
|
def strip_node(self, node):
|
|
self.kvpairs = self.kvpairs[node]
|
|
|
|
def html(self):
|
|
#this is used to facilitate the api explorer feature
|
|
snippet = ""
|
|
for key in self.kvpairs.iterkeys():
|
|
val = self.kvpairs[key]
|
|
value = self.defaultvalue
|
|
type = self.defaulttype
|
|
if val is not None and 'value' in val:
|
|
value = val['value']
|
|
elif val is not None and 'expression' in val:
|
|
value = val['expression']
|
|
if value is None:
|
|
value = ''
|
|
if val is not None and value == '' and 'isset' in val and val['isset'] is True:
|
|
# an encrypted value, put some *** to show it is set
|
|
# in the explorer
|
|
value = '********'
|
|
if isinstance(val, list):
|
|
snippet += key + ":"
|
|
if len(val) == 0:
|
|
snippet += ('<input type="{0}" name="{1}" value="" '
|
|
' "title="{2}">'
|
|
).format(type, key, self.desc)
|
|
for v in val:
|
|
snippet += ('<input type="{0}" name="{1}" value="{2}" '
|
|
' "title="{3}">'
|
|
).format(type, key, v, self.desc)
|
|
snippet += (
|
|
'<input type="{0}" name="{1}" value="" title="{2}">'
|
|
'<input type="checkbox" name="restexplorerhonorkey" '
|
|
'value="{1}">').format(type, key, self.desc)
|
|
return snippet
|
|
snippet += (key + ":" +
|
|
'<input type="{0}" name="{1}" value="{2}" '
|
|
'title="{3}"><input type="checkbox" '
|
|
'name="restexplorerhonorkey" value="{1}">'
|
|
).format(type, key, value, self.desc)
|
|
return snippet
|
|
|
|
|
|
class DeletedResource(ConfluentMessage):
|
|
def __init__(self, resource):
|
|
self.kvpairs = {}
|
|
|
|
|
|
class ConfluentChoiceMessage(ConfluentMessage):
|
|
|
|
def html(self):
|
|
snippet = ""
|
|
for key in self.kvpairs.iterkeys():
|
|
val = self.kvpairs[key]
|
|
snippet += key + ':<select name="%s">' % key
|
|
for opt in self.valid_values:
|
|
snippet += opt + ":"
|
|
if opt == val['value']:
|
|
snippet += '<option value="%s" selected>%s</option>' % (
|
|
opt, opt)
|
|
else:
|
|
snippet += '<option value="%s">%s</option>' % (opt, opt)
|
|
snippet += '</select>'
|
|
snippet += '<input type="checkbox" name="restexplorerhonorkey" '
|
|
snippet += 'value="%s">' % (key)
|
|
return snippet
|
|
|
|
|
|
class LinkRelation(ConfluentMessage):
|
|
def json(self):
|
|
"""Provide json_hal style representation of the relation.
|
|
|
|
This currently only makes sense for the socket api.
|
|
"""
|
|
return {self.rel: '{ "href": "%s" }' % self.href}
|
|
|
|
def raw(self):
|
|
"""Provide python structure of the relation.
|
|
|
|
This currently is only sensible to consume from httpapi.
|
|
"""
|
|
return {self.rel: {"href": self.href}}
|
|
|
|
def html(self):
|
|
"""Provide an html representation of the link relation.
|
|
|
|
This is used by the API explorer aspect of httpapi"""
|
|
return '<a href="{0}" rel="{1}">{0}</a>'.format(self.href, self.rel)
|
|
# return '<a href="%s" rel="%s">%s</a><input type="submit"
|
|
# name="restexprerorop" value="delete:%s"' % (self.href, self.rel,
|
|
# self.href, self.href)
|
|
|
|
|
|
class ChildCollection(LinkRelation):
|
|
def __init__(self, collname, candelete=False):
|
|
self.rel = 'item'
|
|
self.href = collname
|
|
self.candelete = candelete
|
|
|
|
def html(self):
|
|
if self.candelete:
|
|
return ('<a href="{0}" rel="{1}">{0}</a> . . . . . . . . . . . . '
|
|
'<button type="submit" name="restexplorerop" '
|
|
'value="delete" formaction="{0}">delete'
|
|
'</button>').format(self.href, self.rel)
|
|
else:
|
|
return '<a href="{0}" rel="{0}">{0}</a>'.format(self.href)
|
|
|
|
|
|
def get_input_message(path, operation, inputdata, nodes=None):
|
|
if path[0] == 'power' and path[1] == 'state' and operation != 'retrieve':
|
|
return InputPowerMessage(path, nodes, inputdata)
|
|
elif path[0] == 'attributes' and operation != 'retrieve':
|
|
return InputAttributes(path, nodes, inputdata)
|
|
elif path == ['boot', 'device'] and operation != 'retrieve':
|
|
return InputBootDevice(path, nodes, inputdata)
|
|
elif inputdata:
|
|
raise exc.InvalidArgumentException()
|
|
|
|
|
|
class InputAttributes(ConfluentMessage):
|
|
|
|
def __init__(self, path, nodes, inputdata):
|
|
self.nodeattribs = {}
|
|
nestedmode = False
|
|
if not inputdata:
|
|
raise exc.InvalidArgumentException
|
|
if nodes is None:
|
|
self.attribs = inputdata
|
|
for attrib in self.attribs:
|
|
if (type(self.attribs[attrib]) == str and
|
|
'{' in self.attribs[attrib]):
|
|
self.attribs[attrib] = {'expression': self.attribs[attrib]}
|
|
return
|
|
for node in nodes:
|
|
if node in inputdata:
|
|
nestedmode = True
|
|
self.nodeattribs[node] = inputdata[node]
|
|
if nestedmode:
|
|
for key in inputdata:
|
|
if key not in nodes:
|
|
raise exc.InvalidArgumentException
|
|
else:
|
|
for node in nodes:
|
|
self.nodeattribs[node] = inputdata
|
|
|
|
def get_attributes(self, node):
|
|
if node not in self.nodeattribs:
|
|
return {}
|
|
nodeattr = self.nodeattribs[node]
|
|
for attr in nodeattr:
|
|
if type(nodeattr[attr]) == str and '{' in nodeattr[attr]:
|
|
nodeattr[attr] = {'expression': nodeattr[attr]}
|
|
return nodeattr
|
|
|
|
|
|
class InputPowerMessage(ConfluentMessage):
|
|
valid_values = set([
|
|
'on',
|
|
'off',
|
|
'reset',
|
|
'boot',
|
|
])
|
|
|
|
def __init__(self, path, nodes, inputdata):
|
|
self.powerbynode = {}
|
|
if not inputdata:
|
|
raise exc.InvalidArgumentException()
|
|
if 'state' not in inputdata:
|
|
#assume we have nested information
|
|
for key in nodes:
|
|
if key not in inputdata:
|
|
raise exc.InvalidArgumentException()
|
|
datum = inputdata[key]
|
|
if ('state' not in datum or
|
|
datum['state'] not in self.valid_values):
|
|
raise exc.InvalidArgumentException()
|
|
self.powerbynode[key] = datum['state']
|
|
else: # we have a state argument not by node
|
|
datum = inputdata
|
|
if ('state' not in datum or
|
|
datum['state'] not in self.valid_values):
|
|
raise exc.InvalidArgumentException()
|
|
for node in nodes:
|
|
self.powerbynode[node] = datum['state']
|
|
|
|
def powerstate(self, node):
|
|
return self.powerbynode[node]
|
|
|
|
|
|
class BootDevice(ConfluentChoiceMessage):
|
|
valid_values = set([
|
|
'network',
|
|
'hd',
|
|
'setup',
|
|
'default',
|
|
'cd',
|
|
])
|
|
|
|
def __init__(self, node, device):
|
|
if device not in self.valid_values:
|
|
raise Exception("Invalid boot device argument passed in:" + device)
|
|
self.kvpairs = {
|
|
node: {
|
|
'device': {'value': device},
|
|
}
|
|
}
|
|
|
|
|
|
class InputBootDevice(BootDevice):
|
|
def __init__(self, path, nodes, inputdata):
|
|
self.bootdevbynode = {}
|
|
if not inputdata:
|
|
raise exc.InvalidArgumentException()
|
|
if 'device' not in inputdata:
|
|
for key in nodes:
|
|
if key not in inputdata:
|
|
raise exc.InvalidArgumentException()
|
|
datum = inputdata[key]
|
|
if ('state' not in datum or
|
|
datum['state'] not in self.valid_values):
|
|
raise exc.InvalidArgumenTException()
|
|
self.bootdevbynode[key] = datum['device']
|
|
else:
|
|
datum = inputdata
|
|
if ('device' not in datum or
|
|
datum['device'] not in self.valid_values):
|
|
raise exc.InvalidArgumentException()
|
|
for node in nodes:
|
|
self.bootdevbynode[node] = datum['device']
|
|
|
|
def bootdevice(self, node):
|
|
return self.bootdevbynode[node]
|
|
|
|
|
|
class PowerState(ConfluentChoiceMessage):
|
|
valid_values = set([
|
|
'on',
|
|
'off',
|
|
'reset',
|
|
'boot',
|
|
])
|
|
|
|
def __init__(self, node, state):
|
|
self.kvpairs = {
|
|
node: {
|
|
'state': {'value': state},
|
|
}
|
|
}
|
|
|
|
|
|
class Attributes(ConfluentMessage):
|
|
def __init__(self, node=None, kv=None, desc=''):
|
|
self.desc = desc
|
|
nkv = {}
|
|
for key in kv.iterkeys():
|
|
if type(kv[key]) == str:
|
|
nkv[key] = {'value': kv[key]}
|
|
else:
|
|
nkv[key] = kv[key]
|
|
if node is None:
|
|
self.kvpairs = nkv
|
|
else:
|
|
self.kvpairs = {
|
|
node: nkv
|
|
}
|
|
|
|
|
|
class ListAttributes(ConfluentMessage):
|
|
def __init__(self, node=None, kv=None, desc=''):
|
|
self.desc = desc
|
|
if node is None:
|
|
self.kvpairs = kv
|
|
else:
|
|
self.kvpairs = {node: kv}
|
|
|
|
|
|
class CryptedAttributes(Attributes):
|
|
defaulttype = 'password'
|
|
|
|
def __init__(self, node=None, kv=None, desc=''):
|
|
# for now, just keep the dictionary keys and discard crypt value
|
|
self.desc = desc
|
|
nkv = {}
|
|
for key in kv.iterkeys():
|
|
nkv[key] = {'isset': False}
|
|
try:
|
|
if kv[key] is not None and kv[key]['cryptvalue'] != '':
|
|
nkv[key] = {'isset': True}
|
|
except KeyError:
|
|
pass
|
|
if node is None:
|
|
self.kvpairs = nkv
|
|
else:
|
|
self.kvpairs = {
|
|
node: nkv
|
|
}
|