From 52673a990b20da08f3befdbe150129adb3a6ca91 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 26 Oct 2017 11:45:16 -0400 Subject: [PATCH] Confluent server side enclosure reseat This provides ability to request reseat of nodes, and redirects it to the enclosure manager. --- confluent_server/confluent/core.py | 6 +++++ confluent_server/confluent/messages.py | 26 +++++++++++++++++-- .../plugins/hardwaremanagement/enclosure.py | 26 +++++++++++++++++++ .../plugins/hardwaremanagement/ipmi.py | 7 +++++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 confluent_server/confluent/plugins/hardwaremanagement/enclosure.py diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py index 2b523c9b..b5bee1a4 100644 --- a/confluent_server/confluent/core.py +++ b/confluent_server/confluent/core.py @@ -185,6 +185,11 @@ def _init_core(): 'handler': 'ssh', }), }, + '_enclosure': { + 'reseat_bay': PluginRoute( + {'pluginattrs': ['hardwaremanagement.method'], + 'default': 'ipmi'}), + }, 'shell': { # another special case similar to console 'sessions': PluginCollection({ @@ -250,6 +255,7 @@ def _init_core(): 'pluginattrs': ['hardwaremanagement.method'], 'default': 'ipmi', }), + 'reseat': PluginRoute({'handler': 'enclosure'}), }, 'sensors': { 'hardware': { diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index 30ad9c13..fa38a3c9 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -364,6 +364,9 @@ class ChildCollection(LinkRelation): def get_input_message(path, operation, inputdata, nodes=None, multinode=False): if path[0] == 'power' and path[1] == 'state' and operation != 'retrieve': return InputPowerMessage(path, nodes, inputdata) + elif (path in (['power', 'reseat'], ['_enclosure', 'reseat_bay']) and + operation != 'retrieve'): + return InputReseatMessage(path, nodes, inputdata) elif path == ['attributes', 'expression']: return InputExpression(path, inputdata, nodes) elif path[0] in ('attributes', 'users') and operation != 'retrieve': @@ -624,7 +627,7 @@ class ConfluentInputMessage(ConfluentMessage): if self.keyname not in datum: raise exc.InvalidArgumentException( 'missing {0} argument'.format(self.keyname)) - elif datum[self.keyname] not in self.valid_values: + elif not self.is_valid_key(datum[self.keyname]): raise exc.InvalidArgumentException( datum[self.keyname] + ' is not one of ' + ','.join(self.valid_values)) @@ -634,13 +637,15 @@ class ConfluentInputMessage(ConfluentMessage): if self.keyname not in datum: raise exc.InvalidArgumentException( 'missing {0} argument'.format(self.keyname)) - elif datum[self.keyname] not in self.valid_values: + elif not self.is_valid_key(datum[self.keyname]): raise exc.InvalidArgumentException(datum[self.keyname] + ' is not one of ' + ','.join(self.valid_values)) for node in nodes: self.inputbynode[node] = datum[self.keyname] + def is_valid_key(self, key): + return key in self.valid_values class InputIdentifyMessage(ConfluentInputMessage): valid_values = set([ @@ -664,6 +669,16 @@ class InputPowerMessage(ConfluentInputMessage): def powerstate(self, node): return self.inputbynode[node] +class InputReseatMessage(ConfluentInputMessage): + valid_values = set([ + 'reseat', + ]) + + keyname = 'reseat' + + def is_valid_key(self, key): + return key in self.valid_values or isinstance(key, int) + class InputBMCReset(ConfluentInputMessage): valid_values = set([ @@ -875,6 +890,13 @@ class IdentifyState(ConfluentChoiceMessage): keyname = 'identify' +class ReseatResult(ConfluentChoiceMessage): + valid_values = set([ + 'success', + ]) + keyname = 'reseat' + + class PowerState(ConfluentChoiceMessage): valid_values = set([ 'on', diff --git a/confluent_server/confluent/plugins/hardwaremanagement/enclosure.py b/confluent_server/confluent/plugins/hardwaremanagement/enclosure.py new file mode 100644 index 00000000..701333fd --- /dev/null +++ b/confluent_server/confluent/plugins/hardwaremanagement/enclosure.py @@ -0,0 +1,26 @@ +# Copyright 2017 Lenovo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import confluent.core as core + +def update(nodes, element, configmanager, inputdata): + emebs = configmanager.get_node_attributes( + nodes, (u'enclosure.manager', u'enclosure.bay')) + for node in nodes: + em = emebs[node]['enclosure.manager']['value'] + eb = emebs[node]['enclosure.bay']['value'] + for rsp in core.handle_path( + '/nodes/{0}/_enclosure/reseat_bay'.format(em), + 'update', configmanager, + inputdata={'reseat': int(eb)}): + yield rsp diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index e0403587..5fb50ec5 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -432,6 +432,8 @@ class IpmiHandler(object): raise Exception(self.error) if self.element == ['power', 'state']: self.power() + elif self.element == ['_enclosure', 'reseat_bay']: + self.reseat_bay() elif self.element == ['boot', 'nextdevice']: self.bootdevice() elif self.element == ['health', 'hardware']: @@ -820,6 +822,11 @@ class IpmiHandler(object): else: raise exc.InvalidArgumentException('health is read-only') + def reseat_bay(self): + bay = self.inputdata.inputbynode[self.node] + self.ipmicmd.reseat_bay(bay) + self.output.put(msg.ReseatResult(self.node, 'success')) + def bootdevice(self): if 'read' == self.op: bootdev = self.ipmicmd.get_bootdev()