From 73cab3774dbf5c0c465cfee17a3cc057a27b9408 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Fri, 26 Oct 2018 14:31:28 -0400 Subject: [PATCH] Add support for volume creation --- confluent_client/bin/confetty | 3 +- confluent_server/confluent/messages.py | 57 ++++++++++++++++++ .../plugins/hardwaremanagement/ipmi.py | 59 +++++++++++++++---- 3 files changed, 107 insertions(+), 12 deletions(-) diff --git a/confluent_client/bin/confetty b/confluent_client/bin/confetty index 6ad1896c..a321a141 100755 --- a/confluent_client/bin/confetty +++ b/confluent_client/bin/confetty @@ -478,7 +478,8 @@ def createresource(args): collection = targpath else: collection, _, resname = targpath.rpartition('/') - keydata['name'] = resname + if 'name' not in keydata: + keydata['name'] = resname makecall(session.create, (collection, keydata)) diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index 6badb1d4..28dbfb34 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -422,6 +422,9 @@ def get_input_message(path, operation, inputdata, nodes=None, multinode=False, elif (path[:3] == ['configuration', 'storage', 'disks'] and operation != 'retrieve'): return InputDisk(path, nodes, inputdata) + elif (path[:3] == ['configuration', 'storage', 'volumes'] and + operation in ('update', 'create')): + return InputVolumes(path, nodes, inputdata) elif 'inventory/firmware/updates/active' in '/'.join(path) and inputdata: return InputFirmwareUpdate(path, nodes, inputdata) elif '/'.join(path).startswith('media/detach'): @@ -713,6 +716,60 @@ class InputDisk(ConfluentInputMessage): keyname = 'state' +class InputVolumes(ConfluentInputMessage): + def __init__(self, path, nodes, inputdata): + self.inputbynode = {} + self.stripped = False + if not inputdata: + raise exc.InvalidArgumentException('missing input data') + if isinstance(inputdata, dict): + volnames = [None] + if len(path) == 6: + volnames = path[-1] + volnames = inputdata.get('name', volnames) + if not isinstance(volnames, list): + volnames = volnames.split(',') + sizes = inputdata.get('size', [None]) + if not isinstance(sizes, list): + sizes = sizes.split(',') + disks = inputdata.get('disks', []) + if not disks: + raise exc.InvalidArgumentException( + 'disks are currently required to create a volume') + raidlvl = inputdata.get('raidlevel', None) + inputdata = [] + for size in sizes: + if volnames: + currname = volnames.pop(0) + else: + currname = None + inputdata.append( + {'name': currname, 'size': size, + 'disks': disks, + 'raidlevel': raidlvl}) + for node in nodes: + self.inputbynode[node] = [] + for input in inputdata: + volname = None + if len(path) == 6: + volname = path[-1] + volname = input.get('name', volname) + if not volname: + volname = None + volsize = input.get('size', None) + if isinstance(input['disks'], list): + disks = input['disks'] + else: + disks = input['disks'].split(',') + raidlvl = input.get('raidlevel', None) + for node in nodes: + self.inputbynode[node].append({'name': volname, + 'size': volsize, + 'disks': disks, + 'raidlevel': raidlvl, + }) + + class InputPowerMessage(ConfluentInputMessage): valid_values = set([ 'on', diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index 27871955..6b6ba079 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -341,7 +341,7 @@ class IpmiConsole(conapi.Console): self.solconnection.send_break() -def perform_requests(operator, nodes, element, cfg, inputdata): +def perform_requests(operator, nodes, element, cfg, inputdata, realop): cryptit = cfg.decrypt cfg.decrypt = True configdata = cfg.get_node_attributes(nodes, _configattributes) @@ -351,7 +351,7 @@ def perform_requests(operator, nodes, element, cfg, inputdata): for node in nodes: livingthreads.add(_ipmiworkers.spawn( perform_request, operator, node, element, configdata, inputdata, - cfg, resultdata)) + cfg, resultdata, realop)) while livingthreads: try: datum = resultdata.get(timeout=10) @@ -377,10 +377,10 @@ def perform_requests(operator, nodes, element, cfg, inputdata): def perform_request(operator, node, element, - configdata, inputdata, cfg, results): + configdata, inputdata, cfg, results, realop): try: return IpmiHandler(operator, node, element, configdata, inputdata, - cfg, results).handle_request() + cfg, results, realop).handle_request() except pygexc.IpmiException as ipmiexc: excmsg = str(ipmiexc) if excmsg in ('Session no longer connected', 'timeout'): @@ -410,7 +410,8 @@ def perform_request(operator, node, element, persistent_ipmicmds = {} class IpmiHandler(object): - def __init__(self, operation, node, element, cfd, inputdata, cfg, output): + def __init__(self, operation, node, element, cfd, inputdata, cfg, output, + realop): self.sensormap = {} self.invmap = {} self.output = output @@ -424,6 +425,7 @@ class IpmiHandler(object): self.node = node self.element = element self.op = operation + self.realop = realop connparams = get_conn_params(node, self.cfg) self.ipmicmd = None self.inputdata = inputdata @@ -939,10 +941,12 @@ class IpmiHandler(object): storelem = self.element[2:] if 'read' == self.op: return self._show_storage(storelem) - elif 'update' == self.op: + elif 'update' == self.realop: return self._update_storage(storelem) elif 'delete' == self.op: return self._delete_storage(storelem) + elif 'create' == self.realop: + return self._create_storage(storelem) def _delete_storage(self, storelem): if len(storelem) < 2 or storelem[0] != 'volumes': @@ -956,7 +960,39 @@ class IpmiHandler(object): if simplify_name(vol.name) == volname: volumes.append(vol) self.ipmicmd.remove_storage_configuration(toremove) + self.output.put(msg.DeletedResource(volname)) + def _create_storage(self, storelem): + if 'volumes' not in storelem: + raise exc.InvalidArgumentException('Can only create volumes') + vols = [] + thedisks = None + currcfg = self.ipmicmd.get_storage_configuration() + disks = [] + vols = [] + vol = self.inputdata.inputbynode[self.node][0] + raidlvl = vol['raidlevel'] + for disk in currcfg.disks: + if simplify_name(disk.name) in vol['disks']: + disks.append(disk) + elif (disk.status == 'Unconfigured Good' and + vol['disks'][0] in ('remainder', 'rest')): + disks.append(disk) + elif vol['disks'][0] == 'all': + disks.append(disk) + for vol in self.inputdata.inputbynode[self.node]: + if thedisks and thedisks != vol['disks']: + raise exc.InvalidArgumentException( + 'Not currently supported to create multiple arrays ' + 'in a single request') + if raidlvl and vol['raidlevel'] != raidlvl: + raise exc.InvalidArgumentException('Cannot mix raid levels in ' + 'a single array') + vols.append(storage.Volume(name=vol['name'], size=vol['size'])) + newcfg = storage.ConfigSpec( + arrays=(storage.Array(raid=raidlvl, disks=disks, volumes=vols),)) + self.ipmicmd.apply_storage_configuration(newcfg) + return self._show_storage(storelem) def _update_storage(self, storelem): if storelem[0] == 'disks': @@ -1315,7 +1351,7 @@ def initthread(): _ipmithread = eventlet.spawn(_ipmi_evtloop) -def create(nodes, element, configmanager, inputdata): +def create(nodes, element, configmanager, inputdata, realop='create'): initthread() if element == ['_console', 'session']: if len(nodes) > 1: @@ -1323,12 +1359,12 @@ def create(nodes, element, configmanager, inputdata): return IpmiConsole(nodes[0], configmanager) else: return perform_requests( - 'update', nodes, element, configmanager, inputdata) + 'update', nodes, element, configmanager, inputdata, realop) def update(nodes, element, configmanager, inputdata): initthread() - return create(nodes, element, configmanager, inputdata) + return create(nodes, element, configmanager, inputdata, 'update') def retrieve(nodes, element, configmanager, inputdata): @@ -1343,7 +1379,8 @@ def retrieve(nodes, element, configmanager, inputdata): return firmwaremanager.list_updates(nodes, configmanager.tenant, element, 'ffdc') else: - return perform_requests('read', nodes, element, configmanager, inputdata) + return perform_requests('read', nodes, element, configmanager, + inputdata, 'read') def delete(nodes, element, configmanager, inputdata): initthread() @@ -1357,5 +1394,5 @@ def delete(nodes, element, configmanager, inputdata): return firmwaremanager.remove_updates(nodes, configmanager.tenant, element, type='ffdc') return perform_requests( - 'delete', nodes, element, configmanager, inputdata) + 'delete', nodes, element, configmanager, inputdata, 'delete')