Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ Unit tests must not depend on a real BMC. Exercising the RMCP+ session code agai

## Code quality reports

Code quality reports (checkstyle, pmd/cpd, spotbugs) are generated by `mvn verify site` into ./target/checkstyle-result.xml, ./target/pmd.xml, ./target/cpd.xml and ./target/spotbugsXml.xml. Checkstyle and PMD are gated (the build fails on any error); CPD is gated on duplications of 100 tokens or more (`cpd-check` at `verify`), while the site report lists those of 50 tokens or more; SpotBugs is not yet gated (issue #116 tracks the clean-up). Extract shared code instead of copying it: `AbstractSensorRecord` holds what the Full, Compact and Event-Only sensor records share, and `IpmiCommandCoder.validateResponse()` the response checks of every command. Do not add new violations: check the reports for the files you changed before committing and submitting your code. On JDK 21+ the SpotBugs plugin version inherited from the parent POM cannot read the JDK class files; run `mvn com.github.spotbugs:spotbugs-maven-plugin:4.10.4.1:spotbugs` instead.
Code quality reports (checkstyle, pmd/cpd, spotbugs) are generated by `mvn verify site` into ./target/checkstyle-result.xml, ./target/pmd.xml, ./target/cpd.xml and ./target/spotbugsXml.xml. Checkstyle and PMD are gated (the build fails on any error); CPD is gated on duplications of 100 tokens or more (`cpd-check` at `verify`), while the site report lists those of 50 tokens or more; SpotBugs is not yet gated (issue #116 tracks the clean-up). Extract shared code instead of copying it: `AbstractSensorRecord` holds what the Full, Compact and Event-Only sensor records share, and `IpmiCommandCoder.validateResponse()` the response checks of every command. Do not add new violations: check the reports for the files you changed before committing and submitting your code. The SpotBugs site report runs spotbugs-maven-plugin 4.10.4.1 (pinned in `<reporting>`, as the 4.9.3.0 of the parent POM cannot read the class files of JDK 21+); to run SpotBugs alone, use `mvn com.github.spotbugs:spotbugs-maven-plugin:4.10.4.1:spotbugs`.

## Documentation

Always make sure that public API changes are properly documented in src/site/markdown/*.md and that README.md is always up-to-date. The published documentation is https://metricshub.org/ipmi-java (see issue #113 for the planned overhaul).
Always make sure that public API changes are properly documented in src/site/markdown/*.md and that README.md is always up-to-date. The published documentation is https://metricshub.org/ipmi-java.

The site is built with maven-site-plugin 4 and the Sentry Maven Skin (https://sentrysoftware.org/sentry-maven-skin/, see its "Vibe Writing" page for the syntax: callouts, tabs, `toc` macro). Every page starts with `keywords:` and `description:` headers, has a single H1 and the `toc` macro, and is listed in `src/site/site.xml` (Getting Started, Usage or Reference menu). Keep the pages true to the code: verify examples against the API (and, where they show output, against a real BMC). File the library bugs found while writing documentation as GitHub issues instead of describing them in the pages, which document how the library is meant to work. User-visible changes go to `upgrading.md`.

## IPMI specifics

Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@ This project is a fork of the excellent [IPMI Library for Java by Verax Systems]

See **[Project Documentation](https://metricshub.org/ipmi-java)** and the [Javadoc](https://metricshub.org/ipmi-java/apidocs) for more information on how to use this library in your code.

The IPMI Java Client is a library that communicates with the IPMI host, fetches Field Replaceable Units (FRUs) and Sensors information then reports these information as a text output.
The IPMI Java Client talks to the Baseboard Management Controller (BMC) of a server over IPMI 2.0 over LAN (RMCP+): it reads the chassis status, the Field Replaceable Units (FRUs) and the sensors of the SDR repository, as Java objects or as the text output that MetricsHub parses, and its low-level API sends any IPMI command (System Event Log, chassis control, Serial over LAN). It requires Java 8 or later.

```java
IpmiClientConfiguration config = new IpmiClientConfiguration("bmc.example.com", "monitor", password, null, false, 120);
System.out.println(IpmiClient.getChassisStatusAsStringResult(config));
System.out.println(IpmiClient.getFrusAndSensorsAsStringResult(config));
```

The BMC must have IPMI over LAN enabled and an account with the User privilege: see [Preparing the BMC](https://metricshub.org/ipmi-java/preparing-the-bmc.html).

## Upgrading

Version 1.2.03 makes the `protected` fields of the protocol classes (`AbstractIpmiRunner`, `MessageHandler`, `IpmiLanMessage`, `ConfidentialityAlgorithm`, `IntegrityAlgorithm`) `private`. Subclasses must use the new `protected` accessors instead; see [Upgrading from 1.2.02](https://metricshub.org/ipmi-java/#upgrading-from-1-2-02) for the list. The `IpmiClient` API is unchanged. The Full, Compact and Event-Only sensor records now share the `AbstractSensorRecord` superclass, and commands can check responses with `IpmiCommandCoder.validateResponse()`; both are described on the same page.
Version 1.2.03 makes the `protected` fields of the protocol classes (`AbstractIpmiRunner`, `MessageHandler`, `IpmiLanMessage`, `ConfidentialityAlgorithm`, `IntegrityAlgorithm`) `private`. Subclasses must use the new `protected` accessors instead; see [Upgrading from 1.2.02](https://metricshub.org/ipmi-java/upgrading.html#upgrading-from-1-2-02) for the list. The `IpmiClient` API is unchanged. The Full, Compact and Event-Only sensor records now share the `AbstractSensorRecord` superclass, and commands can check responses with `IpmiCommandCoder.validateResponse()`; both are described on the same page.

## Build instructions

Expand All @@ -22,6 +30,8 @@ This is a simple Maven project. Build with:
mvn verify
```

`mvn verify site` also builds the documentation in `target/site` (sources in [src/site](src/site)).

## Code format

The code is formatted with the MetricsHub Eclipse formatter profile ([metricshub-eclipse-formatter.xml](metricshub-eclipse-formatter.xml), shared with the other MetricsHub Java projects), and the build fails on unformatted code. Simply run the below command before committing:
Expand Down
49 changes: 48 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.metricshub</groupId>
<artifactId>oss-parent</artifactId>
<version>4</version>
<version>5</version>
</parent>

<artifactId>ipmi-java</artifactId>
Expand Down Expand Up @@ -68,8 +68,14 @@
<!-- Java 8 -->
<maven.compiler.release>8</maven.compiler.release>

<!-- Encoding of the generated site reports. oss-parent only sets the source encoding;
spotbugs-maven-plugin 4.10.4 fails with "charsetName" while rendering its report
during `mvn verify site` when this is missing. -->
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<!-- Reproducible Build -->
<!-- See https://maven.apache.org/guides/mini/guide-reproducible-builds.html -->
<!-- Also drives the Maven site copyright year and "Documentation as of" date; bumped at release. -->
<project.build.outputTimestamp>2025-11-20T18:41:44Z</project.build.outputTimestamp>
</properties>

Expand Down Expand Up @@ -184,12 +190,53 @@
<canUpdateDescription>false</canUpdateDescription>
</configuration>
</plugin>

<!-- site: the maven-site-plugin 3.12.1 inherited from oss-parent mixes Velocity 1.7 with the Velocity 2.x tools
of maven-skin-tools and randomly fails with an AbstractMethodError (ToolContext vs Context). Sentry Maven Skin 8
needs maven-site-plugin 3.21.0+ (Doxia 2.0) and its companion maven-skin-tools -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>4.0.0-M16</version>
<dependencies>
<dependency>
<groupId>org.sentrysoftware.maven</groupId>
<artifactId>maven-skin-tools</artifactId>
<version>1.8.01</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

<reporting>
<plugins>

<!-- project-info-reports, jxr, checkstyle and javadoc (published at apidocs/) are inherited from oss-parent -->

<!-- surefire report: renders the unit-test results from target/surefire-reports -->
<plugin>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>3.6.0</version>
</plugin>

<!-- spotbugs: the 4.9.3.0 inherited from oss-parent cannot read the class files of JDK 21+ -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.10.4.1</version>
</plugin>

<!-- changelog: the inherited maven-changelog-plugin 3.0.0-M1 predates Doxia 2.0; disable it with an empty
report set -->
<plugin>
<artifactId>maven-changelog-plugin</artifactId>
<reportSets>
<reportSet>
<reports />
</reportSet>
</reportSets>
</plugin>

<!-- pmd: same version as the build gate, so the report shows what the gate checked -->
<plugin>
<artifactId>maven-pmd-plugin</artifactId>
Expand Down
76 changes: 76 additions & 0 deletions src/site/markdown/chassis-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
keywords: chassis status, power state, power on, power off, power restore policy, intrusion, fault, get chassis status
description: Read the chassis status of a server through its BMC — power state, last power event, power restore policy, faults, intrusion and front panel flags.

# Chassis Status

<!-- MACRO{toc|fromDepth=2|toDepth=3|id=toc} -->

The IPMI **Get Chassis Status** command reports whether the server is powered on, why it last
changed power state, what it does when mains power returns, and a few fault and front panel
flags. It is a single request: reading it is fast, and it is a good way to check that the
credentials work.

## As text

```java
String status = IpmiClient.getChassisStatusAsStringResult(config);
// "System power state is up" or "System power state is down"
```

The text reports the power state only.

## As an object

```java
GetChassisStatusResponseData status = IpmiClient.getChassisStatus(config);

System.out.println("Power: " + (status.isPowerOn() ? "on" : "off"));
System.out.println("Restore policy: " + status.getPowerRestorePolicy());
System.out.println("Intrusion: " + status.isChassisIntrusionActive());
```

[`GetChassisStatusResponseData`](apidocs/org/metricshub/ipmi/core/coding/commands/chassis/GetChassisStatusResponseData.html)
decodes the response (IPMI 2.0, section 28.2):

| Method | Meaning |
| --- | --- |
| **Current power state** | |
| `isPowerOn()` | System power is on |
| `isPowerOverload()` | The system was shut down because of a power overload |
| `isInterlock()` | A power interlock (a switch that cuts power when the chassis is open) is active |
| `isPowerFault()` | A fault was detected in the main power subsystem |
| `isPowerControlFault()` | The power controller tried to change the power state and failed |
| `getPowerRestorePolicy()` | What happens when mains power returns: `PoweredOff`, `PowerRestored` (back to the previous state) or `PoweredUp` |
| **Last power event** | |
| `wasIpmiPowerOn()` | The last power-on was requested through IPMI |
| `wasPowerFault()` | The last power-down was caused by a power fault |
| `wasInterlock()` | The last power-down was caused by a power interlock |
| `wasPowerOverload()` | The last power-down was caused by a power overload |
| `acFailed()` | Mains (AC) power was lost |
| **Miscellaneous chassis state** | |
| `isChassisIntrusionActive()` | The chassis intrusion sensor is active (the case is or was open) |
| `isFrontPanelLockoutActive()` | The power off and reset buttons of the front panel are disabled |
| `driveFaultDetected()` | A drive fault was detected |
| `coolingFaultDetected()` | A cooling or fan fault was detected |
| `isChassisIdentifyCommandSupported()`, `getChassisIdentifyState()` | Whether the identify LED can be read, and its state: `Off`, `TemporaryOn`, `IndefiniteOn` |
| **Front panel buttons** (optional in the response) | |
| `isFrontPanelButtonCapabilitiesSet()` | The BMC returned the front panel button byte; the methods below throw `IllegalAccessException` otherwise |
| `isPowerOffButtonDisabled()`, `isResetButtonDisabled()`, ... | State of each button, and whether it can be disabled (`...DisableAllowed()`) |

> [!NOTE]
> `getChassisIdentifyState()` throws `IllegalAccessError` when
> `isChassisIdentifyCommandSupported()` is `false`: check it first. `getPowerRestorePolicy()`
> throws `IllegalArgumentException` when the BMC reports the policy as *unknown*
> ([#87](https://github.com/metricshub/ipmi-java/issues/87)).

Not every BMC fills every flag: the intrusion, drive and cooling bits in particular are optional
in the specification, and a BMC that does not implement them reports `false`.

## Controlling the power

`IpmiClient` only reads. To power the server on or off or reset it, send the
[`ChassisControl`](apidocs/org/metricshub/ipmi/core/coding/commands/chassis/ChassisControl.html)
command with the [low-level API](low-level-api.html#sending-commands), in a session opened with
the **Operator** or **Administrator** privilege. The supported power commands are `PowerDown`,
`PowerUp` and `HardReset`. Exposing power control in `IpmiClient` is tracked in
[#104](https://github.com/metricshub/ipmi-java/issues/104).
117 changes: 117 additions & 0 deletions src/site/markdown/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
keywords: configuration, ipmiclientconfiguration, credentials, password, bmc key, kg, skipauth, timeout, pingperiod, keep-alive, port
description: Every option of IpmiClientConfiguration — host and port, credentials, BMC key, skipAuth, the overall timeout and the keep-alive period — with their defaults and exact semantics.

# Configuration

<!-- MACRO{toc|fromDepth=2|toDepth=3|id=toc} -->

Every call of [`IpmiClient`](apidocs/org/metricshub/ipmi/client/IpmiClient.html) takes an
[`IpmiClientConfiguration`](apidocs/org/metricshub/ipmi/client/IpmiClientConfiguration.html),
which carries the target, the credentials and the timing options. It is a plain mutable object:
build it once per BMC and reuse it for every call; the client never modifies it.

## Constructors

```java
// host, user, password, BMC key, skipAuth, timeout (s)
new IpmiClientConfiguration("bmc.example.com", "monitor", password, null, false, 120);

// ... with a UDP port other than 623
new IpmiClientConfiguration("bmc.example.com", 6230, "monitor", password, null, false, 120);

// ... with a keep-alive period (ms), 0 to disable the keep-alive messages
new IpmiClientConfiguration("bmc.example.com", "monitor", password, null, false, 120, 0);
```

Every option also has a setter (`setPort(int)`, `setPingPeriod(long)`, ...), so the options of
one constructor can be combined with those of another.

## Options

| Option | Default | Details |
| --- | --- | --- |
| `hostname` | required | [Host and port](#host-and-port) |
| `port` | `623` | [Host and port](#host-and-port) |
| `username`, `password` | required | [Credentials](#credentials) |
| `bmcKey` | `null` | [BMC key](#bmc-key) |
| `skipAuth` | required | [skipAuth](#skipauth) |
| `timeout` | required, in **seconds** | [Timeout](#timeout) |
| `pingPeriod` | `-1`: 30 000 ms | [Keep-alive](#keep-alive) |

### Host and port

`hostname` is the host name or the IP address (IPv4 or IPv6) of the **BMC**, not of the server's
operating system. It is resolved with `InetAddress.getByName()` at each call.

`port` is the UDP port of the BMC, **623** by default. Change it only for a BMC behind a NAT
or a proxy that forwards another port to 623. The local UDP port is always an ephemeral one,
chosen by the operating system for each session.

### Credentials

`username` and `password` are the IPMI account of the BMC: see
[Preparing the BMC](preparing-the-bmc.html#creating-the-account). The password is a `char[]`;
the library converts it to a `String` internally to open the session and does not clear the
array, so clear it yourself once you no longer need the configuration.

The client opens every session with the **User** privilege level, which is enough for every
`IpmiClient` method.

### BMC key

`bmcKey` is the **BMC key (Kg)** of the BMC, as raw bytes, for BMCs configured with *two-key*
logins. Leave it `null` (the default on virtually every BMC): the session keys are then derived
from the password. See [Preparing the BMC](preparing-the-bmc.html#bmc-key-kg).
Comment thread
bertysentry marked this conversation as resolved.

### skipAuth

Despite its name, `skipAuth` does **not** skip authentication: the session is always
authenticated with the user name and password (RAKP handshake). It chooses how the cipher suite is
picked:

| `skipAuth` | Before opening the session | Cipher suite | Privilege |
| --- | --- | --- | --- |
| `false` (recommended) | Get Channel Cipher Suites, then Get Channel Authentication Capabilities | Picked from the BMC's list, [by position](preparing-the-bmc.html#how-ipmiclient-chooses-the-suite) | User |
| `true` | Nothing: the session is opened directly | Always **3** (RAKP-HMAC-SHA1, HMAC-SHA1-96, AES-CBC-128) | User |

Use `true` to force suite 3 on a BMC whose suite list would make the position rule pick a suite
the client does not implement, or to save two round trips per call.

### Timeout

`timeout` is the **overall deadline of each `IpmiClient` call, in seconds**: opening the session,
every command, and closing the session. When it expires, the call is cancelled and throws
`java.util.concurrent.TimeoutException`. Walking a large SDR repository or reading many FRUs can
take tens of seconds on a slow BMC: 120 s is a safe value.

`getFrusAndSensorsAsStringResult()` makes two calls (FRUs, then sensors), each with this
deadline, so it can take up to twice the timeout.

The timeout of each **message** is a different setting, 5 minutes by default; see
[Timeouts and Errors](timeouts-and-errors.html).

### Keep-alive

While a session is open, the client sends a no-op message (Get Channel Authentication
Capabilities) every `pingPeriod` **milliseconds**, so that the BMC does not close the session for
inactivity during a long collection.

| `pingPeriod` | Behavior |
| --- | --- |
| `-1` (default) | The `pingPeriod` of [`connection.properties`](timeouts-and-errors.html#library-wide-defaults): 30 000 ms |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore the actual default keep-alive behavior

Fresh evidence after the earlier fix is that the final tree restores the claim that -1 uses the 30-second property value, while AbstractIpmiRunner passes -1 to the two-argument connector and ConnectionManager(int, long) overwrites the property-derived value with -1; Connection.connect() then starts a timer only for positive values. Thus the default sends no keep-alive, and a collection longer than the BMC's inactivity timeout can lose its session unless callers explicitly set a positive period; either fix the constructor or document the effective behavior.

AGENTS.md reference: AGENTS.md:L35-L35

Useful? React with 👍 / 👎.

| `> 0` | One keep-alive message every `pingPeriod` ms |
| `0` (or any other negative value) | No keep-alive messages |

Each `IpmiClient` call opens its own session and closes it when it is done, so the keep-alive
only matters for calls that last longer than the BMC's session inactivity timeout (typically
60 s). Disable it (`0`) to keep the traffic to the strict minimum.

## Thread safety

`IpmiClient` methods are static and keep no state between calls: each call creates its own
connector, local UDP port, session and worker thread. Calls can run in parallel, for different
BMCs. Parallel sessions against the **same BMC** are not reliable: BMCs accept a limited number of
sessions and drop replies under load, and parallel sessions from one JVM lose far more replies
than the same sessions from separate processes
([#97](https://github.com/metricshub/ipmi-java/issues/97)). Query a given BMC from one thread at a
time.
Loading
Loading