Skip to main content

Pure Python-based IPMI client developed by Hyve Design Solutions.

Project description

Hyve PyIPMI

Pure Python-based IPMI client developed by Hyve Design Solutions.

The original purpose of this package is to provide a pure python-based IPMI client library for developing Python test scripts for the IPMI service. It provides two categories: one is for IPMI raw commands and the other is for PyIPMI commands, which are similar to the famous ipmitool commands like "ipmitool mc info" or "ipmitool sdr list".

By this pure Python library, several console programs are provided for BMC developers' convenience.

Samples are included in the package to show how to write test scripts. The performance of using this pure Python library is significantly faster than using a hybrid method of shell scripts + system calls to ipmitool to develop your test scripts.

Features

  • Supported IPMI channels
    • RMCP
    • RMCP+
    • KCS
    • Message bridging from LAN/KCS to IPMB
  • Console programs
    • pyipmi - a Python program similar to ipmitool
    • pyipmr - a Python program supports "ipmitool raw" and has message bridging capability
    • pyping - an RMCP client
    • pysh - an interactive shell for the PyIPMI commands, with auto completion and up/down keys to show previous commands
  • Auto test interface
    • IPMI raw command support (see samples 1-3 below)
    • PyIPMI command support (see samples 4-6 below)

Installation

The following steps were tested on Ubuntu desktop 16.04 LTS, 18.04 LTS, and 20.04 LTS.

1) Prerequisite

$ sudo apt -y install git python3-pip

2) Download the source

$ git clone https://github.com/hyvedesignsolutions/hyve-pyipmi
$ cd hyve-pyipmi

3) Install the Hyve PyIPMI package and the console programs

$ pip3 install .

Test Hyve PyIPMI

By default, pip3 will install the Hyve PyIPMI package in $HOME/.local/lib/python3.x/site-packages and its console programs in $HOME/.local/bin. Generally speaking, the site-packages directory has been included in sys.path and you need to add the bin directory in PATH by yourself. For example, execute

$ export PATH=~/.local/bin:$PATH

Then, you are able to execute your first PyIPMI command. For example, we execute something very similar to ipmitool as follows:

$ pyipmi -H 192.168.0.169 -I lanplus -U root -P root123 raw 6 1

Note that by default, if you choose to use "-I kcs" that goes through the Linux OpenIPMI driver, you will need root privilege to execute the program.

During the first time when the package is used, it automatically generates a user config file in $HOME/.config/pyipmi/pyipmi.conf with default settings. Then, the options supported will overwrite some of the settings. In the above example, it specifies four options and issues a raw IPMI command.

After the config file is created, you can choose to either use all the settings in the config file or continue to overwrite some of the options as the previous example. The program will automatically record the latest overwritten values in the config file.

$ pyipmi sdr list   # Just use the settings in the config file
$ pyipmi -C 2 user list  # Use RMCP+ Cipher Suite 2
$ pyipmi -U hyve -P hyve456 lan print 1  # User credential hyve/hyve456

Type -h to show all the command options and use "help" command to list the available commands.

$ pyipmi -h
$ pyipmi help

Advanced Usage

The program pyipmr is equal to "pyipmi raw" + additional features. It supports

  • IPMI response LUN other than 0
  • IPMI message bridging from LAN to IPMB
$ pyipmr 6 1   # Get Device ID (NetFn=App, CMD=01h)
$ pyipmr -L 1 6 1  # LUN = 1
$ pyipmr -b 6 -t 0x2c raw 6 1  # Bridge Get Device ID to destination 0x2c via channel 6

Test scripts examples

Sample 1

#!/usr/bin/env python3
import sys
from pyipmi.util import PyTest

# Just overwrite the run_commands() method and you can issue
# IPMI commands like this example
class Sample1(PyTest):
    def __init__(self):
        super(Sample1, self).__init__()

    def run_commands(self, argv=None):
        # issue_raw_cmd(req, lun):
        #     req (list): [NetFn, CMD, Req_Data]
        #     lun (int): default is 0
        print('Get Device ID:')
        rsp = self.intf.issue_raw_cmd([6, 1])
        self.print_rsp(rsp)

        print('\nGet User Name 2:')
        rsp = self.intf.issue_raw_cmd([6, 0x46, 2])
        self.print_rsp(rsp)

        print('\nGet LAN Config 1:')
        rsp = self.intf.issue_raw_cmd([0x0c, 2, 1, 1, 0, 0])
        self.print_rsp(rsp)

if __name__ == '__main__':
    test = Sample1()
    sys.exit(test.run())

Sample 2

#!/usr/bin/env python3
import sys
from pyipmi.util import PyTest
from pyipmi.util.config import PyOpts

# Same as sample1, with simple checks on the response data
# and overwrite some values of the user config
class Sample2(PyTest):
    def __init__(self, opts):
        super(Sample2, self).__init__(opts)

    def run_commands(self, argv=None):
        req = ([[6, 1],                     # Get Device ID
                [6, 0x46, 2],               # Get User Name 2
                [0x0c, 2, 1, 1, 0, 0]])     # Get LAN Config 1

        rsp = ([[0, 0x22, 1, 1, 2, 2, 0x9f, 0x55, 0xda, 0, 1, 0, 0x59, 1, 0, 0],
                [0, 0x72, 0x6f, 0x6f, 0x74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0x11, 0x15]])

        # run the commands
        count_p = 0
        count_f = 0
        for i in range(len(req)):
            rsp_one = self.intf.issue_raw_cmd(req[i])
            if rsp_one == rsp[i]:
                print('test case {0}: PASSED!'.format(i + 1))
                count_p += 1
            else:            
                print('test case {0}: FAILED!'.format(i + 1))
                count_f += 1

        # output the results            
        print('Total {0} cases executed: PASSED {1}, FAILED {2}.'
                .format(count_p + count_f, count_p, count_f))

if __name__ == '__main__':
    pyopts = PyOpts()
    pyopts.add_options()
    opts = pyopts.parse_options('-U hyve -P hyve123')

    test = Sample2(opts)
    sys.exit(test.run())

Sample 3

#!/usr/bin/env python3
import sys
from pyipmi.util import PyTest
from pyipmi.util.config import PyOpts

# Message bridging example
# Support the following configuration
# [PyIPMI] <-- LAN (RMCP/RMCP+) --> [BMC] <-- IPMB --> [ME]
class Sample3(PyTest):
    def __init__(self, opts, chnl, target):
        self.chnl = chnl
        self.target = target

        super(Sample3, self).__init__(opts)

    def bridge_cmd(self, req):
        # issue_bridging_cmd(chnl, target, req, lun):
        #     chnl (int): IPMI channel number to bridge the message
        #     target (int): I2C slave address of the bridging destination
        #     req (list): [NetFn, CMD, Req_Data]
        #     lun (int): default is 0
        return self.intf.issue_bridging_cmd(self.chnl, self.target, req) 

    def run_commands(self, argv=None):       
        print('Get Device ID:')
        rsp = self.bridge_cmd([6, 1])
        self.print_rsp(rsp)

        print('\nGet SEL Time:')
        rsp = self.bridge_cmd([0xa, 0x48])
        self.print_rsp(rsp)

        print('\nGet Event Receiver:')
        rsp = self.bridge_cmd([4, 1])
        self.print_rsp(rsp)

        print('\nGet Intel ME FW Capabilities:')
        rsp = self.bridge_cmd([0x2e, 0xde, 0x57, 1, 0, 0, 0, 0, 0, 2, 0xff, 0])
        self.print_rsp(rsp)

        print('\nGet Intel ME Factory Presets Signature:')
        rsp = self.bridge_cmd([0x2e, 0xe0, 0x57, 1, 0])
        self.print_rsp(rsp)

        print('\nGet Exception Data:')
        rsp = self.bridge_cmd([0x2e, 0xe6, 0x57, 1, 0, 0])
        self.print_rsp(rsp)

        print('\nGet Sensor Reading:')
        sensor_num = (8, 197)
        for num in sensor_num:
            rsp = self.bridge_cmd([4, 0x2d, num])
            self.print_rsp(rsp)

        print('\nGet SEL Entry:')
        req = [0xa, 0x43, 0, 0, 0, 0, 0, 0xff]
        rsp = self.bridge_cmd(req)
        id1, id2 = rsp[1:3]        
        while not (id1 == 0xff and id2 == 0xff):
            req[4], req[5] = id1, id2
            rsp = self.bridge_cmd(req)
            id1, id2 = rsp[1:3]        
            self.print_rsp(rsp)

if __name__ == '__main__':
    pyopts = PyOpts()
    pyopts.add_options()
    opts = pyopts.parse_options('-H 10.19.84.90 -I lanplus -U admin -P admin')

    test = Sample3(opts, 6, 0x2c)
    sys.exit(test.run())

Sample 4

#!/usr/bin/env python3
from pyipmi.cmds import PyCmds

if __name__ == '__main__':
    # This example shows how to use PyIPMI commands in a script
    cmd = PyCmds()
    cmd.exec_command('raw 6 1')
    cmd.exec_command('mc info')
    cmd.exec_command('chassis status')
    cmd.exec_command('power status')
    cmd.exec_command('sdr list')
    cmd.exec_command('sel list')
    cmd.exec_command('sensor list')
    cmd.exec_command('lan print 1')
    cmd.exec_command('user list')

sample 5

#!/usr/bin/env python3
from pyipmi.cmds import PyCmds
from pyipmi.util.config import PyOpts

if __name__ == '__main__':
    # Same as sample4,
    # with overwriting some of the options in the user config
    pyopts = PyOpts()
    pyopts.add_options()
    opts = pyopts.parse_options('-U hyve -P hyve123')

    cmd = PyCmds(opts)
    cmd.exec_command('raw 6 1')
    cmd.exec_command('mc info')
    cmd.exec_command('chassis status')
    cmd.exec_command('power status')
    cmd.exec_command('sdr list')
    cmd.exec_command('sel list')
    cmd.exec_command('sensor list')
    cmd.exec_command('lan print 1')
    cmd.exec_command('user list')

Sample 6

#!/usr/bin/env python3
from pyipmi.cmds import PyCmds, StrEx
from pyipmi.util.config import PyOpts

def find_line(target, src_str):
    ret = '(not found)'
    list1 = src_str.split('\n')    
    for s in list1:
        if s[:len(target)] == target:
            ret = s
            break

    return ret + '\n'

if __name__ == '__main__':
    pyopts = PyOpts()
    pyopts.add_options()
    opts = pyopts.parse_options('-U hyve -P hyve123')

    # Instead of printing out the results to the console, this sample shows
    # how to record them in a string named 'print_str'.
    print_str = StrEx()
    cmd = PyCmds(opts, print_str)

    # The results will be appended in 'print_str' when the next command is called, 
    # just like what you see from the console by running sample4 and sample5.
    # If you don't want this, you need to explictly call 'print_str.reset()'
    # before calling the next cmd.exec_command().

    # In this example, assume we'd like to retrieve specific lines from the outputs
    cmd.exec_command('raw 6 1')
    ret_all = print_str.get_str()
    print_str.reset()

    cmd.exec_command('mc info')
    ret = print_str.get_str()
    ret_all += find_line('Manufacturer ID', ret)
    print_str.reset()

    cmd.exec_command('lan print 1')
    ret = print_str.get_str()
    ret_all += find_line('RMCP+ Cipher Suites', ret)    

    print(ret_all)

Contact

You may like to contact Janny Au, the main developer of this program, if you have any questions or suggestions.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hyve-pyipmi-1.1.2.tar.gz (54.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hyve_pyipmi-1.1.2-py3-none-any.whl (94.8 kB view details)

Uploaded Python 3

File details

Details for the file hyve-pyipmi-1.1.2.tar.gz.

File metadata

  • Download URL: hyve-pyipmi-1.1.2.tar.gz
  • Upload date:
  • Size: 54.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.22.0 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.5

File hashes

Hashes for hyve-pyipmi-1.1.2.tar.gz
Algorithm Hash digest
SHA256 4a78f195d309635e9d6f3ae21b4c1d2f49c939ba656a39572ed41814139c355f
MD5 38083b93336e0652bc1e4bddb0c60d62
BLAKE2b-256 36c1841342b339dc274b29b965176f0715616bb8e6879f4c4aae45b43738fd2a

See more details on using hashes here.

File details

Details for the file hyve_pyipmi-1.1.2-py3-none-any.whl.

File metadata

  • Download URL: hyve_pyipmi-1.1.2-py3-none-any.whl
  • Upload date:
  • Size: 94.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.22.0 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.5

File hashes

Hashes for hyve_pyipmi-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 acda38fb71e96c9fcc621c54f66d48112a15b642538106683c307acf1b61c34d
MD5 410219e17561940d9fe037f9524303bb
BLAKE2b-256 1dbba03db2b8f448956f144ed7fe30a22e4b709a2ccaa1a62ce0368df53c33eb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page