rubyipmi 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/README.md +303 -333
- data/VERSION +1 -1
- data/lib/rubyipmi/commands/basecommand.rb +1 -1
- data/lib/rubyipmi/freeipmi/commands/basecommand.rb +2 -2
- data/lib/rubyipmi/ipmitool/commands/basecommand.rb +4 -4
- data/lib/rubyipmi/version.rb +1 -1
- data/lib/rubyipmi.rb +1 -1
- data/rubyipmi.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3c77f389e3e5f24be0a5176bd3a02c510e0d4ade7be804a955ae0e6237e64f9d
|
|
4
|
+
data.tar.gz: 32771f70ef680b8d9a1cd0dd08a815abff60bd0c3a64d5971a0b858086a50ff5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 72fb59a9cd489faa8aec2218ceca97b7bb6e012989253838b0bf766da478d5c1ebdf2a5689d7038f6f56810aca7ec45f52358b55a0c5e9b77a2a23847c651949
|
|
7
|
+
data.tar.gz: a530de8260fcd287b22be9c31934d4ae7457fe19e97368ff3d02f2c14029fb104ecbbbda018ea818de23b80a7d4f9f87963632da902fd22a71532ffd8be8c23e
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [Unreleased]
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.13.0]
|
|
11
|
+
- Drop support for testing ruby 2.x #66
|
|
12
|
+
- Adds experimental testing support for ruby 4.0
|
|
13
|
+
- Fixes: CVE-2026-0980 @evgeni
|
|
14
|
+
|
|
15
|
+
## [0.12.1] - 2026
|
|
9
16
|
|
|
10
17
|
### Fixed
|
|
11
18
|
- Don't re-raise the original exception in basecommand
|
data/README.md
CHANGED
|
@@ -1,464 +1,434 @@
|
|
|
1
|
-
Table of Contents
|
|
2
|
-
=================
|
|
3
|
-
|
|
4
|
-
* [Rubyipmi](#rubyipmi)
|
|
5
|
-
* [Projects that use Rubyipmi](#projects-that-use-rubyipmi)
|
|
6
|
-
* [Support](#support)
|
|
7
|
-
* [Using the library in your code](#using-the-library-in-your-code)
|
|
8
|
-
* [Requirements](#requirements)
|
|
9
|
-
* [Create a connection object](#create-a-connection-object)
|
|
10
|
-
* [Use power functions (not all listed)](#use-power-functions-not-all-listed)
|
|
11
|
-
* [Boot to specific device](#boot-to-specific-device)
|
|
12
|
-
* [Sensors](#sensors)
|
|
13
|
-
* [Fru](#fru)
|
|
14
|
-
* [Testing](#testing)
|
|
15
|
-
* [Security](#security)
|
|
16
|
-
* [How the library works](#how-the-library-works)
|
|
17
|
-
* [Creating a new command](#creating-a-new-command)
|
|
18
|
-
* [Writing a function for running a command](#writing-a-function-for-running-a-command)
|
|
19
|
-
* [Running the cmd](#running-the-cmd)
|
|
20
|
-
* [The Options hash](#the-options-hash)
|
|
21
|
-
* [How to get the results of the command](#how-to-get-the-results-of-the-command)
|
|
22
|
-
* [The command function](#the-command-function)
|
|
23
|
-
* [The following are tools bundled with freeipmi](#the-following-are-tools-bundled-with-freeipmi)
|
|
24
|
-
* [To contrast ipmitool has one command with many options](#to-contrast-ipmitool-has-one-command-with-many-options)
|
|
25
|
-
* [Auto Detect workarounds](#auto-detect-workarounds)
|
|
26
|
-
* [Troubleshooting](#troubleshooting)
|
|
27
|
-
* [Log files](#log-files)
|
|
28
|
-
* [Diagnostics Function](#diagnostics-function)
|
|
29
|
-
* [Test Function](#test-function)
|
|
30
|
-
* [Contributing to rubyipmi](#contributing-to-rubyipmi)
|
|
31
|
-
* [Copyright](#copyright)
|
|
32
|
-
* [Freeipmi Documented Workarounds](#freeipmi-documented-workarounds)
|
|
33
|
-
|
|
34
1
|
# Rubyipmi
|
|
35
|
-
This gem is a ruby wrapper for the freeipmi and ipmitool command line tools.
|
|
36
|
-
It provides a ruby implementation of ipmi commands that will make it simple to connect to BMC devices from ruby.
|
|
37
2
|
|
|
38
|
-
|
|
39
|
-
[](http://badge.fury.io/rb/rubyipmi)
|
|
40
|
-
[](https://coveralls.io/r/logicminds/rubyipmi)
|
|
3
|
+
A Ruby library for controlling and querying BMC (Baseboard Management Controller) devices. Rubyipmi wraps the **freeipmi** and **ipmitool** command-line tools behind a consistent, object-oriented API so you can drive IPMI from Ruby scripts, monitoring tools, or automation without shelling out or parsing CLI output yourself.
|
|
41
4
|
|
|
42
|
-
|
|
43
|
-
and if any IPMI hacks/workarounds were required I wanted to build those into the library to make life easier.
|
|
5
|
+
---
|
|
44
6
|
|
|
45
|
-
##
|
|
46
|
-
* https://github.com/sensu-plugins/sensu-plugins-ipmi
|
|
47
|
-
* https://github.com/theforeman/smart-proxy (Turns Rubyipmi into a Remote Web API Proxy server)
|
|
48
|
-
* https://github.com/logicminds/ipmispec (just started)
|
|
7
|
+
## Table of Contents
|
|
49
8
|
|
|
50
|
-
|
|
9
|
+
- [Requirements](#requirements)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Usage Scenarios](#usage-scenarios)
|
|
13
|
+
- [Connection options](#connection-options)
|
|
14
|
+
- [Power control](#power-control)
|
|
15
|
+
- [Boot device and PXE](#boot-device-and-pxe)
|
|
16
|
+
- [Sensors and monitoring](#sensors-and-monitoring)
|
|
17
|
+
- [FRU (Field Replaceable Unit) info](#fru-field-replaceable-unit-info)
|
|
18
|
+
- [BMC info and diagnostics](#bmc-info-and-diagnostics)
|
|
19
|
+
- [Development](#development)
|
|
20
|
+
- [Running tests](#running-tests)
|
|
21
|
+
- [Extending the library](#extending-the-library)
|
|
22
|
+
- [Troubleshooting](#troubleshooting)
|
|
23
|
+
- [Security](#security)
|
|
24
|
+
- [Projects using Rubyipmi](#projects-using-rubyipmi)
|
|
25
|
+
- [Support](#support)
|
|
26
|
+
- [Contributing](#contributing)
|
|
27
|
+
- [License](#license)
|
|
28
|
+
- [FreeIPMI workarounds](#freeipmi-documented-workarounds)
|
|
51
29
|
|
|
52
|
-
|
|
53
|
-
General support is offered via github issues and whenever I have time I will try to resolve any issues. I do offer
|
|
54
|
-
professional paid support through my [company](http://www.logicminds.biz) and can be contracted to work directly with your organization
|
|
55
|
-
on Rubyipmi or other related automation/devops projects that you might have.
|
|
30
|
+
---
|
|
56
31
|
|
|
57
|
-
|
|
58
|
-
generic in nature there may be newer devices that do not work correctly and its an issue I cannot troubleshoot directly. If you would
|
|
59
|
-
like to see newer/other devices be part of my test suite and you have extra servers lying around. I would encourage you to donate
|
|
60
|
-
server equipment so that I can extend my test suite against this newer equipment.
|
|
32
|
+
## Requirements
|
|
61
33
|
|
|
62
|
-
|
|
34
|
+
- **Ruby** 3.0+
|
|
35
|
+
- One of:
|
|
36
|
+
- [freeipmi](https://www.gnu.org/software/freeipmi/) (from source or package), or
|
|
37
|
+
- **ipmitool**
|
|
63
38
|
|
|
64
|
-
|
|
65
|
-
* IBM
|
|
66
|
-
* HP server with ilo3+
|
|
67
|
-
* Super Micro
|
|
68
|
-
* Cisco
|
|
39
|
+
Rubyipmi auto-detects which provider is installed. You can also force a specific provider (see [Connection options](#connection-options)).
|
|
69
40
|
|
|
70
|
-
|
|
71
|
-
from the standard. In general this library should work will all servers.
|
|
41
|
+
---
|
|
72
42
|
|
|
73
|
-
##
|
|
43
|
+
## Installation
|
|
74
44
|
|
|
75
|
-
|
|
45
|
+
Add to your Gemfile:
|
|
76
46
|
|
|
47
|
+
```ruby
|
|
48
|
+
gem 'rubyipmi'
|
|
49
|
+
```
|
|
77
50
|
|
|
78
|
-
|
|
79
|
-
2. `gem install rubyipmi`
|
|
51
|
+
Then:
|
|
80
52
|
|
|
81
|
-
|
|
53
|
+
```bash
|
|
54
|
+
bundle install
|
|
55
|
+
```
|
|
82
56
|
|
|
83
|
-
|
|
84
|
-
require 'rubyipmi'
|
|
85
|
-
conn = Rubyipmi.connect("username", "password", "hostname", "providertype")
|
|
86
|
-
```
|
|
57
|
+
Or install globally:
|
|
87
58
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this driver to
automatically use the openipmi if no host is given. The one caveat here is that you cannot control
|
|
92
|
-
remote hosts using
openipmi. The rubyipmi code must be executed on the host you want to control.
|
|
93
|
-
The upside is that you don't need
any credentials. Some commands may require root privileges to run when using openipmi.
|
|
59
|
+
```bash
|
|
60
|
+
|
|
61
|
+
|
|
94
62
|
|
|
95
|
-
|
|
63
|
+
---
|
|
96
64
|
|
|
97
|
-
|
|
65
|
+
## Quick Start
|
|
98
66
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
provider.
|
|
67
|
+
```ruby
|
|
68
|
+
require 'rubyipmi'
|
|
102
69
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
conn = Rubyipmi.connect("username", "password", "hostname", 'freeipmi', {:privilege =>'USER', :driver => 'lan20'})
|
|
106
|
-
```
|
|
70
|
+
# Connect (provider is auto-detected: freeipmi or ipmitool)
|
|
71
|
+
conn = Rubyipmi.connect("username", "password", "hostname")
|
|
107
72
|
|
|
108
|
-
|
|
73
|
+
# Verify connectivity
|
|
74
|
+
conn.connection_works? # => true/false
|
|
109
75
|
|
|
110
|
-
|
|
111
|
-
|
|
76
|
+
# Power
|
|
77
|
+
conn.chassis.power.on?
|
|
78
|
+
conn.chassis.power.off
|
|
79
|
+
conn.chassis.power.on
|
|
80
|
+
conn.chassis.power.cycle
|
|
112
81
|
|
|
113
|
-
|
|
82
|
+
# Sensors
|
|
83
|
+
conn.sensors.names
|
|
84
|
+
conn.sensors.list
|
|
114
85
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
86
|
+
# FRU (serial, model, etc.)
|
|
87
|
+
conn.fru.list
|
|
88
|
+
conn.fru.serial
|
|
89
|
+
conn.fru.manufacturer
|
|
90
|
+
conn.fru.product
|
|
118
91
|
|
|
92
|
+
# BMC info
|
|
93
|
+
conn.bmc.info
|
|
94
|
+
conn.bmc.version
|
|
95
|
+
```
|
|
119
96
|
|
|
120
|
-
|
|
97
|
+
---
|
|
121
98
|
|
|
122
|
-
|
|
123
|
-
require 'rubyipmi'
|
|
124
|
-
conn = Rubyipmi.connect("username", "password", "hostname")
|
|
125
|
-
conn.chassis.power.on
|
|
126
|
-
conn.chassis.power.off
|
|
127
|
-
conn.chassis.power.on?
|
|
128
|
-
conn.chassis.power.off?
|
|
129
|
-
conn.chassis.power.cycle
|
|
99
|
+
## Usage Scenarios
|
|
130
100
|
|
|
131
|
-
|
|
101
|
+
### Connection options
|
|
132
102
|
|
|
133
|
-
|
|
103
|
+
**Basic connection** (auto-detect provider):
|
|
134
104
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
conn.chassis.bootpxe(reboot=bool, persistent=bool)
|
|
139
|
-
conn.chassis.bootdisk(reboot=bool, persistent=bool)
|
|
140
|
-
|
|
141
|
-
Examples:
|
|
142
|
-
conn.chassis.bootpxe(reboot=true, persistent=false) # reboot immediately, PXE boot once
|
|
143
|
-
conn.chassis.bootpxe(reboot=false, persistent=false) # on next reboot PXE boot once
|
|
144
|
-
conn.chassis.bootdisk(reboot=true, persistent=false) # reboot immediately, boot off disk once
|
|
145
|
-
conn.chassis.bootdisk(reboot=true, persistent=true) # reboot immediately, boot off disk forever
|
|
146
|
-
conn.chassis.bootdisk(reboot=false, persistent=true) # reboot off disk forever, starting on next reboot
|
|
147
|
-
```
|
|
105
|
+
```ruby
|
|
106
|
+
conn = Rubyipmi.connect("username", "password", "192.168.1.10")
|
|
107
|
+
```
|
|
148
108
|
|
|
109
|
+
**Choose provider explicitly** (`'auto'`, `'freeipmi'`, or `'ipmitool'`):
|
|
149
110
|
|
|
150
|
-
|
|
111
|
+
```ruby
|
|
112
|
+
conn = Rubyipmi.connect("username", "password", "192.168.1.10", "freeipmi")
|
|
113
|
+
```
|
|
151
114
|
|
|
152
|
-
|
|
153
|
-
require 'rubyipmi'
|
|
154
|
-
conn = Rubyipmi.connect("username", "password", "hostname")
|
|
155
|
-
conn.sensors.names
|
|
156
|
-
conn.sensors.list
|
|
157
|
-
conn.sensors.<sensor name>
|
|
115
|
+
**Extra options** (privilege, driver):
|
|
158
116
|
|
|
159
|
-
|
|
117
|
+
```ruby
|
|
118
|
+
conn = Rubyipmi.connect("username", "password", "192.168.1.10", "freeipmi", {
|
|
119
|
+
privilege: 'ADMINISTRATOR',
|
|
120
|
+
driver: 'lan20' # or 'lan15', 'auto', 'open'
|
|
121
|
+
})
|
|
122
|
+
```
|
|
160
123
|
|
|
161
|
-
|
|
124
|
+
**Local host with OpenIPMI** (no credentials; run on the same host as the BMC):
|
|
162
125
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
conn.fru.serial
|
|
168
|
-
conn.fru.manufacturer
|
|
169
|
-
conn.fru.product
|
|
126
|
+
```ruby
|
|
127
|
+
# Uses openipmi driver; no username/password/host needed
|
|
128
|
+
conn = Rubyipmi.connect(nil, nil, "localhost", "freeipmi", { driver: 'open' })
|
|
129
|
+
```
|
|
170
130
|
|
|
171
|
-
|
|
131
|
+
Valid `privilege` values: `'CALLBACK'`, `'USER'`, `'OPERATOR'`, `'ADMINISTRATOR'`.
|
|
132
|
+
Valid `driver` values: `'auto'`, `'lan15'`, `'lan20'`, `'open'`.
|
|
172
133
|
|
|
173
|
-
|
|
174
|
-
There are a series of automated rspec tests that test the functionality of this library with the ipmi device.
|
|
175
|
-
In order to perform use the following steps.
|
|
134
|
+
---
|
|
176
135
|
|
|
177
|
-
|
|
136
|
+
### Power control
|
|
178
137
|
|
|
138
|
+
```ruby
|
|
139
|
+
conn = Rubyipmi.connect(user, pass, host)
|
|
179
140
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
141
|
+
conn.chassis.power.on # power on
|
|
142
|
+
conn.chassis.power.off # power off
|
|
143
|
+
conn.chassis.power.cycle # power cycle
|
|
144
|
+
conn.chassis.power.on? # => true if on
|
|
145
|
+
conn.chassis.power.off? # => true if off
|
|
146
|
+
```
|
|
185
147
|
|
|
186
|
-
|
|
187
|
-
The only security used throughout the library is the use of temporary password files that store the password while
|
|
188
|
-
the command is being executed. This password file is created and deleted on the fly with every library call.
|
|
189
|
-
The password will not be shown in any logs or process lists due to this enhancement. The filename is a long random string
|
|
190
|
-
as is the folder name so it would be difficult to guess the password file. If for some reason a command takes a long
|
|
191
|
-
time to run anyone could get the location of the file but the file is 0600 so it should not be readable by anyone outside
|
|
192
|
-
of the process.
|
|
148
|
+
---
|
|
193
149
|
|
|
150
|
+
### Boot device and PXE
|
|
194
151
|
|
|
195
|
-
|
|
196
|
-
Since this library is based off of running a suite of command line tools I have created a base class called baseCommand
|
|
197
|
-
that performs the actual execution of the command. The baseCommand only executes the command with the supplied
|
|
198
|
-
arguments and options and returns the exit status. Additionally the result of the executed command is stored in
|
|
199
|
-
the result variable should we need to retrieve the output of the command. To further extend the baseCommand class.
|
|
152
|
+
Set next boot device and optionally reboot. Methods take `(reboot, persistent)`:
|
|
200
153
|
|
|
154
|
+
```ruby
|
|
155
|
+
conn = Rubyipmi.connect(user, pass, host)
|
|
201
156
|
|
|
202
|
-
|
|
203
|
-
|
|
157
|
+
# PXE once, then revert to normal boot (reboot now)
|
|
158
|
+
conn.chassis.bootpxe(true, false)
|
|
204
159
|
|
|
205
|
-
|
|
206
|
-
|
|
160
|
+
# PXE on next reboot only (no reboot now)
|
|
161
|
+
conn.chassis.bootpxe(false, false)
|
|
207
162
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
@options = opts
|
|
211
|
-
super("bmc-info", opts)
|
|
212
|
-
end
|
|
163
|
+
# Boot from disk once, with immediate reboot
|
|
164
|
+
conn.chassis.bootdisk(true, false)
|
|
213
165
|
|
|
214
|
-
|
|
166
|
+
# Boot from disk from now on (persistent)
|
|
167
|
+
conn.chassis.bootdisk(true, true)
|
|
215
168
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
super("ipmitool", opts)
|
|
220
|
-
end
|
|
169
|
+
# CDROM
|
|
170
|
+
conn.chassis.bootcdrom(true, false)
|
|
171
|
+
```
|
|
221
172
|
|
|
222
|
-
|
|
223
|
-
|
|
173
|
+
`reboot`: perform a power cycle after setting the boot device.
|
|
174
|
+
`persistent`: keep the boot device across reboots (otherwise one-time).
|
|
224
175
|
|
|
225
|
-
|
|
226
|
-
The freeipmi command line tools have two different ways to run the commands.
|
|
176
|
+
---
|
|
227
177
|
|
|
228
|
-
|
|
229
|
-
2. ``` ipmi-chassis --hostname=host --password=pass --username=user --chassis-identify=FORCE ``` (multiple arguments, one main action with different qualifers)
|
|
178
|
+
### Sensors and monitoring
|
|
230
179
|
|
|
231
|
-
|
|
180
|
+
Useful for monitoring stacks (e.g. Sensu, Prometheus exporters):
|
|
232
181
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
3. The options variable gets set when instantiating a subclass of the baseCommand class.
|
|
182
|
+
```ruby
|
|
183
|
+
conn = Rubyipmi.connect(user, pass, host)
|
|
236
184
|
|
|
237
|
-
|
|
185
|
+
# List sensor names
|
|
186
|
+
conn.sensors.names
|
|
238
187
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
3. To run the cmd, with just arguments ``` runcmd_with_args([]) ``` and pass in a array of the arguments. (Example: 192.168.1.1)
|
|
188
|
+
# Full sensor list (array of sensor data)
|
|
189
|
+
conn.sensors.list
|
|
242
190
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
the runcmd command. Failure to do so will add previous options to subsequent run options.
|
|
191
|
+
# Access a sensor by normalized name (underscores, no spaces/dots)
|
|
192
|
+
conn.sensors.temperature_cpu1
|
|
193
|
+
conn.sensors.fan_speed_1
|
|
194
|
+
```
|
|
248
195
|
|
|
249
|
-
Example:
|
|
196
|
+
Example: collect temperatures for a custom monitor:
|
|
250
197
|
|
|
251
198
|
```ruby
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
options["chassis-identify"] = "FORCE"
|
|
256
|
-
else
|
|
257
|
-
options["chassis-identify"] = delay
|
|
258
|
-
end
|
|
259
|
-
else
|
|
260
|
-
options["chassis-identify"] = "TURN-OFF"
|
|
261
|
-
end
|
|
262
|
-
# Run the command
|
|
263
|
-
run
|
|
264
|
-
# unset the option by deleting from hash
|
|
265
|
-
options.delete("chassis-identify")
|
|
199
|
+
conn.sensors.list.each do |sensor|
|
|
200
|
+
next unless sensor[:name].to_s.include?('temp')
|
|
201
|
+
puts "#{sensor[:name]}: #{sensor[:value]} #{sensor[:unit]}"
|
|
266
202
|
end
|
|
267
|
-
|
|
268
203
|
```
|
|
269
204
|
|
|
270
|
-
|
|
271
|
-
After running a command it may be desirable to get the results for further processing.
|
|
272
|
-
Note that there are two kinds of results.
|
|
273
|
-
1. the text returned from the shell command, this is stored in @results
|
|
274
|
-
2. the status value returned from the shell command (true or false only) this is returned from runcmd.
|
|
205
|
+
---
|
|
275
206
|
|
|
276
|
-
|
|
207
|
+
### FRU (Field Replaceable Unit) info
|
|
277
208
|
|
|
278
|
-
|
|
209
|
+
Serial numbers, product names, manufacturers:
|
|
279
210
|
|
|
280
211
|
```ruby
|
|
212
|
+
conn = Rubyipmi.connect(user, pass, host)
|
|
281
213
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
end
|
|
214
|
+
conn.fru.list # full FRU data
|
|
215
|
+
conn.fru.serial # serial number(s)
|
|
216
|
+
conn.fru.manufacturer # manufacturer
|
|
217
|
+
conn.fru.product # product name
|
|
218
|
+
```
|
|
288
219
|
|
|
289
|
-
|
|
290
|
-
status = run([opt])
|
|
291
|
-
return status
|
|
292
|
-
end
|
|
220
|
+
Handy for asset tracking or automation that needs to identify hardware.
|
|
293
221
|
|
|
294
|
-
|
|
295
|
-
status == "on"
|
|
296
|
-
end
|
|
222
|
+
---
|
|
297
223
|
|
|
298
|
-
|
|
224
|
+
### BMC info and diagnostics
|
|
299
225
|
|
|
300
|
-
|
|
301
|
-
Although its not necessary to implement the command function it may be desirable if your code starts to repeat itself.
|
|
302
|
-
In this example the command function is just a wrapper command that calls run. Your implementation will vary,
|
|
303
|
-
but be sure to always call it the "command" function, so its easily identified.
|
|
304
|
-
Additionally, should this gem ever become out of date one could call the command function and pass in any
|
|
305
|
-
arguments that have not already been implemented in the rest of the class.
|
|
226
|
+
**BMC info and version:**
|
|
306
227
|
|
|
307
228
|
```ruby
|
|
229
|
+
conn.bmc.info
|
|
230
|
+
conn.bmc.version
|
|
231
|
+
```
|
|
308
232
|
|
|
309
|
-
|
|
310
|
-
status = runcmd([opt])
|
|
311
|
-
return status
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
def on
|
|
315
|
-
command("--on")
|
|
316
|
-
end
|
|
233
|
+
**Connection check** (single place to validate credentials/host):
|
|
317
234
|
|
|
235
|
+
```ruby
|
|
236
|
+
if conn.connection_works?
|
|
237
|
+
# proceed with power, sensors, etc.
|
|
238
|
+
else
|
|
239
|
+
# handle unreachable or bad credentials
|
|
240
|
+
end
|
|
318
241
|
```
|
|
319
242
|
|
|
320
|
-
|
|
243
|
+
**Generate a diagnostics file** (for bug reports or vendor-specific issues):
|
|
321
244
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
* ipmidetect
|
|
329
|
-
* ipmi-raw
|
|
330
|
-
* ipmi-fru
|
|
331
|
-
* ipmi-sel
|
|
332
|
-
* ipmi-locate
|
|
333
|
-
* ipmi-sensors
|
|
334
|
-
* ipmimonitoring
|
|
335
|
-
* ipmi-sensors-config
|
|
336
|
-
* bmc-config
|
|
337
|
-
* bmc-device
|
|
338
|
-
* bmc-info
|
|
245
|
+
```ruby
|
|
246
|
+
require 'rubyipmi'
|
|
247
|
+
Rubyipmi.get_diag(user, pass, host)
|
|
248
|
+
# Writes: /tmp/rubyipmi_diag_data.txt
|
|
249
|
+
# Review for sensitive data (IP/MAC, etc.) before sharing.
|
|
250
|
+
```
|
|
339
251
|
|
|
340
|
-
|
|
341
|
-
* ipmitool
|
|
252
|
+
With debug logging:
|
|
342
253
|
|
|
254
|
+
```ruby
|
|
255
|
+
require 'rubyipmi'
|
|
256
|
+
require 'logger'
|
|
257
|
+
Rubyipmi.log_level = Logger::DEBUG
|
|
258
|
+
Rubyipmi.get_diag(user, pass, host)
|
|
259
|
+
# Also creates /tmp/rubyipmi.log with commands run
|
|
260
|
+
```
|
|
343
261
|
|
|
262
|
+
---
|
|
344
263
|
|
|
345
|
-
##
|
|
346
|
-
IPMI is great for a vendor neutral management interface. However, not all servers are 100% compatible with the specifications.
|
|
347
|
-
In order to overcome ipmi non-compliance there will be some workarounds built into this library
|
|
264
|
+
## Development
|
|
348
265
|
|
|
349
|
-
|
|
266
|
+
### Running tests
|
|
350
267
|
|
|
351
|
-
|
|
352
|
-
Rubyipmi has a built in logging system for debugging purposes. By default logging is disabled. The logger is a class instance
|
|
353
|
-
variable and will stay in memory for as long as your program or interpreter is loaded. In order to enable logging
|
|
354
|
-
you need to do the following.
|
|
268
|
+
**Unit tests** (no BMC required; mocks only):
|
|
355
269
|
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
Rubyipmi.log_level = Logger::DEBUG
|
|
270
|
+
```bash
|
|
271
|
+
bundle install
|
|
272
|
+
bundle exec rake unit
|
|
360
273
|
```
|
|
361
|
-
This will create a log file in /tmp/rubyipmi.log which you can use to trace the commands Rubyipmi generates and runs.
|
|
362
274
|
|
|
363
|
-
|
|
275
|
+
**Integration tests** (require a real BMC; **they will power off/cycle the device**):
|
|
364
276
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
277
|
+
Do **not** run on production systems.
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
bundle exec rake integration \
|
|
281
|
+
ipmiuser=USER \
|
|
282
|
+
ipmipass=PASS \
|
|
283
|
+
ipmihost=192.168.1.10 \
|
|
284
|
+
ipmiprovider=freeipmi
|
|
372
285
|
```
|
|
373
286
|
|
|
374
|
-
|
|
375
|
-
Running IPMI commands can be frustrating sometimes and with the addition of this library you are bound to find edge
|
|
376
|
-
cases. If you do find an edge case there is a easy function that will generate a diagnostics file that you can
|
|
377
|
-
review and optionally create an issue with us to work with. Without this information its really hard to help because
|
|
378
|
-
every server is different. The following code will generate a file in /tmp/rubyipmi_diag_data.txt that we can use
|
|
379
|
-
as test cases. Please look over the file for any sensitive data you don't want to share like ip/mac address.
|
|
287
|
+
**Vagrant-based integration** (if you use the spec Vagrant setup):
|
|
380
288
|
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
|
|
289
|
+
```bash
|
|
290
|
+
cd spec && vagrant up && vagrant provision
|
|
291
|
+
vagrant ssh -c "/rubyipmi/rake integration ipmiuser=... ipmipass=... ipmihost=... ipmiprovider=freeipmi"
|
|
384
292
|
```
|
|
385
293
|
|
|
386
|
-
|
|
294
|
+
**CI:** The repo uses GitHub Actions; see `.github/workflows/test.yml`. Typical flow: checkout → Ruby 3.x → `bundle install` → `bundle exec rake unit` → `gem build`.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### Extending the library
|
|
299
|
+
|
|
300
|
+
Rubyipmi runs the underlying CLI tools via a small command layer. To add or wrap new behavior:
|
|
301
|
+
|
|
302
|
+
1. **Subclass the provider’s BaseCommand**
|
|
303
|
+
Use `Rubyipmi::Freeipmi::Commands::BaseCommand` or `Rubyipmi::Ipmitool::Commands::BaseCommand`.
|
|
304
|
+
|
|
305
|
+
2. **Initialize with the executable name** (e.g. freeipmi’s `bmc-info` or `ipmitool`):
|
|
306
|
+
|
|
307
|
+
```ruby
|
|
308
|
+
def initialize(opts = {})
|
|
309
|
+
@options = opts
|
|
310
|
+
super("bmc-info", opts) # or "ipmitool" for ipmitool
|
|
311
|
+
end
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
3. **Use the shared execution helpers**
|
|
315
|
+
- `runcmd` – run with current `options` (e.g. hostname, username, password).
|
|
316
|
+
- `runcmd(["--option"])` – run with extra arguments.
|
|
317
|
+
- `runcmd_with_args([...])` – run with only the given args.
|
|
318
|
+
- `@result` holds stdout; the return value of `runcmd` is the command success (true/false).
|
|
319
|
+
|
|
320
|
+
4. **Options hash**
|
|
321
|
+
Connection options (host, user, password, driver, privilege) are in `options`. Add command-specific keys for that run, then **delete them after** so they don’t leak into the next command:
|
|
322
|
+
|
|
323
|
+
```ruby
|
|
324
|
+
def some_action
|
|
325
|
+
options["chassis-identify"] = "FORCE"
|
|
326
|
+
runcmd
|
|
327
|
+
options.delete("chassis-identify")
|
|
328
|
+
end
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
5. **Expose the new command from the Connection**
|
|
332
|
+
In `lib/rubyipmi/freeipmi/connection.rb` (or ipmitool equivalent), add an accessor and instantiate your command class with `@options`.
|
|
333
|
+
|
|
334
|
+
**Freeipmi** uses many separate binaries (e.g. `ipmi-chassis`, `ipmi-sensors`, `bmc-info`). **Ipmitool** uses a single `ipmitool` binary with subcommands. Implement the appropriate BaseCommand and wire it into the connection object.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Troubleshooting
|
|
339
|
+
|
|
340
|
+
### Logging
|
|
341
|
+
|
|
342
|
+
By default, logging is disabled. To trace commands and options:
|
|
387
343
|
|
|
388
344
|
```ruby
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
345
|
+
require 'rubyipmi'
|
|
346
|
+
require 'logger'
|
|
347
|
+
Rubyipmi.log_level = Logger::DEBUG
|
|
348
|
+
# Log file: /tmp/rubyipmi.log
|
|
393
349
|
```
|
|
394
350
|
|
|
395
|
-
|
|
396
|
-
If you need to test if the bmc device and run a basic call there is now a function that retruns boolean true when
|
|
397
|
-
the connection attempt was successful.
|
|
351
|
+
Custom logger:
|
|
398
352
|
|
|
399
353
|
```ruby
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
354
|
+
custom = Logger.new('/var/log/rubyipmi.log')
|
|
355
|
+
custom.progname = 'Rubyipmi'
|
|
356
|
+
custom.level = Logger::DEBUG
|
|
357
|
+
Rubyipmi.logger = custom
|
|
403
358
|
```
|
|
404
359
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
* Check out the latest code to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
|
408
|
-
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
|
409
|
-
* Fork the project.
|
|
410
|
-
* Start a feature/bugfix branch.
|
|
411
|
-
* Commit and push until you are happy with your contribution.
|
|
412
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
|
413
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
|
360
|
+
### Diagnostics
|
|
414
361
|
|
|
415
|
-
|
|
362
|
+
For support or bug reports, generate a diagnostics file and (after redacting) attach it:
|
|
416
363
|
|
|
417
|
-
|
|
418
|
-
|
|
364
|
+
```ruby
|
|
365
|
+
Rubyipmi.get_diag(user, pass, host)
|
|
366
|
+
# Edit /tmp/rubyipmi_diag_data.txt to remove sensitive data, then share.
|
|
367
|
+
```
|
|
419
368
|
|
|
369
|
+
### Connection test
|
|
420
370
|
|
|
421
|
-
|
|
371
|
+
```ruby
|
|
372
|
+
conn = Rubyipmi.connect(user, pass, host)
|
|
373
|
+
conn.connection_works? # => true/false
|
|
374
|
+
```
|
|
422
375
|
|
|
376
|
+
---
|
|
423
377
|
|
|
424
|
-
|
|
378
|
+
## Security
|
|
425
379
|
|
|
426
|
-
|
|
380
|
+
Credentials are not passed on the command line. The library uses temporary files (mode `0600`) to pass passwords to the underlying CLI tools; files are created and removed around each call. Filenames and directory names are randomized to avoid guessing. Passwords do not appear in process listings or in logs.
|
|
427
381
|
|
|
428
|
-
|
|
429
|
-
The hardware listed below may only indicate the hardware that a problem was discovered on. Newer versions of hardware may fix the problems indicated below. Similar machines from vendors may or may not exhibit the same problems. Different vendors may license their firmware from the same IPMI firmware developer, so it may be worthwhile to try workarounds listed below even if your motherboard is not listed.
|
|
382
|
+
---
|
|
430
383
|
|
|
431
|
-
|
|
384
|
+
## Projects using Rubyipmi
|
|
432
385
|
|
|
433
|
-
|
|
386
|
+
- [sensu-plugins-ipmi](https://github.com/sensu-plugins/sensu-plugins-ipmi) – IPMI checks for Sensu
|
|
387
|
+
- [smart-proxy](https://github.com/theforeman/smart-proxy) – Foreman Smart Proxy (exposes Rubyipmi as a remote API)
|
|
388
|
+
- [ipmispec](https://github.com/logicminds/ipmispec)
|
|
434
389
|
|
|
435
|
-
|
|
390
|
+
If you use Rubyipmi in a project, open a PR to add it to this list.
|
|
436
391
|
|
|
437
|
-
|
|
392
|
+
---
|
|
438
393
|
|
|
439
|
-
|
|
394
|
+
## Support
|
|
440
395
|
|
|
441
|
-
|
|
396
|
+
- **Community:** Open a [GitHub issue](https://github.com/logicminds/rubyipmi/issues) for bugs or feature requests.
|
|
397
|
+
- **Paid support:** [LogicMinds](http://www.logicminds.biz) offers professional support and custom development.
|
|
442
398
|
|
|
443
|
-
|
|
399
|
+
Test coverage is limited to the hardware available to the maintainers (e.g. HP DL380 G5). IPMI is vendor-neutral, but implementations vary. Devices not regularly tested include Dell, IBM, HP iLO3+, Supermicro, Cisco. If you hit vendor-specific issues, diagnostics (see above) and FreeIPMI workarounds (below) often help.
|
|
444
400
|
|
|
445
|
-
|
|
401
|
+
---
|
|
446
402
|
|
|
447
|
-
|
|
403
|
+
## Contributing
|
|
448
404
|
|
|
449
|
-
|
|
405
|
+
1. Check existing issues and PRs to avoid duplicate work.
|
|
406
|
+
2. Fork the repo and create a feature or bugfix branch.
|
|
407
|
+
3. Add tests for new behavior (unit tests for logic, integration only when needed).
|
|
408
|
+
4. Keep Rakefile, version, and history changes minimal; if necessary, isolate in a single commit.
|
|
409
|
+
5. Open a pull request with a clear description of the change.
|
|
450
410
|
|
|
451
|
-
|
|
411
|
+
---
|
|
452
412
|
|
|
453
|
-
|
|
413
|
+
## License
|
|
454
414
|
|
|
455
|
-
|
|
415
|
+
Copyright (c) 2015 Corey Osman. See [LICENSE.txt](LICENSE.txt) for details (LGPL-2.1).
|
|
456
416
|
|
|
457
|
-
|
|
417
|
+
---
|
|
458
418
|
|
|
459
|
-
|
|
419
|
+
## FreeIPMI documented workarounds
|
|
460
420
|
|
|
461
|
-
|
|
421
|
+
Many vendors implement IPMI with quirks. FreeIPMI documents workarounds (e.g. `-W intel20`, `-W supermicro20`). Rubyipmi may expose or use some of these; for the full list and `-W` options, see the [FreeIPMI documentation](https://www.gnu.org/software/freeipmi/). If you need a workaround not yet supported, opening an issue with your BMC model and the FreeIPMI workaround that works on the CLI can help.
|
|
462
422
|
|
|
423
|
+
Common workaround flags (refer to FreeIPMI for current details):
|
|
463
424
|
|
|
425
|
+
- **assumeio** – inband I/O (e.g. HP ProLiant DL145 G1).
|
|
426
|
+
- **authcap** – skip early auth capability checks (e.g. Asus, Intel, Sun).
|
|
427
|
+
- **intel20** – Intel IPMI 2.0 auth (e.g. Intel SE7520AF2).
|
|
428
|
+
- **supermicro20** – Supermicro IPMI 2.0 (e.g. H8QME).
|
|
429
|
+
- **sun20** / **opensesspriv** – Sun/ILOM auth and session handling.
|
|
430
|
+
- **idzero**, **unexpectedauth**, **forcepermsg**, **endianseq** – session and auth quirks on various Dell, IBM, Tyan, Sun.
|
|
431
|
+
- **No IPMI 1.5 support** – use driver `lan20` (e.g. HP ProLiant DL145).
|
|
432
|
+
- **slowcommit** / **veryslowcommit** – BMCs that need slower config commits (e.g. Supermicro, Quanta/Dell).
|
|
464
433
|
|
|
434
|
+
Hardware listed is where issues were first seen; newer firmware may fix them. Similar or licensed firmware from other vendors may behave the same. To request new workarounds in FreeIPMI, contact [freeipmi-users](https://www.gnu.org/software/freeipmi/) or [freeipmi-devel](https://www.gnu.org/software/freeipmi/).
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.13.0
|
|
@@ -66,7 +66,7 @@ module Rubyipmi
|
|
|
66
66
|
logger.debug(makecommand) if logger
|
|
67
67
|
begin
|
|
68
68
|
command = makecommand
|
|
69
|
-
@lastcall = command
|
|
69
|
+
@lastcall = command
|
|
70
70
|
@result, @result_err, status = Rubyipmi.capture3(command)
|
|
71
71
|
# sometimes the command tool does not return the correct result, validate it with additional code
|
|
72
72
|
process_status = validate_status(status)
|
|
@@ -25,9 +25,9 @@ module Rubyipmi::Freeipmi
|
|
|
25
25
|
else
|
|
26
26
|
"--#{k}=#{v}"
|
|
27
27
|
end
|
|
28
|
-
end
|
|
28
|
+
end
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
[cmd] + args.compact
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# This method will check if the results are really valid as the exit code can be misleading and incorrect
|
|
@@ -15,19 +15,19 @@ module Rubyipmi::Ipmitool
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def makecommand
|
|
18
|
-
args =
|
|
18
|
+
args = []
|
|
19
19
|
# need to format the options to ipmitool format
|
|
20
20
|
@options.each do |k, v|
|
|
21
21
|
# must remove from command line as its handled via conf file
|
|
22
22
|
next if k == "P"
|
|
23
23
|
next if k == "cmdargs"
|
|
24
|
-
args
|
|
24
|
+
args += ["-#{k}", v]
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# since ipmitool requires commands to be in specific order
|
|
28
|
-
args
|
|
28
|
+
args += options.fetch('cmdargs', '').split
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
[cmd] + args.compact
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# The findfix method acts like a recursive method and applies fixes defined in the errorcodes
|
data/lib/rubyipmi/version.rb
CHANGED
data/lib/rubyipmi.rb
CHANGED
data/rubyipmi.gemspec
CHANGED
|
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
|
|
|
25
25
|
s.add_dependency 'logger'
|
|
26
26
|
s.add_development_dependency 'rspec'
|
|
27
27
|
s.add_development_dependency 'rdoc', "~> 6.1"
|
|
28
|
-
s.add_development_dependency 'bundler', "
|
|
28
|
+
s.add_development_dependency 'bundler', ">= 2.0"
|
|
29
29
|
s.add_development_dependency 'highline', '>= 1.0', '< 3'
|
|
30
30
|
s.add_development_dependency 'rake', '~> 13'
|
|
31
31
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubyipmi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Corey Osman
|
|
@@ -69,14 +69,14 @@ dependencies:
|
|
|
69
69
|
name: bundler
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
|
-
- - "
|
|
72
|
+
- - ">="
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
74
|
version: '2.0'
|
|
75
75
|
type: :development
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
|
-
- - "
|
|
79
|
+
- - ">="
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '2.0'
|
|
82
82
|
- !ruby/object:Gem::Dependency
|