diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py
index 7d5f2a63..0cb320dc 100644
--- a/confluent_server/confluent/core.py
+++ b/confluent_server/confluent/core.py
@@ -110,6 +110,10 @@ noderesources = {
'default': 'ipmi',
}),
},
+ 'identify': PluginRoute({
+ 'pluginattrs': ['hardwaremanagement.method'],
+ 'default': 'ipmi',
+ }),
'boot': {
'nextdevice': PluginRoute({
'pluginattrs': ['hardwaremanagement.method'],
@@ -166,6 +170,8 @@ def show_user(name, configmanager):
def stripnode(iterablersp, node):
for i in iterablersp:
+ if i is None:
+ raise exc.NotImplementedException("Not Implemented")
i.strip_node(node)
yield i
diff --git a/confluent_server/confluent/exceptions.py b/confluent_server/confluent/exceptions.py
index 7ed2235b..67a39a41 100644
--- a/confluent_server/confluent/exceptions.py
+++ b/confluent_server/confluent/exceptions.py
@@ -36,6 +36,7 @@ class TargetEndpointUnreachable(ConfluentException):
# was unreachable. http code 504
pass
+
class TargetEndpointBadCredentials(ConfluentException):
# target was reachable, but authentication/authorization
# failed
@@ -45,3 +46,9 @@ class TargetEndpointBadCredentials(ConfluentException):
class ForbiddenRequest(ConfluentException):
# The client request is not allowed by authorization engine
pass
+
+
+class NotImplementedException(ConfluentException):
+ # The current configuration/plugin is unable to perform
+ # the requested task. http code 501
+ pass
\ No newline at end of file
diff --git a/confluent_server/confluent/httpapi.py b/confluent_server/confluent/httpapi.py
index 58149237..f826f722 100644
--- a/confluent_server/confluent/httpapi.py
+++ b/confluent_server/confluent/httpapi.py
@@ -390,8 +390,9 @@ def resourcehandler_backend(env, start_response):
except exc.TargetEndpointBadCredentials:
start_response('502 Bad Credentials', headers)
yield '502 - Bad Credentials'
-
-
+ except exc.NotImplementedException:
+ start_response('501 Not Implemented', headers)
+ yield '501 Not Implemented'
def _assemble_html(responses, resource, querydict, url, extension):
yield '
' \
diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py
index 0c9ed55f..25b76bf2 100644
--- a/confluent_server/confluent/messages.py
+++ b/confluent_server/confluent/messages.py
@@ -171,6 +171,13 @@ class ConfluentChoiceMessage(ConfluentMessage):
valid_values = set()
valid_paramset = {}
+ def __init__(self, node, state):
+ self.kvpairs = {
+ node: {
+ self.keyname: {'value': state},
+ }
+ }
+
def html(self, extension=''):
snippet = ""
for key in self.kvpairs.iterkeys():
@@ -247,6 +254,8 @@ def get_input_message(path, operation, inputdata, nodes=None):
return InputAttributes(path, inputdata, nodes)
elif path == ['boot', 'nextdevice'] and operation != 'retrieve':
return InputBootDevice(path, nodes, inputdata)
+ elif path == [ 'identify' ] and operation != 'retrieve':
+ return InputIdentifyMessage(path, nodes, inputdata)
elif inputdata:
raise exc.InvalidArgumentException()
@@ -308,7 +317,49 @@ class InputAttributes(ConfluentMessage):
return nodeattr
-class InputPowerMessage(ConfluentMessage):
+class ConfluentInputMessage(ConfluentMessage):
+ keyname = 'state'
+
+ def __init__(self, path, nodes, inputdata):
+ self.inputbynode = {}
+ if not inputdata:
+ raise exc.InvalidArgumentException('missing input data')
+ if self.keyname not in inputdata:
+ #assume we have nested information
+ for key in nodes:
+ if key not in inputdata:
+ raise exc.InvalidArgumentException(key + ' not in request')
+ datum = inputdata[key]
+ if self.keyname not in datum:
+ raise exc.InvalidArgumentException(
+ 'missing {0} argument'.format(self.keyname))
+ elif datum[self.keyname] not in self.valid_values:
+ raise exc.InvalidArgumentException(
+ datum[self.keyname] + ' is not one of ' +
+ ','.join(self.valid_values))
+ self.inputbynode[key] = datum[self.keyname]
+ else: # we have a state argument not by node
+ datum = inputdata
+ if self.keyname not in datum:
+ raise exc.InvalidArgumentException('missing {0} argument'.format(self.keyname))
+ elif datum[self.keyname] not in self.valid_values:
+ raise exc.InvalidArgumentException(datum[self.keyname] +
+ ' is not one of ' +
+ ','.join(self.valid_values))
+ for node in nodes:
+ self.inputbynode[node] = datum[self.keyname]
+
+
+class InputIdentifyMessage(ConfluentInputMessage):
+ valid_values = set([
+ 'on',
+ 'off',
+ ])
+
+ keyname = 'identify'
+
+
+class InputPowerMessage(ConfluentInputMessage):
valid_values = set([
'on',
'off',
@@ -316,37 +367,8 @@ class InputPowerMessage(ConfluentMessage):
'boot',
])
- def __init__(self, path, nodes, inputdata):
- self.powerbynode = {}
- if not inputdata:
- raise exc.InvalidArgumentException('missing input data')
- if 'state' not in inputdata:
- #assume we have nested information
- for key in nodes:
- if key not in inputdata:
- raise exc.InvalidArgumentException(key + ' not in request')
- datum = inputdata[key]
- if 'state' not in datum:
- raise exc.InvalidArgumentException(
- 'missing state argument')
- elif datum['state'] not in self.valid_values:
- raise exc.InvalidArgumentException(
- datum['state'] + ' is not one of ' +
- ','.join(self.valid_values))
- self.powerbynode[key] = datum['state']
- else: # we have a state argument not by node
- datum = inputdata
- if 'state' not in datum:
- raise exc.InvalidArgumentException('missing state argument')
- elif datum['state'] not in self.valid_values:
- raise exc.InvalidArgumentException(datum['state'] +
- ' is not one of ' +
- ','.join(self.valid_values))
- for node in nodes:
- self.powerbynode[node] = datum['state']
-
def powerstate(self, node):
- return self.powerbynode[node]
+ return self.inputbynode[node]
class BootDevice(ConfluentChoiceMessage):
@@ -428,6 +450,14 @@ class InputBootDevice(BootDevice):
return self.bootmodebynode.get(node, 'unspecified')
+class IdentifyState(ConfluentChoiceMessage):
+ valid_values = set([
+ 'on',
+ 'off',
+ ])
+ keyname = 'identify'
+
+
class PowerState(ConfluentChoiceMessage):
valid_values = set([
'on',
@@ -435,13 +465,9 @@ class PowerState(ConfluentChoiceMessage):
'reset',
'boot',
])
+ keyname = 'state'
+
- def __init__(self, node, state):
- self.kvpairs = {
- node: {
- 'state': {'value': state},
- }
- }
class SensorReadings(ConfluentMessage):
diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
index 9cd7bb4b..5bc6cb76 100644
--- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
+++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
@@ -213,6 +213,8 @@ def perform_request(operator, node, element, configdata, inputdata, cfg):
excmsg = str(ipmiexc)
if excmsg == 'Session no longer connected':
return msg.ConfluentTargetTimeout(node)
+ else:
+ raise
persistent_ipmicmds = {}
@@ -275,6 +277,8 @@ class IpmiHandler(object):
return self.bootdevice()
elif self.element == ['health', 'hardware']:
return self.health()
+ elif self.element == ['identify']:
+ return self.identify()
@staticmethod
def _str_health(health):
@@ -331,6 +335,12 @@ class IpmiHandler(object):
return msg.BootDevice(node=self.node,
device=bootdev['bootdev'])
+ def identify(self):
+ if 'update' == self.op:
+ identifystate = self.inputdata.inputbynode[self.node] == 'on'
+ self.ipmicmd.set_identify(on=identifystate)
+ return msg.IdentifyState(
+ node=self.node, state=self.inputdata.inputbynode[self.node])
def power(self):
if 'read' == self.op:
power = self.ipmicmd.get_power()
diff --git a/confluent_server/confluent/sockapi.py b/confluent_server/confluent/sockapi.py
index 1e8b3795..da857de3 100644
--- a/confluent_server/confluent/sockapi.py
+++ b/confluent_server/confluent/sockapi.py
@@ -112,6 +112,10 @@ def sessionhdl(connection, authname, skipauth=False):
tlvdata.send(connection, {'errorcode': 504,
'error': 'Unreachable Target'})
tlvdata.send(connection, {'_requestdone': 1})
+ except exc.NotImplementedException:
+ tlvdata.send(connection, {'errorcode': 501,
+ 'error': 'Not Implemented'})
+ tlvdata.send(connection, {'_requestdone': 1})
except SystemExit:
sys.exit(0)
except: