Last active
January 3, 2024 16:35
-
-
Save czechnology/fb989fc0ee740c7b9945a9620d60ab6b to your computer and use it in GitHub Desktop.
Simple tool to parse the BMC web console of a Nokia Airframe rack server and retrieve the MAC addresses of all available interfaces. Output as ASCII table or tab-separated values (for simple copying to a spreadsheet).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Simple tool to parse the BMC web console of a Nokia Airframe rack server and | |
retrieve the MAC addresses of all available interfaces. Output as ASCII table | |
or tab-separated values (for simple copying to a spreadsheet). | |
Example usage: | |
./bmc.py -u admin -p admin 10.11.12.101 10.11.12.102 10.11.12.103 | |
Tested with BMC Web UI version 1.00.0, KVM Remote Console Utility Version | |
1.70.0. Modifications may be required for other versions of the console. | |
Requires: Requests lib (http://docs.python-requests.org) | |
Author: Martin Kulhavy | |
License: GPL | |
""" | |
import argparse | |
import ast | |
import json | |
import re | |
import sys | |
import requests | |
import urllib3 | |
from requests import Session | |
__author__ = "Martin Kulhavy" | |
class BmcWebClient: | |
def __init__(self, host, user, passwd): | |
self.host = host | |
self.user = user | |
self.passwd = passwd | |
self.session = None | |
self.session_cookie = None | |
def login(self): | |
if not self.session: | |
self.session = Session() | |
url = "https://%s/rpc/WEBSES/create.asp" % self.host | |
data = {"WEBVAR_USERNAME": self.user, "WEBVAR_PASSWORD": self.passwd} | |
resp = self.session.post(url, data, verify=False) | |
dynamic_data_match = re.search( | |
'//Dynamic Data Begin\s*' | |
'WEBVAR_JSONVAR_WEB_SESSION =\s*' | |
'(.+?)\s*' | |
'(//Dynamic data end\s*)?$', | |
resp.text, re.DOTALL) | |
if not dynamic_data_match: | |
raise Exception("Unexpected response: \n%s" % resp.text) | |
dynamic_data = dynamic_data_match.group(1) | |
# The data is not a valid JSON so instead of parsing, | |
# let's just use regex | |
m = re.search("'SESSION_COOKIE'\s*:\s*'([^']+)'", dynamic_data) | |
if not m: | |
raise Exception("No session cookie received") | |
self.session_cookie = m.group(1) | |
m = re.search("'BMC_IP_ADDR'\s*:\s*'([.0-9]+)'", dynamic_data) | |
if not m or m.group(1) != self.host: | |
raise Exception("Wrong BMC host") | |
return self.session_cookie | |
def _session_get(self, path): | |
url = 'https://' + self.host | |
url += path if path.startswith('/') else '/' + path | |
cookies = {'SessionCookie': self.session_cookie, 'Username': self.user} | |
return self.session.get(url, cookies=cookies, verify=False) | |
def get_system_mac(self): | |
if not self.session: | |
self.login() | |
path = "/rpc/getsystemmac.asp" | |
resp = self._session_get(path) | |
dynamic_data_match = re.search( | |
'//Dynamic Data Begin\s*' | |
'WEBVAR_JSONVAR_HL_GETSYSTEMMACINFO =\s*{\s*' | |
'WEBVAR_STRUCTNAME_HL_GETSYSTEMMACINFO\s*:\s*' | |
'(.+?)\s*' | |
',\s*HAPI_STATUS\s*:\s*0\s*};', | |
resp.text, re.DOTALL) | |
if not dynamic_data_match: | |
raise Exception("Unexpected response: \n%s" % resp.text) | |
dynamic_data = dynamic_data_match.group(1) | |
system_mac_info = json.loads(dynamic_data.replace("'", '"')) | |
interfaces = [] | |
for intf in system_mac_info: | |
if not intf: | |
continue | |
name_match = re.match('^(.+)\s*:\s*[:0-9a-f]{17}', | |
intf['MAC_ADDRESS_STR'], re.IGNORECASE) | |
if name_match: | |
name = name_match.group(1).strip() | |
else: | |
name = intf['MAC_ADDRESS_STR'] | |
interfaces.append({'name': name, 'mac': intf['MAC_ADDRESS']}) | |
return interfaces | |
def main(hosts, user, passwd, output_format='ascii'): | |
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) | |
if output_format == 'ascii': | |
line_format = '| {0:<15} | {1:<17} | {2:30} |' | |
elif output_format == 'tsv': | |
line_format = '{0}\t{1}\t{2}' | |
else: | |
raise ValueError('Unknown format') | |
# Print values | |
if output_format == 'ascii': | |
table_separator = '+{0:-<17}+{0:-<19}+{0:-<32}+'.format('') | |
print(table_separator) | |
print(line_format.format( | |
'BMC host/IP', 'MAC address', 'Name (if available)')) | |
print(table_separator) | |
for host in hosts: | |
client = BmcWebClient(host, user, passwd) | |
macs = client.get_system_mac() | |
for mac in macs: | |
print(line_format.format(host, mac['mac'], mac['name'])) | |
if output_format == 'ascii': | |
print(table_separator) | |
if __name__ == "__main__": | |
desc = ("Simple tool to parse the BMC web console of a Nokia Airframe " | |
"rack server and retrieve the MAC addresses of all available " | |
"interfaces. Output as ASCII table or tab-separated values" | |
"(for simple copying to a spreadsheet).\n" | |
"Example usage: ./bmc.py -u admin -p admin " | |
"10.11.12.101 10.11.12.102 10.11.12.103") | |
epi = "Author: Martin Kulhavy, 2017" | |
parser = argparse.ArgumentParser(description=desc, epilog=epi) | |
parser.add_argument('-u', '--user', default='admin', | |
help="Username to log in with to the BMC console") | |
parser.add_argument('-p', '--passwd', default='admin', | |
help="Password to log in with to the BMC console") | |
parser.add_argument('-f', '--format', default='ascii', | |
choices=['ascii', 'tsv'], | |
help="Output format (ASCII table, tab-separated values") | |
parser.add_argument('host', nargs='+', | |
help="One or more hosts to connect to") | |
parsed = parser.parse_args(sys.argv[1:]) | |
main(hosts=parsed.host, user=parsed.user, passwd=parsed.passwd, | |
output_format=parsed.format) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment