Skip to content

feat(xo-server-ipmi-sensors) : Created the plugin#9724

Open
All-Ki wants to merge 1 commit intomasterfrom
feat/xo-server-ipmi-sensors
Open

feat(xo-server-ipmi-sensors) : Created the plugin#9724
All-Ki wants to merge 1 commit intomasterfrom
feat/xo-server-ipmi-sensors

Conversation

@All-Ki
Copy link
Copy Markdown

@All-Ki All-Ki commented Apr 15, 2026

Description

Move ipmi sensor discovery to it's own plugin and removed old logic, ships with a default configuration with the same behaviour as the old system.
The plugin can be configured via the UI, accepts an exact match for the vendor and regexes for the ipmi matching.

Tested via unit tests.

Tested with R620-L1 host on xo-lab by adding and removing Dell from configured vendors

Screenshots

{AF4DECC4-9536-436B-B0EA-F64C93DC8B48} {F315309F-981A-41EA-B1C8-234C03107914}

Checklist

  • Commit
    • Title follows commit conventions
    • Reference the relevant issue (Fixes #007, See xoa-support#42, See https://...)
    • If bug fix, add Introduced by
  • Changelog
    • If visible by XOA users, add changelog entry
    • Update "Packages to release" in CHANGELOG.unreleased.md
  • PR
    • If UI changes, add screenshots
    • If not finished or not tested, open as Draft

Review process

If you are an external contributor, you can skip this part. Simply create the pull request, and we'll get back to you as soon as possible.

This 2-passes review process aims to:

  • develop skills of junior reviewers
  • limit the workload for senior reviewers
  • limit the number of unnecessary changes by the author
  1. The author creates a PR.
  2. Review process:
    1. The author assigns the junior reviewer.
    2. The junior reviewer conducts their review:
      • Resolves their comments if they are addressed.
      • Adds comments if necessary or approves the PR.
    3. The junior reviewer assigns the senior reviewer.
    4. The senior reviewer conducts their review:
      • If there are no unresolved comments on the PR → merge.
      • Otherwise, we continue with 3.
  3. The author responds to comments and/or makes corrections, and we go back to 2.

Notes:

  1. The author can request a review at any time, even if the PR is still a Draft.
  2. In theory, there should not be more than one reviewer at a time.
  3. The author should not make any changes:
    • When a reviewer is assigned.
    • Between the junior and senior reviews.

@All-Ki All-Ki requested a review from spacotte-vates April 15, 2026 09:46
@All-Ki All-Ki force-pushed the feat/xo-server-ipmi-sensors branch 3 times, most recently from 07394cb to 22e797a Compare April 15, 2026 10:29
@All-Ki All-Ki marked this pull request as ready for review April 15, 2026 11:35
Comment thread packages/xo-server-ipmi-sensors/src/index.mts Outdated
Comment thread packages/xo-server-ipmi-sensors/src/index.mts Outdated
Comment thread packages/xo-server-ipmi-sensors/src/index.mts Outdated
Comment on lines +143 to +145
const callIpmiPlugin = async function <T>(fn: string): Promise<T> {
return await xApiHost.$call<T>('call_plugin', 'ipmitool.py', fn, {})
}
Copy link
Copy Markdown
Collaborator

@spacotte-vates spacotte-vates Apr 15, 2026

Choose a reason for hiding this comment

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

This is some complicated code to be able call ipmi plugin functions, I understand this is the existing solution but since there are not a lot of functions I think it would be better to either call them directly without this intermediary function:
await (xApiHost.$call('call_plugin', 'ipmitool.py`, 'is_ipmi_device_available, {})) as string

0r externalize them in a separate ipmi-tool.mts file:

function isIpmiDeviceAvailable(): Promise<string>{
    return xApiHost.$call('call_plugin', 'ipmitool.py`, 'is_ipmi_device_available, {})
}

Maybe this is outside of the card scope.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'd think that the best would be an external util across all XO rather than a file specific one, agree with the idea though i'd say out of scope

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fair enough, I just want to get the opinion of Florent before resolving.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this would also need arguments , for example in another part of the codebase

    const result = await this.call('host.call_plugin', this.pool.master, 'trim', 'do_trim', {
      sr_uuid: await this.getField('SR', srRef, 'uuid'),
    })
    ```
it will be tricky to type though, since we can be connected to multiple host/pools, and they can have different versions of the plugin 
 

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I guess that's an issue for another day then, and might not be worth it since it's not used a lot across the codebase

Comment thread packages/xo-server-ipmi-sensors/.USAGE.md Outdated
Comment thread packages/xo-server-ipmi-sensors/src/index.mts
Comment thread packages/xo-server-ipmi-sensors/src/default-rules.mts Outdated
Comment thread packages/xo-server-ipmi-sensors/package.json Outdated
Comment thread packages/xo-server-ipmi-sensors/src/ipmi-rules.mts Outdated
Comment thread packages/xo-server-ipmi-sensors/src/index.mts Outdated
Comment thread packages/xo-server-ipmi-sensors/README.md Outdated
@All-Ki All-Ki force-pushed the feat/xo-server-ipmi-sensors branch 6 times, most recently from 105774b to 17e496d Compare April 15, 2026 15:30
@spacotte-vates
Copy link
Copy Markdown
Collaborator

Do a MAJ+CTRL+F search for "IMPI", there are still some not fixed.

@spacotte-vates spacotte-vates self-requested a review April 16, 2026 08:13
@spacotte-vates
Copy link
Copy Markdown
Collaborator

I think you are also missing some function return types.

* Load and start the plugin.
*/
async load(): Promise<void> {
this.#removeApiMethod = (this.#xo as any).addApiMethod(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why as any?
this.#xo type is correct

}

addIpmiSensorDataType(ipmiSensor, productName, this.#configuredRulesByProduct)
const dataType = ipmiSensor.dataType as IMPI_SENSOR_DATA_TYPE_STRINGS
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Avoid to use as as much as possible.

Here, addIpmiSensorDataType always injects the dataType property into your ipmiSensor object.
You can type addIpmiSensorDataType correctly to reflect what the function does.

export const addIpmiSensorDataType: (
  data: ReturnedSensorData,
  productName: string,
  configuredRules: SensorRegexByProduct[]
) => asserts data is ReturnedSensorData & { dataType: IMPI_SENSOR_DATA_TYPE_STRINGS } = (
  data,
  productName,
  configuredRules
) => {. .. }

Or using function instead anonymous function

 export function addIpmiSensorDataType(
  data: ReturnedSensorData,
  productName: string,
  configuredRules: SensorRegexByProduct[]
): asserts data is ReturnedSensorData & { dataType: IMPI_SENSOR_DATA_TYPE_STRINGS } {...}

so, when you do addIpmiSensorDataType(foo, ...)
then foo.dataType will no longer be considered undefined

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Oh thanks for this one

},
}

export const addCustomIpmiSensors = (ipmiSensorsByDataType: any, productName: string) => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No any.
At least unknown if you can't type it. (or any but with a comment explaining why)
But based on the parameter name, I think it's possible to type it.

@All-Ki All-Ki force-pushed the feat/xo-server-ipmi-sensors branch 4 times, most recently from 0e719b4 to 4f063fc Compare April 17, 2026 12:58
@All-Ki All-Ki force-pushed the feat/xo-server-ipmi-sensors branch from 4f063fc to 1feb8b6 Compare April 17, 2026 13:05
Comment thread CHANGELOG.unreleased.md
- [i18n] Update Chinese (Simplified Han script), Czech, Danish, Dutch, Finnish, German, Italian, Korean, Norwegian, Persian, Polish, Portuguese, Portuguese (Brasil), Russian, Slovak and Spanish translations (PR [#9649](https://github.com/vatesfr/xen-orchestra/pull/9649))
- [OpenMetrics] Add 9 missing host RRD metrics: `hostload`, `memory_reclaimed`, `memory_reclaimed_max`, `running_vcpus`, `pif_aggr_rx`, `pif_aggr_tx`, `iops_total`, `io_throughput_total`, `latency` per SR (PR [#9696](https://github.com/vatesfr/xen-orchestra/pull/9696))
- [Netbox] Use platform hierarchy to assign versioned OS names (e.g. "Debian 12" instead of "Debian") when the major version is known (requires Netbox >= 4.4) #7773 (PR [#9644](https://github.com/vatesfr/xen-orchestra/pull/9644))
- [IPMI] Add a plugin for configuration of ipmi rules and vendors [#9724] (https://github.com/vatesfr/xen-orchestra/pull/9724)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- [IPMI] Add a plugin for configuration of ipmi rules and vendors [#9724] (https://github.com/vatesfr/xen-orchestra/pull/9724)
- [IPMI] Add a plugin for configuration of ipmi rules and vendors ( PR [#9724] (https://github.com/vatesfr/xen-orchestra/pull/9724))

Copy link
Copy Markdown
Collaborator

@fbeauchamp fbeauchamp left a comment

Choose a reason for hiding this comment

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

nice work, that's a very good first PR. Additionnally to my comments, there is a unknown at the bottom of the fields list. I don't understand what is its goal ?

result[key] = propSchema.default
hasDefaults = true
} else if (propSchema.type === 'object') {
const nested = getSchemaDefaults(propSchema)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we need this

Suggested change
const nested = getSchemaDefaults(propSchema)
const nested = this.getSchemaDefaults(propSchema)

this.getIpmiSensors.bind(this)
),
// replacement for the method in packages/xo-server/src/api/host.mjs
this.#xo.addApiMethod<[{ id: XoHost | Branded<'host'> }], FinalSensorData>(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the original method had a permission check and a resolve property
this is important to keep them

const match = str.match(/^\/(.+)\/([a-z]*)$/i)
if (!match) return undefined
const [, pattern, flags] = match
if (pattern === undefined) return undefined
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think a failed regexp compilation shouldn't fail silently. Since it will make the plugin unusable, I am ok with throwing an error here

const customSensorsByDataType = IPMI_SENSOR_CUSTOM_BY_DATA_TYPE_BY_SUPPORTED_PRODUCT_NAME[productName]
for (const dataType in customSensorsByDataType) {
const dataTypeKey = dataType as IMPI_SENSOR_DATA_TYPE_STRINGS
const fn = customSensorsByDataType[dataTypeKey]!! // we know this exists since we're iterating over the keys of customSensorsByDataType
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we only need on !

Suggested change
const fn = customSensorsByDataType[dataTypeKey]!! // we know this exists since we're iterating over the keys of customSensorsByDataType
const fn = customSensorsByDataType[dataTypeKey]! // we know this exists since we're iterating over the keys of customSensorsByDataType

if (systemManufacturer.includes('dell')) productName = 'dell'
if (systemManufacturer.includes('lenovo')) productName = 'lenovo'

const data = this.#configuredRulesByProduct || []
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think #configuredRulesByProduct can be undefined, since it's instantiated in the constructor and not accessible by exterior code

Suggested change
const data = this.#configuredRulesByProduct || []
const data = this.#configuredRulesByProduct

getIpmiSensors.resolve = {
host: ['id', 'host', 'administrate'],
}
// host.getApiMethod moved to packages/xo-server-ipmi-sensors/src/index.mts
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// host.getApiMethod moved to packages/xo-server-ipmi-sensors/src/index.mts
// host.getIpmiSensors moved to packages/xo-server-ipmi-sensors/src/index.mts

// ============================================================================

class IpmiSensorsPlugin {
#configuration: PluginConfiguration | undefined
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this looks unused, is it intended ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I wasnt sure if configuration is a required parameter or not, i thought it was automatically set by the framework

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants