mirror of
https://github.com/xcat2/confluent.git
synced 2025-01-20 22:53:17 +00:00
Begin PDU implementation
This commit is contained in:
parent
0e879dc3de
commit
6229cb23e8
@ -534,6 +534,12 @@ node = {
|
||||
'To support this scenario, the switch should be set up to allow independent operation of member ports123654 (e.g. lacp bypass mode or fallback mode).',
|
||||
'validvalues': ('lacp', 'loadbalance', 'roundrobin', 'activebackup', 'none')
|
||||
},
|
||||
'power.pdu': {
|
||||
'description': 'Specifies the managed PDU associated with a power input on the node'
|
||||
},
|
||||
'power.outlet': {
|
||||
'description': 'Species the outlet identifier on the PDU associoted with a power input on the node'
|
||||
},
|
||||
# 'id.modelnumber': {
|
||||
# 'description': 'The manufacturer dictated model number for the node',
|
||||
# },
|
||||
|
@ -485,7 +485,7 @@ def attribute_is_invalid(attrname, attrval):
|
||||
|
||||
|
||||
def _get_valid_attrname(attrname):
|
||||
if attrname.startswith('net.'):
|
||||
if attrname.startswith('net.') or attrname.startswith('power.'):
|
||||
# For net.* attribtues, split on the dots and put back together
|
||||
# longer term we might want a generic approach, but
|
||||
# right now it's just net. attributes
|
||||
|
@ -360,6 +360,10 @@ def _init_core():
|
||||
{'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi'}),
|
||||
},
|
||||
'_pdu': {
|
||||
'outlets': PluginCollection(
|
||||
{'pluginattrs': ['hardwaremanagement.method']}),
|
||||
},
|
||||
'shell': {
|
||||
# another special case similar to console
|
||||
'sessions': PluginCollection({
|
||||
@ -457,6 +461,7 @@ def _init_core():
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'inlets': PluginCollection({'handler': 'pdu'}),
|
||||
'reseat': PluginRoute({'handler': 'enclosure'}),
|
||||
},
|
||||
'sensors': {
|
||||
|
@ -21,6 +21,10 @@ try:
|
||||
import Cookie
|
||||
except ModuleNotFoundError:
|
||||
import http.cookies as Cookie
|
||||
try:
|
||||
import pywarp
|
||||
except ImportError:
|
||||
pywarp = None
|
||||
import confluent.auth as auth
|
||||
import confluent.config.attributes as attribs
|
||||
import confluent.consoleserver as consoleserver
|
||||
|
103
confluent_server/confluent/plugins/hardwaremanagement/geist.py
Normal file
103
confluent_server/confluent/plugins/hardwaremanagement/geist.py
Normal file
@ -0,0 +1,103 @@
|
||||
# Copyright 2022 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 pyghmi.util.webclient as wc
|
||||
import confluent.util as util
|
||||
import confluent.messages as msg
|
||||
|
||||
|
||||
class GeistClient(object):
|
||||
def __init__(self, pdu, configmanager):
|
||||
self.node = pdu
|
||||
self.configmanager = configmanager
|
||||
self._token = None
|
||||
self._wc = None
|
||||
self.username = None
|
||||
|
||||
@property
|
||||
def token(self):
|
||||
if not self._token:
|
||||
self._token = self.login(self.configmanager)
|
||||
return self._token
|
||||
|
||||
@property
|
||||
def wc(self):
|
||||
if self._wc:
|
||||
return self._wc
|
||||
targcfg = self.configmanager.get_node_attributes(self.node,
|
||||
['hardwaremanagement.manager'],
|
||||
decrypt=True)
|
||||
targcfg = targcfg.get(self.node, {})
|
||||
target = targcfg.get(
|
||||
'hardwaremanagement.manager', {}).get('value', None)
|
||||
if not target:
|
||||
target = self.node
|
||||
cv = util.TLSCertVerifier(
|
||||
self.configmanager, self.node,
|
||||
'pubkeys.tls_hardwaremanager').verify_cert
|
||||
self._wc = wc.SecureHTTPConnection(target, verifycallback=cv)
|
||||
return self._wc
|
||||
|
||||
def login(self, configmanager):
|
||||
credcfg = configmanager.get_node_attributes(self.node,
|
||||
['secret.hardwaremanagementuser',
|
||||
'secret.hardwaremanagementpassword'],
|
||||
decrypt=True)
|
||||
username = credcfg.get(
|
||||
'secret.hardwaremanagementuser', {}).get('value', None)
|
||||
passwd = credcfg.get(
|
||||
'secret.hardwaremanagementpassword', {}).get('value', None)
|
||||
if not username or not passwd:
|
||||
raise Exception('Missing username or password')
|
||||
self.username = username
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/api/auth/{0]'.format(username),
|
||||
{'cmd': 'login', 'data': {'password': passwd}})
|
||||
token = rsp['data']['token']
|
||||
return token
|
||||
|
||||
def logout(self):
|
||||
if self._token:
|
||||
self.wc.grab_json_response('/api/auth/{0}'.format(self.username),
|
||||
{'cmd': 'logout', 'token': self.token})
|
||||
self._token = None
|
||||
|
||||
def get_outlet(self, outlet):
|
||||
rsp = self.wc.grab_json_response('/api/dev')
|
||||
rsp = rsp['data']
|
||||
if len(rsp) != 1:
|
||||
raise Exception('Multiple PDUs not supported per pdu')
|
||||
pduname = list(rsp)[0]
|
||||
outlet = rsp[pduname]['outlet'][str(int(outlet) - 1)]
|
||||
state = outlet['state']
|
||||
return state
|
||||
|
||||
def set_outlet(self, outlet, state):
|
||||
rsp = self.wc.grab_json_response('/api/dev')
|
||||
if len(rsp['data'] != 1):
|
||||
self.logout()
|
||||
raise Exception('Multiple PDUs per endpoint not supported')
|
||||
pdu = list(rsp['data'])[0]
|
||||
outlet = int(outlet) - 1
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/api/dev/{0}/outlet/{1}'.format(pdu, outlet),
|
||||
{'cmd': 'control', 'token': self.token,
|
||||
'data': {'action': state, 'delay': False}})
|
||||
|
||||
|
||||
def retrieve(nodes, element, configmanager, inputdata):
|
||||
for node in nodes:
|
||||
gc = GeistClient(node, configmanager)
|
||||
state = gc.get_outlet(element[-1])
|
||||
yield msg.PowerState(node=node, state=state)
|
93
confluent_server/confluent/plugins/hardwaremanagement/pdu.py
Normal file
93
confluent_server/confluent/plugins/hardwaremanagement/pdu.py
Normal file
@ -0,0 +1,93 @@
|
||||
# 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
|
||||
import confluent.messages as msg
|
||||
import pyghmi.exceptions as pygexc
|
||||
import confluent.exceptions as exc
|
||||
|
||||
def retrieve(nodes, element, configmanager, inputdata):
|
||||
emebs = configmanager.get_node_attributes(
|
||||
nodes, (u'power.*pdu', u'power.*outlet'))
|
||||
if element == ['power', 'inlets']:
|
||||
outletnames = set([])
|
||||
for node in nodes:
|
||||
for attrib in emebs[node]:
|
||||
attrib = attrib.replace('power.', '').rsplit('.', 1)
|
||||
if len(attrib) > 1:
|
||||
outletnames.add('inlet_' + attrib[0])
|
||||
else:
|
||||
outletnames.add('default')
|
||||
if outletnames:
|
||||
outletnames.add('all')
|
||||
for inlet in outletnames:
|
||||
yield msg.ChildCollection(inlet)
|
||||
elif len(element) == 3:
|
||||
inletname = element[-1]
|
||||
outlets = get_outlets(nodes, emebs, inletname)
|
||||
for node in outlets:
|
||||
for pgroup in outlets[node]:
|
||||
pdu = outlets[node][pgroup]['pdu']
|
||||
outlet = outlets[node][pgroup]['outlet']
|
||||
for rsp in core.handle_path(
|
||||
'/nodes/{0}/_pdu/outlets/{1}'.format(pdu, outlet),
|
||||
'retrieve', configmanager):
|
||||
yield msg.KeyValueData({pgroup: rsp.kvpairs['state']['value']}, node)
|
||||
|
||||
def get_outlets(nodes, emebs, inletname):
|
||||
outlets = {}
|
||||
for node in nodes:
|
||||
if node not in outlets:
|
||||
outlets[node] = {}
|
||||
for attrib in emebs[node]:
|
||||
v = emebs[node][attrib].get('value', None)
|
||||
if not v:
|
||||
continue
|
||||
attrib = attrib.replace('power.', '').rsplit('.', 1)
|
||||
if len(attrib) > 1:
|
||||
pgroup = 'inlet_' + attrib[0]
|
||||
else:
|
||||
pgroup = 'default'
|
||||
if inletname == 'all' or pgroup == 'inletname':
|
||||
if pgroup not in outlets[node]:
|
||||
outlets[node][pgroup] = {}
|
||||
outlets[node][pgroup][attrib[-1]] = v
|
||||
return outlets
|
||||
|
||||
|
||||
def update(nodes, element, configmanager, inputdata):
|
||||
emebs = configmanager.get_node_attributes(
|
||||
nodes, (u'power.*pdu', u'power.*outlet'))
|
||||
for node in nodes:
|
||||
for attrib in emebs[node]:
|
||||
print(repr(attrib))
|
||||
try:
|
||||
em = emebs[node]['enclosure.manager']['value']
|
||||
eb = emebs[node]['enclosure.bay']['value']
|
||||
except KeyError:
|
||||
em = node
|
||||
eb = -1
|
||||
if not em:
|
||||
em = node
|
||||
if not eb:
|
||||
eb = -1
|
||||
try:
|
||||
for rsp in core.handle_path(
|
||||
'/nodes/{0}/_enclosure/reseat_bay'.format(em),
|
||||
'update', configmanager,
|
||||
inputdata={'reseat': int(eb)}):
|
||||
yield rsp
|
||||
except pygexc.UnsupportedFunctionality as uf:
|
||||
yield msg.ConfluentNodeError(node, str(uf))
|
||||
except exc.TargetEndpointUnreachable as uf:
|
||||
yield msg.ConfluentNodeError(node, str(uf))
|
Loading…
x
Reference in New Issue
Block a user