diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py
index f8ff23e2..7bd9850d 100644
--- a/confluent_server/confluent/core.py
+++ b/confluent_server/confluent/core.py
@@ -83,7 +83,7 @@ def load_plugins():
if plugtype == '.sh':
pluginmap[plugin] = shellmodule.Plugin(
os.path.join(plugindir, plugin + '.sh'))
- elif "__init__" not in plugin:
+ elif "__init__" not in plugin:
plugins.add(plugin)
for plugin in plugins:
tmpmod = __import__(plugin)
@@ -126,7 +126,11 @@ noderesources = {
'pluginattrs': ['hardwaremanagement.method'],
'default': 'ipmi',
}),
- }
+ },
+ 'users': PluginCollection({
+ 'pluginattrs': ['hardwaremanagement.method'],
+ 'default': 'ipmi',
+ }),
}
},
'_console': {
diff --git a/confluent_server/confluent/httpapi.py b/confluent_server/confluent/httpapi.py
index 9aea1ea6..0256113c 100644
--- a/confluent_server/confluent/httpapi.py
+++ b/confluent_server/confluent/httpapi.py
@@ -103,9 +103,36 @@ def node_creation_resources():
desc=attribs.node[attr]['description']).html() + '
\n'
+def user_creation_resources():
+ credential = {
+ 'uid': {
+ 'description': (''),
+ },
+ 'username': {
+ 'description': (''),
+ },
+ 'password': {
+ 'description': (''),
+ },
+ 'privilege_level': {
+ 'description': (''),
+ },
+ }
+ for attr in sorted(credential.iterkeys()):
+ if attr == "password":
+ yield confluent.messages.CryptedAttributes(
+ kv={attr: None},
+ desc=credential[attr]['description']).html() + '
\n'
+ else:
+ yield confluent.messages.Attributes(
+ kv={attr: None},
+ desc=credential[attr]['description']).html() + '
\n'
+
+
create_resource_functions = {
- '/nodes/': node_creation_resources,
- '/groups/': group_creation_resources,
+ 'nodes': node_creation_resources,
+ 'groups': group_creation_resources,
+ 'users': user_creation_resources,
}
@@ -444,11 +471,15 @@ def _assemble_html(responses, resource, querydict, url, extension):
if iscollection:
# localpath = url[:-2] (why was this here??)
try:
+ if url == '/users/':
+ return
firstpass = True
- for y in create_resource_functions[url]():
+ module = url.split('/')
+ if not module:
+ return
+ for y in create_resource_functions[module[-2]]():
if firstpass:
- yield "
Define new resource in %s:
" % \
- url.split("/")[-2]
+ yield "
Define new resource in %s:
" % module[-2]
firstpass = False
yield y
yield (''
diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py
index 1483120a..144244fa 100644
--- a/confluent_server/confluent/messages.py
+++ b/confluent_server/confluent/messages.py
@@ -107,7 +107,10 @@ class ConfluentMessage(object):
for key in pairs.iterkeys():
val = pairs[key]
value = self.defaultvalue
- valtype = self.defaulttype
+ if isinstance(val, dict) and 'type' in val:
+ valtype = val['type']
+ else:
+ valtype = self.defaulttype
notes = []
if isinstance(val, list):
@@ -322,6 +325,9 @@ def get_input_message(path, operation, inputdata, nodes=None, multinode=False):
return InputIdentifyMessage(path, nodes, inputdata)
elif path == ['events', 'hardware', 'decode']:
return InputAlertData(path, inputdata, nodes)
+ elif (path[:3] == ['configuration', 'management_controller', 'users'] and
+ operation != 'retrieve'):
+ return InputCredential(path, inputdata, nodes)
elif inputdata:
raise exc.InvalidArgumentException()
@@ -405,6 +411,61 @@ class InputAttributes(ConfluentMessage):
return nodeattr
+class InputCredential(ConfluentMessage):
+ valid_privilege_levels = set([
+ 'callback',
+ 'user',
+ 'operator',
+ 'administrator',
+ 'proprietary',
+ 'no_access',
+ ])
+
+ def __init__(self, path, inputdata, nodes=None):
+ self.credentials = {}
+ nestedmode = False
+ if not inputdata:
+ raise exc.InvalidArgumentException('no request data provided')
+
+ if len(path) == 4:
+ inputdata['uid'] = path[-1]
+
+ if ('uid' not in inputdata or 'privilege_level' not in inputdata
+ or 'username' not in inputdata or 'password' not in inputdata):
+ raise exc.InvalidArgumentException('missing arguments')
+
+ if not inputdata['uid'].isdigit():
+ raise exc.InvalidArgumentException('uid must be a number')
+ else:
+ inputdata['uid'] = int(inputdata['uid'])
+ if inputdata['privilege_level'] not in self.valid_privilege_levels:
+ raise exc.InvalidArgumentException('privilege_level is not one of '
+ + ','.join(self.valid_privilege_levels))
+
+ if nodes is None:
+ raise exc.InvalidArgumentException(
+ 'This only supports per-node input')
+ for node in nodes:
+ self.credentials[node] = inputdata
+
+ def get_attributes(self, node):
+ if node not in self.credentials:
+ return {}
+ credential = self.credentials[node]
+ for attr in credentials:
+ if type(credentials[attr]) in (str, unicode):
+ try:
+ # as above, use format() to see if string follows
+ # expression, store value back in case of escapes
+ tv = credential[attr].format()
+ credential[attr] = tv
+ except (KeyError, IndexError):
+ # an expression string will error if format() done
+ # use that as cue to put it into config as an expr
+ credential[attr] = {'expression': credential[attr]}
+ return credential
+
+
class ConfluentInputMessage(ConfluentMessage):
keyname = 'state'
@@ -480,7 +541,7 @@ class BootDevice(ConfluentChoiceMessage):
valid_paramset = {
'bootmode': valid_bootmodes,
}
-
+
def __init__(self, node, device, bootmode='unspecified'):
if device not in self.valid_values:
@@ -601,6 +662,21 @@ class EventCollection(ConfluentMessage):
self.kvpairs = {name: {'events': eventdata}}
+class User(ConfluentMessage):
+ def __init__(self, uid, username, privilege_level, name=None):
+ self.desc = 'foo'
+ self.stripped = False
+ self.notnode = name is None
+ kvpairs = {'username': {'value': username},
+ 'password': {'value': '', 'type': 'password'},
+ 'privilege_level': {'value': privilege_level}
+ }
+ if self.notnode:
+ self.kvpairs = kvpairs
+ else:
+ self.kvpairs = {name: kvpairs}
+
+
class AlertDestination(ConfluentMessage):
def __init__(self, ip, acknowledge=False, retries=0, name=None):
self.desc = 'foo'
diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
index cdb02153..0c6fb43e 100644
--- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
+++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
@@ -371,6 +371,8 @@ class IpmiHandler(object):
def handle_configuration(self):
if self.element[1:3] == ['management_controller', 'alerts' ]:
return self.handle_alerts()
+ elif self.element[1:3] == ['management_controller', 'users' ]:
+ return self.handle_users()
raise Exception('Not implemented')
def decode_alert(self):
@@ -421,6 +423,39 @@ class IpmiHandler(object):
return
raise Exception('Not implemented')
+ def handle_users(self):
+ if len(self.element) == 3:
+ if self.op == 'update':
+ user = self.inputdata.credentials[self.node]
+ self.ipmicmd.create_user(uid=user['uid'], name=user['username'],
+ password=user['password'], channel=1,
+ callback=True,link_auth=True, ipmi_msg=True,
+ privilege_level=user['privilege_level'])
+ # A list of users
+ for user in self.ipmicmd.get_users(channel=1):
+ self.output.put(msg.ChildCollection(user))
+ return
+ elif len(self.element) == 4:
+ user = int(self.element[-1])
+ if self.op == 'read':
+ data = self.ipmicmd.get_user(uid=user, channel=1)
+ self.output.put(msg.User(
+ uid=data['uid'],
+ username=data['name'],
+ privilege_level=data['access']['privilege_level'],
+ name=self.node))
+ return
+ elif self.op == 'update':
+ user = self.inputdata.credentials[self.node]
+ self.ipmicmd.create_user(uid=user['uid'], name=user['username'],
+ password=user['password'], channel=1,
+ callback=True,link_auth=True, ipmi_msg=True,
+ privilege_level=user['privilege_level'])
+ return
+ elif self.op == 'delete':
+ self.ipmicmd.user_delete(uid=user, channel=1)
+ return
+
def do_eventlog(self):
eventout = []
for event in self.ipmicmd.get_event_log():