From 3e1690c8605235e8959e2f8098db1e70ccb14373 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 4 Sep 2019 16:20:31 -0400 Subject: [PATCH] Add authentication and ipmi configuration to SR635/SR655 Network configuration and the lockout policy remain to be configured. --- confluent_server/confluent/discovery/core.py | 3 +- .../confluent/discovery/handlers/smm.py | 2 +- .../confluent/discovery/handlers/tsm.py | 152 ++++++++++++++++++ .../confluent/discovery/handlers/xcc.py | 2 +- .../confluent/discovery/protocols/slp.py | 2 +- 5 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 confluent_server/confluent/discovery/handlers/tsm.py diff --git a/confluent_server/confluent/discovery/core.py b/confluent_server/confluent/discovery/core.py index d12a2702..202edf21 100644 --- a/confluent_server/confluent/discovery/core.py +++ b/confluent_server/confluent/discovery/core.py @@ -69,6 +69,7 @@ import confluent.discovery.protocols.pxe as pxe import confluent.discovery.protocols.slp as slp import confluent.discovery.handlers.imm as imm import confluent.discovery.handlers.cpstorage as cpstorage +import confluent.discovery.handlers.tsm as tsm import confluent.discovery.handlers.pxe as pxeh import confluent.discovery.handlers.smm as smm import confluent.discovery.handlers.xcc as xcc @@ -103,7 +104,7 @@ nodehandlers = { 'pxe-client': pxeh, 'service:io-device.Lenovo:management-module': None, 'service:thinkagile-storage': cpstorage, - 'service:lenovo-tsm': None, + 'service:lenovo-tsm': tsm, } servicenames = { diff --git a/confluent_server/confluent/discovery/handlers/smm.py b/confluent_server/confluent/discovery/handlers/smm.py index e6e2bdad..30b713d7 100644 --- a/confluent_server/confluent/discovery/handlers/smm.py +++ b/confluent_server/confluent/discovery/handlers/smm.py @@ -14,7 +14,7 @@ import confluent.discovery.handlers.bmc as bmchandler import confluent.exceptions as exc -import pyghmi.util.webclient as webclient +webclient = eventlet.import_patched('pyghmi.util.webclient') import struct import urllib import eventlet.support.greendns diff --git a/confluent_server/confluent/discovery/handlers/tsm.py b/confluent_server/confluent/discovery/handlers/tsm.py new file mode 100644 index 00000000..83f6ff41 --- /dev/null +++ b/confluent_server/confluent/discovery/handlers/tsm.py @@ -0,0 +1,152 @@ +# Copyright 2019 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.discovery.handlers.generic as generic +import confluent.exceptions as exc +import confluent.netutil as netutil +import confluent.util as util +import eventlet.support.greendns +import json +import urllib + +webclient = eventlet.import_patched('pyghmi.util.webclient') + +class NodeHandler(generic.NodeHandler): + DEFAULT_USER = 'USERID' + DEFAULT_PASS = 'PASSW0RD' + + def __init__(self, info, configmanager): + self.trieddefault = None + self.targuser = None + self.curruser = None + self.currpass = None + self.targpass = None + self.nodename = None + self.csrftok = None + self.channel = None + self.atdefault = True + super(NodeHandler, self).__init__(info, configmanager) + + def validate_cert(self, certificate): + # broadly speaking, merely checks consistency moment to moment, + # but if https_cert gets stricter, this check means something + fprint = util.get_fingerprint(self.https_cert) + return util.cert_matches(fprint, certificate) + + def _get_wc(self): + authdata = { # start by trying factory defaults + 'username': self.DEFAULT_USER, + 'password': self.DEFAULT_PASS, + } + if not self.trieddefault: + wc = webclient.SecureHTTPConnection(self.ipaddr, 443, verifycallback=self.validate_cert) + rsp, status = wc.grab_json_response_with_status('/api/session', urllib.urlencode(authdata)) + if status > 400: + self.trieddefault = True + if '555' in rsp: + passchange = { + 'Password': self.targpass, + 'RetypePassword': self.targpass, + 'param': 4, + 'default_password': self.DEFAULT_PASS, + 'username': self.DEFAULT_USER + } + rsp, status = wc.grab_json_response_with_status('/api/reset-pass', urllib.urlencode(passchange)) + authdata['password'] = self.targpass + rsp, status = wc.grab_json_response_with_status('/api/session', urllib.urlencode(authdata)) + self.csrftok = rsp['CSRFToken'] + self.channel = rsp['channel'] + self.curruser = self.DEFAULT_USER + self.currpass = self.targpass + return wc + else: + self.curruser = self.DEFAULT_USER + self.currpass = self.DEFAULT_PASS + self.csrftok = rsp['CSRFToken'] + self.channel = rsp['channel'] + return wc + if self.curruser: + authdata['username'] = self.curruser + authdata['password'] = self.currpass + rsp, status = wc.grab_json_response_with_status('/api/session', urllib.urlencode(authdata)) + if rsp.status != 200: + return None + self.csrftok = rsp['CSRFToken'] + self.channel = rsp['channel'] + return wc + authdata['username'] = self.targuser + authdata['password'] = self.targpass + rsp, status = wc.grab_json_response_with_status('/api/session', urllib.urlencode(authdata)) + if status != 200: + return None + self.curruser = self.targuser + self.currpass = self.targpass + self.csrftok = rsp['CSRFToken'] + self.channel = rsp['channel'] + return wc + + def config(self, nodename): + self.nodename = nodename + creds = self.configmanager.get_node_attributes( + nodename, ['secret.hardwaremanagementuser', + 'secret.hardwaremanagementpassword', + 'hardwaremanagement.manager', 'hardwaremanagement.method', 'console.method'], + True) + cd = creds.get(nodename, {}) + user, passwd, _ = self.get_node_credentials( + nodename, creds, self.DEFAULT_USER, self.DEFAULT_PASS) + self.targuser = user + self.targpass = passwd + wc = self._get_wc() + wc.set_header('X-CSRFTOKEN', self.csrftok) + curruserinfo = {} + curruserinfo = wc.grab_json_response('/api/settings/users') + authchg = curruserinfo[1] + authupdate = False + wc.set_header('Content-Type', 'application/json') + if user != self.curruser: + authupdate = True + if not curruserinfo: + curruserinfo = wc.grab_json_response('/api/settings/users') + authchg = curruserinfo[1] + authchg['name'] = user + if passwd != self.currpass: + authupdate = True + if not curruserinfo: + curruserinfo = wc.grab_json_response('/api/settings/users') + authchg = curruserinfo[1] + authchg['changepassword'] = 0 + authchg['password_size'] = 'bytes_20' + authchg['password'] = passwd + authchg['confirm_password'] = passwd + if authupdate: + rsp, status = wc.grab_json_response_with_status('/api/settings/users/2', authchg, method='PUT') + if (cd.get('hardwaremanagement.method', {}).get('value', 'ipmi') != 'redfish' + or cd.get('console.method', {}).get('value', None) == 'ipmi'): + # IPMI must be enabled per user config + wc.grab_json_response('/api/settings/ipmilanconfig', { + 'ipv4_enable': 1, 'ipv6_enable': 1, + 'uncheckedipv4lanEnable': 0, 'uncheckedipv6lanEnable': 0, + 'checkedipv4lanEnable': 1, 'checkedipv6lanEnable': 1}) + rsp, status = wc.grab_json_response_with_status('/api/session', method='DELETE') + + +if __name__ == '__main__': + import confluent.config.configmanager as cfm + c = cfm.ConfigManager(None) + import sys + info = {'addresses': [[sys.argv[1]]] } + print(repr(info)) + testr = NodeHandler(info, c) + testr.config(sys.argv[2]) diff --git a/confluent_server/confluent/discovery/handlers/xcc.py b/confluent_server/confluent/discovery/handlers/xcc.py index d7809dde..abc7232a 100644 --- a/confluent_server/confluent/discovery/handlers/xcc.py +++ b/confluent_server/confluent/discovery/handlers/xcc.py @@ -23,7 +23,7 @@ import json import os import pyghmi.exceptions as pygexc import eventlet.green.socket as socket -import pyghmi.util.webclient as webclient +webclient = eventlet.import_patched('pyghmi.util.webclient') import struct getaddrinfo = eventlet.support.greendns.getaddrinfo diff --git a/confluent_server/confluent/discovery/protocols/slp.py b/confluent_server/confluent/discovery/protocols/slp.py index f70dcbf9..3c4adfc2 100644 --- a/confluent_server/confluent/discovery/protocols/slp.py +++ b/confluent_server/confluent/discovery/protocols/slp.py @@ -487,7 +487,7 @@ def snoop(handler, protocol=None): peerbymacaddress[mac]['hwaddr'] = mac peerbymacaddress[mac]['protocol'] = protocol if 'service:ipmi' in peerbymacaddress[mac]['services']: - if 'service:ipmi//Athena:623' in peerbymacaddress[mac]['urls']: + if 'service:ipmi//Athena:623' in peerbymacaddress[mac].get('urls', ()): peerbymacaddress[mac]['services'] = ['service:thinkagile-storage'] else: continue