From c278da254254151ac7dbc7b5c040e28364471193 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Thu, 2 Apr 2026 14:54:12 +0200 Subject: [PATCH 1/9] Simplify backward compat in kw having key value reference as value Add a datarecv func to manage backward compat. --- core/datarecv/keymeta.go | 21 ++++++++++++++++ .../text/kw/node/array.password,optional | 6 +++-- .../text/kw/node/array.password,required | 6 +++-- core/object/text/kw/node/hb.relay.password | 4 ++++ daemon/hb/hbrelay/main.go | 24 ++++--------------- drivers/arrayfreenas/main.go | 21 +++++----------- drivers/arrayhoc/main.go | 22 ++++++----------- drivers/arraypure/main.go | 21 +++++----------- 8 files changed, 57 insertions(+), 68 deletions(-) diff --git a/core/datarecv/keymeta.go b/core/datarecv/keymeta.go index e99f0d583..f9288b3d7 100644 --- a/core/datarecv/keymeta.go +++ b/core/datarecv/keymeta.go @@ -32,6 +32,27 @@ func ParseKeyMetaRelObj(line string, i any) (KeyMeta, error) { return ParseKeyMetaRel(line, o.Path().Namespace) } +// ParseKeyMetaRelWithFallback parses a key reference in the format "from key " +// and falls back to the legacy format "" with a default key name for backward compatibility. +func ParseKeyMetaRelWithFallback(line, namespace, defaultKey string) (KeyMeta, error) { + // Try new format first + km, err := ParseKeyMetaRel(line, namespace) + if err == nil { + return km, nil + } + + // Fall back to legacy format: just the path, using defaultKey + path, err := naming.ParsePath(line) + if err != nil { + return KeyMeta{}, err + } + return KeyMeta{ + Path: path, + From: namespace, + Key: defaultKey, + }, nil +} + func ParseKeyMetaRel(line, namespace string) (KeyMeta, error) { var ( km KeyMeta diff --git a/core/object/text/kw/node/array.password,optional b/core/object/text/kw/node/array.password,optional index 2125b7b53..02011919c 100644 --- a/core/object/text/kw/node/array.password,optional +++ b/core/object/text/kw/node/array.password,optional @@ -1,5 +1,7 @@ -The password to use to log in, expressed as a datastore reference: +The password to use to log in, expressed as a datastore reference. - from // key +Value format: +- New format: `from // key ` +- Legacy format: `//` (uses default key "password") Array passwords are usually stored in sec datastores like `system/sec/`. diff --git a/core/object/text/kw/node/array.password,required b/core/object/text/kw/node/array.password,required index 9245873e1..02011919c 100644 --- a/core/object/text/kw/node/array.password,required +++ b/core/object/text/kw/node/array.password,required @@ -1,5 +1,7 @@ -The password to use to log in, expressed as a datastore reference: +The password to use to log in, expressed as a datastore reference. - from // key +Value format: +- New format: `from // key ` +- Legacy format: `//` (uses default key "password") Array passwords are usually stored in sec datastores like `system/sec/`. diff --git a/core/object/text/kw/node/hb.relay.password b/core/object/text/kw/node/hb.relay.password index 2657ddfc7..7b6611f33 100644 --- a/core/object/text/kw/node/hb.relay.password +++ b/core/object/text/kw/node/hb.relay.password @@ -1 +1,5 @@ A datastore key reference to a password used to authenticate with the relay API. + +Value format: +- New format: `from // key ` +- Legacy format: `//` (uses default key "password") diff --git a/daemon/hb/hbrelay/main.go b/daemon/hb/hbrelay/main.go index 04eaf5612..3eafa4a50 100644 --- a/daemon/hb/hbrelay/main.go +++ b/daemon/hb/hbrelay/main.go @@ -15,7 +15,6 @@ import ( "github.com/opensvc/om3/v3/core/datarecv" "github.com/opensvc/om3/v3/core/hbcfg" "github.com/opensvc/om3/v3/core/naming" - "github.com/opensvc/om3/v3/core/object" "github.com/opensvc/om3/v3/util/hostname" "github.com/opensvc/om3/v3/util/key" "github.com/opensvc/om3/v3/util/plog" @@ -96,27 +95,14 @@ func (t *T) Configure(ctx context.Context) { func (t *T) password() (string, error) { value := t.GetString("password") - km, err := datarecv.ParseKeyMetaRel(value, naming.NsSys) - if err != nil { - return t.backwardCompatPassword(value) - } - b, err := km.Decode() + // Parse key reference with backward compatibility + // New format: password = from system/sec/relay key password + // Old format: password = system/sec/relay (uses default key "password") + km, err := datarecv.ParseKeyMetaRelWithFallback(value, naming.NsSys, "password") if err != nil { return "", err } - return string(b), nil -} - -func (t *T) backwardCompatPassword(secName string) (string, error) { - secPath, err := naming.ParsePath(secName) - if err != nil { - return "", err - } - sec, err := object.NewSec(secPath, object.WithVolatile(true)) - if err != nil { - return "", err - } - b, err := sec.DecodeKey("password") + b, err := km.Decode() if err != nil { return "", err } diff --git a/drivers/arrayfreenas/main.go b/drivers/arrayfreenas/main.go index 45474b1ee..d119d9ed3 100644 --- a/drivers/arrayfreenas/main.go +++ b/drivers/arrayfreenas/main.go @@ -1036,21 +1036,12 @@ func (t Array) password() (string, error) { if err != nil { return "", err } - if len(strings.Fields(s)) == 1 { - // old format: password = system/sec/array1 - path, err := naming.ParsePath(s) - if err != nil { - return "", err - } - km.Path = path - km.From = naming.NsSys - km.Key = "password" - } else { - // new format: password = from system/sec/array1 key password - km, err = datarecv.ParseKeyMetaRel(s, naming.NsSys) - if err != nil { - return "", err - } + // Parse key reference with backward compatibility + // New format: password = from system/sec/array1 key password + // Old format: password = system/sec/array1 (uses default key "password") + km, err = datarecv.ParseKeyMetaRelWithFallback(s, naming.NsSys, "password") + if err != nil { + return "", err } b, err := km.RootDecode() if err != nil { diff --git a/drivers/arrayhoc/main.go b/drivers/arrayhoc/main.go index 676142ec6..26a0f8786 100644 --- a/drivers/arrayhoc/main.go +++ b/drivers/arrayhoc/main.go @@ -852,21 +852,13 @@ func (t Array) password() (string, error) { if err != nil { return "", err } - if len(strings.Fields(s)) == 1 { - // old format: password = system/sec/array1 - path, err := naming.ParsePath(s) - if err != nil { - return "", err - } - km.Path = path - km.From = naming.NsSys - km.Key = "password" - } else { - // new format: password = from system/sec/array1 key password - km, err = datarecv.ParseKeyMetaRel(s, naming.NsSys) - if err != nil { - return "", err - } + + // Parse key reference with backward compatibility + // New format: password = from system/sec/array1 key password + // Old format: password = system/sec/array1 (uses default key "password") + km, err = datarecv.ParseKeyMetaRelWithFallback(s, naming.NsSys, "password") + if err != nil { + return "", err } b, err := km.RootDecode() if err != nil { diff --git a/drivers/arraypure/main.go b/drivers/arraypure/main.go index 0553996f1..32c24933d 100644 --- a/drivers/arraypure/main.go +++ b/drivers/arraypure/main.go @@ -785,21 +785,12 @@ func (t *Array) privateKey() ([]byte, error) { if err != nil { return nil, err } - if len(strings.Fields(s)) == 1 { - // old format: private_key = system/sec/array1 - path, err := naming.ParsePath(s) - if err != nil { - return nil, err - } - km.Path = path - km.From = naming.NsSys - km.Key = "private_key" - } else { - // new format: private_key = from system/sec/array1 key password - km, err = datarecv.ParseKeyMetaRel(s, naming.NsSys) - if err != nil { - return nil, err - } + // Parse key reference with backward compatibility + // New format: private_key = from system/sec/array1 key private_key + // Old format: private_key = system/sec/array1 (uses default key "private_key") + km, err = datarecv.ParseKeyMetaRelWithFallback(s, naming.NsSys, "private_key") + if err != nil { + return nil, err } return km.RootDecode() } From 7c8bb10d7d38008f85138d85c30684da48e21697 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Thu, 2 Apr 2026 21:32:27 +0200 Subject: [PATCH 2/9] Fix disk.md sync resync action * don't use the uuid-base devpath, not supported by mdadm --re-add * use the realpath of the devpath in the --re-add command * log the "succeed" re-add stderr message at the info level instead of error. --- util/md/main.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/util/md/main.go b/util/md/main.go index 053e0ebca..2bb14d8a9 100644 --- a/util/md/main.go +++ b/util/md/main.go @@ -157,7 +157,7 @@ func (t T) Resync(ctx context.Context) error { } } if removed > added { - return fmt.Errorf("no faulty device found to re-add to %s remaining %d removed legs", t.devpathFromUUID(), removed-added) + return fmt.Errorf("no faulty device found to re-add to %s remaining %d removed legs", t.devpathFromName(), removed-added) } return nil } @@ -265,7 +265,8 @@ func (t T) UUID() string { } func (t T) reAdd(ctx context.Context, devpath string) error { - args := []string{"--re-add", t.devpathFromUUID(), devpath} + mdDevPath := t.devpathFromName() + args := []string{"--re-add", mdDevPath, devpath} cmd := command.New( command.WithContext(ctx), command.WithName(mdadm), @@ -273,7 +274,13 @@ func (t T) reAdd(ctx context.Context, devpath string) error { command.WithLogger(t.log), command.WithCommandLogLevel(zerolog.InfoLevel), command.WithStdoutLogLevel(zerolog.InfoLevel), - command.WithStderrLogLevel(zerolog.ErrorLevel), + command.WithOnStderrLine(func(s string) { + if strings.HasSuffix(s, "succeed") { + t.log.Infof("%s", s) + return + } + t.log.Errorf(s) + }), ) cmd.Run() fcache.Clear("mdadm-E-scan-v") @@ -281,7 +288,7 @@ func (t T) reAdd(ctx context.Context, devpath string) error { case 0: // ok default: - return fmt.Errorf("failed to re-add %s to %s", devpath, t.devpathFromUUID()) + return fmt.Errorf("failed to re-add %s to %s", devpath, mdDevPath) } return nil } From 064f77f41eae7641fac02056c6f1923237fc4307 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Fri, 3 Apr 2026 14:43:54 +0200 Subject: [PATCH 3/9] Add the "o[mx] node scsi scan" command And the `POST /api/node/name//action/scsi/scan` handler $ om node scsi scan -h Scan the scsi hosts in search of new disks. This command scans SCSI hosts for new block devices. You can specify specific HBA, target, and LUN to scan. Usage: om node scsi scan [flags] Aliases: scan, sca, sc Flags: --hba string Specify a hba to scan for new block devices. Example: 5001438002432430 or iqn.1993-08.org.debian:01:659b4bbd68bd. --lun string Specify a logical unit number to scan for new block devices. Example: 1. --node string submit the action to the selected nodes --target string Specify a target to scan for new block devices. Example: 5000097358185088 or iqn.clementine.tgt1. ... --- core/commoncmd/flags.go | 12 + core/object/node.go | 7 + core/om/factory.go | 22 + core/om/node.go | 8 + core/omcmd/node_scsi_scan.go | 69 ++ core/ox/factory.go | 22 + core/ox/node.go | 8 + core/oxcmd/node_scsi_scan.go | 60 ++ daemon/api/api.yaml | 54 ++ daemon/api/codegen_client_gen.go | 203 ++++++ daemon/api/codegen_server_gen.go | 605 ++++++++++-------- daemon/api/codegen_type_gen.go | 23 + .../daemonapi/post_node_action_scsi_scan.go | 50 ++ util/scsi/linux.go | 179 +++++- 14 files changed, 1045 insertions(+), 277 deletions(-) create mode 100644 core/omcmd/node_scsi_scan.go create mode 100644 core/oxcmd/node_scsi_scan.go create mode 100644 daemon/daemonapi/post_node_action_scsi_scan.go diff --git a/core/commoncmd/flags.go b/core/commoncmd/flags.go index 49e085fb5..765382be5 100644 --- a/core/commoncmd/flags.go +++ b/core/commoncmd/flags.go @@ -522,3 +522,15 @@ func HiddenFlagObjectSelector(flags *pflag.FlagSet, p *string) { FlagObjectSelector(flags, p) flags.MarkHidden("selector") } + +func FlagSCSIHBA(flags *pflag.FlagSet, p *string) { + flags.StringVar(p, "hba", "", "Specify a hba to scan for new block devices. Example: 5001438002432430 or iqn.1993-08.org.debian:01:659b4bbd68bd.") +} + +func FlagSCSITarget(flags *pflag.FlagSet, p *string) { + flags.StringVar(p, "target", "", "Specify a target to scan for new block devices. Example: 5000097358185088 or iqn.clementine.tgt1.") +} + +func FlagSCSILUN(flags *pflag.FlagSet, p *string) { + flags.StringVar(p, "lun", "", "Specify a logical unit number to scan for new block devices. Example: 1.") +} diff --git a/core/object/node.go b/core/object/node.go index edca2f6d3..da24df040 100644 --- a/core/object/node.go +++ b/core/object/node.go @@ -1,6 +1,7 @@ package object import ( + "context" "fmt" "github.com/google/uuid" @@ -9,6 +10,7 @@ import ( "github.com/opensvc/om3/v3/core/xconfig" "github.com/opensvc/om3/v3/util/funcopt" "github.com/opensvc/om3/v3/util/plog" + "github.com/opensvc/om3/v3/util/scsi" ) type ( @@ -71,3 +73,8 @@ func (t *Node) SetVolatile(v bool) { func (t Node) IsVolatile() bool { return t.volatile } + +// ScanSCSI scans the SCSI hosts in search of new disks +func (t Node) ScanSCSI(ctx context.Context, hba, target, lun string) error { + return scsi.ScanSCSIHosts(hba, target, lun) +} diff --git a/core/om/factory.go b/core/om/factory.go index 2b50f7f13..1356d400b 100644 --- a/core/om/factory.go +++ b/core/om/factory.go @@ -522,6 +522,28 @@ installed software to be discovered without restarting the daemon.`, return cmd } +func newCmdNodeSCSIScan() *cobra.Command { + var options commands.CmdNodeSCSIScan + cmd := &cobra.Command{ + Use: "scan", + Short: "scan the scsi hosts in search of new disks", + Aliases: []string{"sca", "sc"}, + Long: `Scan the scsi hosts in search of new disks. + +This command scans SCSI hosts for new block devices. You can specify specific HBA, target, and LUN to scan.`, + RunE: func(cmd *cobra.Command, args []string) error { + return options.Run() + }, + } + flags := cmd.Flags() + addFlagsGlobal(flags, &options.OptsGlobal) + commoncmd.FlagNodeSelector(flags, &options.NodeSelector) + commoncmd.FlagSCSIHBA(flags, &options.HBA) + commoncmd.FlagSCSITarget(flags, &options.Target) + commoncmd.FlagSCSILUN(flags, &options.LUN) + return cmd +} + func newCmdNodeChecks() *cobra.Command { var options commands.CmdNodeChecks cmd := &cobra.Command{ diff --git a/core/om/node.go b/core/om/node.go index eaf604f7d..313135226 100644 --- a/core/om/node.go +++ b/core/om/node.go @@ -54,6 +54,10 @@ var ( Short: "configuration commands", Aliases: []string{"conf", "c", "cf", "cfg"}, } + cmdNodeSCSI = &cobra.Command{ + Use: "scsi", + Short: "scsi subsystem commands", + } cmdNodePrint = &cobra.Command{ Use: "print", Hidden: true, @@ -163,6 +167,7 @@ func init() { cmdNodePush, cmdNodeRelay, cmdNodeScan, + cmdNodeSCSI, cmdNodeSchedule, cmdNodeSSH, cmdNodeUpdate, @@ -216,6 +221,9 @@ func init() { cmdNodeScan.AddCommand( newCmdNodeScanCapabilities(), ) + cmdNodeSCSI.AddCommand( + newCmdNodeSCSIScan(), + ) cmdNodeSchedule.AddCommand( newCmdNodeScheduleList(), ) diff --git a/core/omcmd/node_scsi_scan.go b/core/omcmd/node_scsi_scan.go new file mode 100644 index 000000000..2d61810b4 --- /dev/null +++ b/core/omcmd/node_scsi_scan.go @@ -0,0 +1,69 @@ +package omcmd + +import ( + "context" + "fmt" + + "github.com/opensvc/om3/v3/core/client" + "github.com/opensvc/om3/v3/core/nodeaction" + "github.com/opensvc/om3/v3/core/object" + "github.com/opensvc/om3/v3/daemon/api" + "github.com/opensvc/om3/v3/util/xsession" +) + +type ( + CmdNodeSCSIScan struct { + OptsGlobal + NodeSelector string + HBA string + Target string + LUN string + } +) + +func (t *CmdNodeSCSIScan) Run() error { + return nodeaction.New( + nodeaction.WithFormat(t.Output), + nodeaction.WithColor(t.Color), + nodeaction.WithRemoteNodes(t.NodeSelector), + nodeaction.WithLocalFunc(func() (interface{}, error) { + n, err := object.NewNode() + if err != nil { + return nil, err + } + ctx := context.Background() + return nil, n.ScanSCSI(ctx, t.HBA, t.Target, t.LUN) + }), + nodeaction.WithRemoteFunc(func(ctx context.Context, nodename string) (interface{}, error) { + c, err := client.New() + if err != nil { + return nil, err + } + params := api.PostNodeActionSCSIScanParams{ + Hba: &t.HBA, + Target: &t.Target, + Lun: &t.LUN, + } + { + sid := xsession.ID + params.RequesterSid = &sid + } + response, err := c.PostNodeActionSCSIScanWithResponse(ctx, nodename, ¶ms) + if err != nil { + return nil, err + } + switch { + case response.JSON200 != nil: + return *response.JSON200, nil + case response.JSON401 != nil: + return nil, fmt.Errorf("node %s: %s", nodename, *response.JSON401) + case response.JSON403 != nil: + return nil, fmt.Errorf("node %s: %s", nodename, *response.JSON403) + case response.JSON500 != nil: + return nil, fmt.Errorf("node %s: %s", nodename, *response.JSON500) + default: + return nil, fmt.Errorf("node %s: unexpected response: %s", nodename, response.Status()) + } + }), + ).Do() +} diff --git a/core/ox/factory.go b/core/ox/factory.go index f40e6df97..43a001da8 100644 --- a/core/ox/factory.go +++ b/core/ox/factory.go @@ -426,6 +426,28 @@ installed software to be discovered without restarting the daemon.`, return cmd } +func newCmdNodeSCSIScan() *cobra.Command { + var options commands.CmdNodeSCSIScan + cmd := &cobra.Command{ + Use: "scan", + Short: "scan the scsi hosts in search of new disks", + Aliases: []string{"sca", "sc"}, + Long: `Scan the scsi hosts in search of new disks. + +This command scans SCSI hosts for new block devices. You can specify specific HBA, target, and LUN to scan.`, + RunE: func(cmd *cobra.Command, args []string) error { + return options.Run() + }, + } + flags := cmd.Flags() + addFlagsGlobal(flags, &options.OptsGlobal) + commoncmd.FlagNodeSelector(flags, &options.NodeSelector) + commoncmd.FlagSCSIHBA(flags, &options.HBA) + commoncmd.FlagSCSITarget(flags, &options.Target) + commoncmd.FlagSCSILUN(flags, &options.LUN) + return cmd +} + func newCmdNodeChecks() *cobra.Command { var options commands.CmdNodeChecks cmd := &cobra.Command{ diff --git a/core/ox/node.go b/core/ox/node.go index 3dad869d2..f76eaaa5e 100644 --- a/core/ox/node.go +++ b/core/ox/node.go @@ -18,6 +18,10 @@ var ( Short: "node collector data management commands", Aliases: []string{"coll"}, } + cmdNodeSCSI = &cobra.Command{ + Use: "scsi", + Short: "scsi subsystem commands", + } cmdNodeCollectorTag = &cobra.Command{ Use: "tag", Short: "collector tags management commands", @@ -86,6 +90,10 @@ func init() { newCmdNodeCapabilitiesList(), newCmdNodeCapabilitiesScan(), ) + cmdNode.AddCommand(cmdNodeSCSI) + cmdNodeSCSI.AddCommand( + newCmdNodeSCSIScan(), + ) cmdNode.AddCommand(cmdNodeCollector) cmdNodeCollector.AddCommand(cmdNodeCollectorTag) cmdNodeCollectorTag.AddCommand( diff --git a/core/oxcmd/node_scsi_scan.go b/core/oxcmd/node_scsi_scan.go new file mode 100644 index 000000000..44dade46a --- /dev/null +++ b/core/oxcmd/node_scsi_scan.go @@ -0,0 +1,60 @@ +package oxcmd + +import ( + "context" + "fmt" + + "github.com/opensvc/om3/v3/core/client" + "github.com/opensvc/om3/v3/core/nodeaction" + "github.com/opensvc/om3/v3/daemon/api" + "github.com/opensvc/om3/v3/util/xsession" +) + +type ( + CmdNodeSCSIScan struct { + OptsGlobal + NodeSelector string + HBA string + Target string + LUN string + } +) + +func (t *CmdNodeSCSIScan) Run() error { + return nodeaction.New( + nodeaction.WithFormat(t.Output), + nodeaction.WithColor(t.Color), + nodeaction.WithRemoteNodes(t.NodeSelector), + nodeaction.WithRemoteFunc(func(ctx context.Context, nodename string) (interface{}, error) { + c, err := client.New() + if err != nil { + return nil, err + } + params := api.PostNodeActionSCSIScanParams{ + Hba: &t.HBA, + Target: &t.Target, + Lun: &t.LUN, + } + { + sid := xsession.ID + params.RequesterSid = &sid + } + response, err := c.PostNodeActionSCSIScanWithResponse(ctx, nodename, ¶ms) + if err != nil { + return nil, err + } + switch { + case response.JSON200 != nil: + return *response.JSON200, nil + case response.JSON401 != nil: + return nil, fmt.Errorf("node %s: %s", nodename, *response.JSON401) + case response.JSON403 != nil: + return nil, fmt.Errorf("node %s: %s", nodename, *response.JSON403) + case response.JSON500 != nil: + return nil, fmt.Errorf("node %s: %s", nodename, *response.JSON500) + default: + return nil, fmt.Errorf("node %s: unexpected response: %s", nodename, response.Status()) + } + }), + ).Do() +} diff --git a/daemon/api/api.yaml b/daemon/api/api.yaml index 5ec4f3e4c..70eb61f9e 100644 --- a/daemon/api/api.yaml +++ b/daemon/api/api.yaml @@ -1033,6 +1033,36 @@ paths: 500: $ref: '#/components/responses/500' + /api/node/name/{nodename}/action/scsi/scan: + post: + operationId: PostNodeActionSCSIScan + tags: + - node + security: + - basicAuth: [] + - bearerAuth: [] + description: | + Scan the scsi hosts in search of new disks. + parameters: + - $ref: '#/components/parameters/inPathNodeName' + - $ref: '#/components/parameters/inQueryRequesterSid' + - $ref: '#/components/parameters/inQueryHBA' + - $ref: '#/components/parameters/inQueryTarget' + - $ref: '#/components/parameters/inQueryLUN' + responses: + 200: + content: + application/json: + schema: + $ref: '#/components/schemas/NodeActionAccepted' + description: OK + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 500: + $ref: '#/components/responses/500' + /api/node/name/{nodename}/action/sysreport: post: operationId: PostNodeActionSysreport @@ -8176,6 +8206,30 @@ components: type: string format: uuid + inQueryHBA: + in: query + name: hba + description: Specify a hba to scan for new block devices. + schema: + type: string + example: 5001438002432430 + + inQueryTarget: + in: query + name: target + description: Specify a target to scan for new block devices. + schema: + type: string + example: 5000097358185088 + + inQueryLUN: + in: query + name: lun + description: Specify a logical unit number to scan for new block devices. + schema: + type: string + example: "1" + inQueryResourceFileName: in: query name: name diff --git a/daemon/api/codegen_client_gen.go b/daemon/api/codegen_client_gen.go index 2b10fc4a3..a66db1fac 100644 --- a/daemon/api/codegen_client_gen.go +++ b/daemon/api/codegen_client_gen.go @@ -192,6 +192,9 @@ type ClientInterface interface { // PostNodeActionScanCapabilities request PostNodeActionScanCapabilities(ctx context.Context, nodename InPathNodeName, params *PostNodeActionScanCapabilitiesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostNodeActionSCSIScan request + PostNodeActionSCSIScan(ctx context.Context, nodename InPathNodeName, params *PostNodeActionSCSIScanParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostNodeActionSysreport request PostNodeActionSysreport(ctx context.Context, nodename InPathNodeName, params *PostNodeActionSysreportParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -963,6 +966,18 @@ func (c *Client) PostNodeActionScanCapabilities(ctx context.Context, nodename In return c.Client.Do(req) } +func (c *Client) PostNodeActionSCSIScan(ctx context.Context, nodename InPathNodeName, params *PostNodeActionSCSIScanParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostNodeActionSCSIScanRequest(c.Server, nodename, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostNodeActionSysreport(ctx context.Context, nodename InPathNodeName, params *PostNodeActionSysreportParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostNodeActionSysreportRequest(c.Server, nodename, params) if err != nil { @@ -4065,6 +4080,110 @@ func NewPostNodeActionScanCapabilitiesRequest(server string, nodename InPathNode return req, nil } +// NewPostNodeActionSCSIScanRequest generates requests for PostNodeActionSCSIScan +func NewPostNodeActionSCSIScanRequest(server string, nodename InPathNodeName, params *PostNodeActionSCSIScanParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "nodename", nodename, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/node/name/%s/action/scsi/scan", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.RequesterSid != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "requester_sid", *params.RequesterSid, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "uuid"}); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Hba != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "hba", *params.Hba, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Target != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "target", *params.Target, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Lun != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "lun", *params.Lun, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("POST", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewPostNodeActionSysreportRequest generates requests for PostNodeActionSysreport func NewPostNodeActionSysreportRequest(server string, nodename InPathNodeName, params *PostNodeActionSysreportParams) (*http.Request, error) { var err error @@ -12508,6 +12627,9 @@ type ClientWithResponsesInterface interface { // PostNodeActionScanCapabilitiesWithResponse request PostNodeActionScanCapabilitiesWithResponse(ctx context.Context, nodename InPathNodeName, params *PostNodeActionScanCapabilitiesParams, reqEditors ...RequestEditorFn) (*PostNodeActionScanCapabilitiesResponse, error) + // PostNodeActionSCSIScanWithResponse request + PostNodeActionSCSIScanWithResponse(ctx context.Context, nodename InPathNodeName, params *PostNodeActionSCSIScanParams, reqEditors ...RequestEditorFn) (*PostNodeActionSCSIScanResponse, error) + // PostNodeActionSysreportWithResponse request PostNodeActionSysreportWithResponse(ctx context.Context, nodename InPathNodeName, params *PostNodeActionSysreportParams, reqEditors ...RequestEditorFn) (*PostNodeActionSysreportResponse, error) @@ -13708,6 +13830,31 @@ func (r PostNodeActionScanCapabilitiesResponse) StatusCode() int { return 0 } +type PostNodeActionSCSIScanResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *NodeActionAccepted + JSON401 *N401 + JSON403 *N403 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostNodeActionSCSIScanResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostNodeActionSCSIScanResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type PostNodeActionSysreportResponse struct { Body []byte HTTPResponse *http.Response @@ -16949,6 +17096,15 @@ func (c *ClientWithResponses) PostNodeActionScanCapabilitiesWithResponse(ctx con return ParsePostNodeActionScanCapabilitiesResponse(rsp) } +// PostNodeActionSCSIScanWithResponse request returning *PostNodeActionSCSIScanResponse +func (c *ClientWithResponses) PostNodeActionSCSIScanWithResponse(ctx context.Context, nodename InPathNodeName, params *PostNodeActionSCSIScanParams, reqEditors ...RequestEditorFn) (*PostNodeActionSCSIScanResponse, error) { + rsp, err := c.PostNodeActionSCSIScan(ctx, nodename, params, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostNodeActionSCSIScanResponse(rsp) +} + // PostNodeActionSysreportWithResponse request returning *PostNodeActionSysreportResponse func (c *ClientWithResponses) PostNodeActionSysreportWithResponse(ctx context.Context, nodename InPathNodeName, params *PostNodeActionSysreportParams, reqEditors ...RequestEditorFn) (*PostNodeActionSysreportResponse, error) { rsp, err := c.PostNodeActionSysreport(ctx, nodename, params, reqEditors...) @@ -19732,6 +19888,53 @@ func ParsePostNodeActionScanCapabilitiesResponse(rsp *http.Response) (*PostNodeA return response, nil } +// ParsePostNodeActionSCSIScanResponse parses an HTTP response from a PostNodeActionSCSIScanWithResponse call +func ParsePostNodeActionSCSIScanResponse(rsp *http.Response) (*PostNodeActionSCSIScanResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostNodeActionSCSIScanResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest NodeActionAccepted + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest N403 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParsePostNodeActionSysreportResponse parses an HTTP response from a PostNodeActionSysreportWithResponse call func ParsePostNodeActionSysreportResponse(rsp *http.Response) (*PostNodeActionSysreportResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/daemon/api/codegen_server_gen.go b/daemon/api/codegen_server_gen.go index a84771dbc..e21d4b98f 100644 --- a/daemon/api/codegen_server_gen.go +++ b/daemon/api/codegen_server_gen.go @@ -120,6 +120,9 @@ type ServerInterface interface { // (POST /api/node/name/{nodename}/action/scan/capabilities) PostNodeActionScanCapabilities(ctx echo.Context, nodename InPathNodeName, params PostNodeActionScanCapabilitiesParams) error + // (POST /api/node/name/{nodename}/action/scsi/scan) + PostNodeActionSCSIScan(ctx echo.Context, nodename InPathNodeName, params PostNodeActionSCSIScanParams) error + // (POST /api/node/name/{nodename}/action/sysreport) PostNodeActionSysreport(ctx echo.Context, nodename InPathNodeName, params PostNodeActionSysreportParams) error @@ -1270,6 +1273,56 @@ func (w *ServerInterfaceWrapper) PostNodeActionScanCapabilities(ctx echo.Context return err } +// PostNodeActionSCSIScan converts echo context to params. +func (w *ServerInterfaceWrapper) PostNodeActionSCSIScan(ctx echo.Context) error { + var err error + // ------------- Path parameter "nodename" ------------- + var nodename InPathNodeName + + err = runtime.BindStyledParameterWithOptions("simple", "nodename", ctx.Param("nodename"), &nodename, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter nodename: %s", err)) + } + + ctx.Set(BasicAuthScopes, []string{}) + + ctx.Set(BearerAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params PostNodeActionSCSIScanParams + // ------------- Optional query parameter "requester_sid" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "requester_sid", ctx.QueryParams(), ¶ms.RequesterSid, runtime.BindQueryParameterOptions{Type: "string", Format: "uuid"}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter requester_sid: %s", err)) + } + + // ------------- Optional query parameter "hba" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "hba", ctx.QueryParams(), ¶ms.Hba, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter hba: %s", err)) + } + + // ------------- Optional query parameter "target" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "target", ctx.QueryParams(), ¶ms.Target, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter target: %s", err)) + } + + // ------------- Optional query parameter "lun" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "lun", ctx.QueryParams(), ¶ms.Lun, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter lun: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostNodeActionSCSIScan(ctx, nodename, params) + return err +} + // PostNodeActionSysreport converts echo context to params. func (w *ServerInterfaceWrapper) PostNodeActionSysreport(ctx echo.Context) error { var err error @@ -6159,6 +6212,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.POST(baseURL+"/api/node/name/:nodename/action/push/patch", wrapper.PostNodeActionPushPatch) router.POST(baseURL+"/api/node/name/:nodename/action/push/pkg", wrapper.PostNodeActionPushPkg) router.POST(baseURL+"/api/node/name/:nodename/action/scan/capabilities", wrapper.PostNodeActionScanCapabilities) + router.POST(baseURL+"/api/node/name/:nodename/action/scsi/scan", wrapper.PostNodeActionSCSIScan) router.POST(baseURL+"/api/node/name/:nodename/action/sysreport", wrapper.PostNodeActionSysreport) router.POST(baseURL+"/api/node/name/:nodename/action/unfreeze", wrapper.PostPeerActionUnfreeze) router.GET(baseURL+"/api/node/name/:nodename/capabilities", wrapper.GetNodeCapabilities) @@ -6277,284 +6331,287 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/api/codegen_type_gen.go b/daemon/api/codegen_type_gen.go index e9e07dc4e..70217f9e5 100644 --- a/daemon/api/codegen_type_gen.go +++ b/daemon/api/codegen_type_gen.go @@ -2406,6 +2406,9 @@ type InQueryForce = bool // InQueryGreetTimeout defines model for inQueryGreetTimeout. type InQueryGreetTimeout = string +// InQueryHBA defines model for inQueryHBA. +type InQueryHBA = string + // InQueryImpersonate The node name to impersonate when evaluating a keyword. Setting impersonate without evaluate=true returns a Bad Request error. type InQueryImpersonate = string @@ -2418,6 +2421,9 @@ type Names = []string // InQueryKeywords defines model for inQueryKeywords. type InQueryKeywords = []string +// InQueryLUN defines model for inQueryLUN. +type InQueryLUN = string + // InQueryLeader defines model for inQueryLeader. type InQueryLeader = bool @@ -2469,6 +2475,9 @@ type InQuerySubset = string // InQueryTag defines model for inQueryTag. type InQueryTag = string +// InQueryTarget defines model for inQueryTarget. +type InQueryTarget = string + // InQueryTo defines model for inQueryTo. type InQueryTo = string @@ -2648,6 +2657,20 @@ type PostNodeActionScanCapabilitiesParams struct { RequesterSid *InQueryRequesterSid `form:"requester_sid,omitempty" json:"requester_sid,omitempty"` } +// PostNodeActionSCSIScanParams defines parameters for PostNodeActionSCSIScan. +type PostNodeActionSCSIScanParams struct { + RequesterSid *InQueryRequesterSid `form:"requester_sid,omitempty" json:"requester_sid,omitempty"` + + // Hba Specify a hba to scan for new block devices. + Hba *InQueryHBA `form:"hba,omitempty" json:"hba,omitempty"` + + // Target Specify a target to scan for new block devices. + Target *InQueryTarget `form:"target,omitempty" json:"target,omitempty"` + + // Lun Specify a logical unit number to scan for new block devices. + Lun *InQueryLUN `form:"lun,omitempty" json:"lun,omitempty"` +} + // PostNodeActionSysreportParams defines parameters for PostNodeActionSysreport. type PostNodeActionSysreportParams struct { Force *InQueryForce `form:"force,omitempty" json:"force,omitempty"` diff --git a/daemon/daemonapi/post_node_action_scsi_scan.go b/daemon/daemonapi/post_node_action_scsi_scan.go new file mode 100644 index 000000000..7f5ba97f7 --- /dev/null +++ b/daemon/daemonapi/post_node_action_scsi_scan.go @@ -0,0 +1,50 @@ +package daemonapi + +import ( + "net/http" + + "github.com/google/uuid" + "github.com/labstack/echo/v4" + + "github.com/opensvc/om3/v3/core/client" + "github.com/opensvc/om3/v3/core/naming" + "github.com/opensvc/om3/v3/daemon/api" +) + +func (a *DaemonAPI) PostNodeActionSCSIScan(ctx echo.Context, nodename string, params api.PostNodeActionSCSIScanParams) error { + if v, err := assertRoot(ctx); !v { + return err + } + nodename = a.parseNodename(nodename) + if nodename == a.localhost { + return a.localNodeActionSCSIScan(ctx, params) + } + return a.proxy(ctx, nodename, func(c *client.T) (*http.Response, error) { + return c.PostNodeActionSCSIScan(ctx.Request().Context(), nodename, ¶ms) + }) +} + +func (a *DaemonAPI) localNodeActionSCSIScan(ctx echo.Context, params api.PostNodeActionSCSIScanParams) error { + log := LogHandler(ctx, "PostNodeActionSCSIScan") + var requesterSid uuid.UUID + args := []string{"node", "scsi", "scan"} + + if params.Hba != nil { + args = append(args, "--hba", *params.Hba) + } + if params.Target != nil { + args = append(args, "--target", *params.Target) + } + if params.Lun != nil { + args = append(args, "--lun", *params.Lun) + } + + if params.RequesterSid != nil { + requesterSid = *params.RequesterSid + } + if sid, err := a.apiExec(ctx, naming.Path{}, requesterSid, args, log); err != nil { + return JSONProblemf(ctx, http.StatusInternalServerError, "", "%s", err) + } else { + return ctx.JSON(http.StatusOK, api.NodeActionAccepted{SessionID: sid}) + } +} diff --git a/util/scsi/linux.go b/util/scsi/linux.go index 0842ae726..9bf6f5546 100644 --- a/util/scsi/linux.go +++ b/util/scsi/linux.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "path/filepath" + "slices" "strings" "time" @@ -79,13 +80,185 @@ func ScanAllBusTargetLun(b, t, l string) error { } func ScanHostDirBusTargetLun(h, b, t, l string) error { + if t == "" { + t = "-" + } + if b == "" { + b = "-" + } + if l == "" { + l = "-" + } filename := fmt.Sprintf("%s/scan", h) s := fmt.Sprintf("%s %s %s", b, t, l) return os.WriteFile(filename, []byte(s), os.ModePerm) } func ScanHostBusTargetLun(h, b, t, l string) error { - filename := fmt.Sprintf("/sys/class/scsi_host/host%s/scan", h) - s := fmt.Sprintf("%s %s %s", b, t, l) - return os.WriteFile(filename, []byte(s), os.ModePerm) + return ScanHostDirBusTargetLun("/sys/class/scsi_host/host"+h, b, t, l) +} + +// hbaNums resolves an HBA identifier (WWN or iSCSI initiator name) to host numbers +// Returns a slice of host numbers since an iSCSI initiator can be associated with multiple hosts +func hbaNums(hba string) ([]string, error) { + if hba == "" { + return nil, nil + } + + var hostNums []string + + // Check if it's an iSCSI initiator name + if strings.HasPrefix(hba, "iqn") { + matches, err := filepath.Glob("/sys/class/scsi_host/host*/device/session*/iscsi_session/session*/initiatorname") + if err != nil { + return nil, err + } + + for _, path := range matches { + content, err := os.ReadFile(path) + if err != nil { + continue + } + if strings.TrimSpace(string(content)) == hba { + hostPath := filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(path))))) + hostNum := strings.TrimPrefix(filepath.Base(hostPath), "host") + // Avoid duplicates + if !slices.Contains(hostNums, hostNum) { + hostNums = append(hostNums, hostNum) + } + } + } + } + + // Check if it's a FC port name + matches, err := filepath.Glob("/sys/class/fc_host/host*/port_name") + if err != nil { + return nil, err + } + + for _, path := range matches { + content, err := os.ReadFile(path) + if err != nil { + continue + } + portName := strings.TrimSpace(string(content)) + if portName == hba || "0x"+portName == hba { + hostPath := filepath.Dir(path) + hostNum := strings.TrimPrefix(filepath.Base(hostPath), "host") + // Avoid duplicates + if !slices.Contains(hostNums, hostNum) { + hostNums = append(hostNums, hostNum) + } + } + } + + if len(hostNums) == 0 { + return nil, fmt.Errorf("HBA %s not found", hba) + } + + return hostNums, nil +} + +// targetNum resolves a target identifier (WWN or iSCSI target name) to a target number +func targetNum(hostNum, target string) (string, error) { + if target == "" { + return "", nil + } + + // Check if it's an iSCSI target name + if strings.HasPrefix(target, "iqn") { + pattern := "/sys/class/scsi_host/host" + hostNum + "/device/session*/iscsi_session/session*/targetname" + matches, err := filepath.Glob(pattern) + if err != nil { + return "", err + } + + for _, path := range matches { + content, err := os.ReadFile(path) + if err != nil { + continue + } + if strings.TrimSpace(string(content)) == target { + sessionPath := filepath.Dir(filepath.Dir(filepath.Dir(path))) + targetPaths, err := filepath.Glob(sessionPath + "/target*:*:*") + if err != nil || len(targetPaths) == 0 { + continue + } + base := filepath.Base(targetPaths[0]) + return base[strings.LastIndex(base, ":")+1:], nil + } + } + } + + // Check if it's a FC target port name + pattern := "/sys/class/fc_transport/target" + hostNum + ":*:*" + matches, err := filepath.Glob(pattern) + if err != nil { + return "", err + } + + for _, path := range matches { + portNamePath := filepath.Join(path, "port_name") + content, err := os.ReadFile(portNamePath) + if err != nil { + continue + } + portName := strings.TrimSpace(string(content)) + if portName == target || "0x"+portName == target { + return filepath.Base(path)[strings.LastIndex(path, ":")+1:], nil + } + } + + return "", fmt.Errorf("target %s not found on host %s", target, hostNum) +} + +// ScanSCSIHosts scans SCSI hosts for new devices +// hba and target are port names in the SCSI transport (e.g., WWN or iSCSI names) +// If hba and target are empty, all hosts will be scanned +// If only hba is provided, all targets on that host(s) will be scanned +// If hba and target are provided, specific target will be scanned on each host +// lun specifies the logical unit number to scan +func ScanSCSIHosts(hba, target, lun string) error { + if hba == "" && target == "" { + // Scan all hosts if no specific HBA or target is provided + return ScanAll() + } + + // Resolve HBA to host numbers (can return multiple for iSCSI) + hostNums, err := hbaNums(hba) + if err != nil { + return fmt.Errorf("failed to resolve HBA: %w", err) + } + + if len(hostNums) == 0 { + // No HBA specified, scan all hosts + return ScanAll() + } + + // Loop over all host numbers (important for iSCSI with multiple hosts) + var scanErrors []error + for _, hostNum := range hostNums { + targetNum, err := targetNum(hostNum, target) + if err != nil { + continue + } + if targetNum == "" { + // No target specified, scan all targets on the host + if err := ScanAllBusTargetLun(hostNum, "-", "-"); err != nil { + scanErrors = append(scanErrors, fmt.Errorf("host %s: %w", hostNum, err)) + } + } else { + // Scan specific host:bus:target:lun + // Note: We need to determine the bus number, for now we use "-" to scan all buses + if err := ScanHostBusTargetLun(hostNum, "-", targetNum, lun); err != nil { + scanErrors = append(scanErrors, fmt.Errorf("host %s: %w", hostNum, err)) + } + } + } + + if len(scanErrors) > 0 { + return fmt.Errorf("scan completed with errors: %v", scanErrors) + } + + return nil } From dcba6245cbbeff8bf7869125dad0c66d577dc477 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Fri, 3 Apr 2026 15:00:04 +0200 Subject: [PATCH 4/9] Add a "om node scanscsi" hidden command for backward compat --- core/om/factory.go | 7 +++++++ core/om/node.go | 1 + 2 files changed, 8 insertions(+) diff --git a/core/om/factory.go b/core/om/factory.go index 1356d400b..0a2b9907a 100644 --- a/core/om/factory.go +++ b/core/om/factory.go @@ -3586,6 +3586,13 @@ func newCmdDataStoreRename(kind string) *cobra.Command { return cmd } +func newCmdNodeScanscsi() *cobra.Command { + cmd := newCmdNodeSCSIScan() + cmd.Use = "scanscsi" + cmd.Hidden = true + return cmd +} + func newCmdObjectGen(kind string) *cobra.Command { return &cobra.Command{ Use: "gen", diff --git a/core/om/node.go b/core/om/node.go index 313135226..c906b0064 100644 --- a/core/om/node.go +++ b/core/om/node.go @@ -189,6 +189,7 @@ func init() { newCmdNodeEvents(), newCmdNodeEval(), newCmdNodeRegister(), + newCmdNodeScanscsi(), newCmdNodeSet(), newCmdNodeStonith(), newCmdNodeSysreport(), From 365f059208907d2f4e3e4fb5e04add5f84566598 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Fri, 3 Apr 2026 15:28:22 +0200 Subject: [PATCH 5/9] Move "om node prkey" to "om node scsi prkey" Keep the backward compatible command, hidden. Also implement the "ox node scsi prkey". --- core/om/factory.go | 8 ++++++- core/om/node.go | 1 + core/ox/factory.go | 2 +- core/ox/node.go | 2 +- core/oxcmd/node_prkey.go | 46 ++++++++++++++++++++++++++++++++++------ 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/core/om/factory.go b/core/om/factory.go index 0a2b9907a..a9caaa4bf 100644 --- a/core/om/factory.go +++ b/core/om/factory.go @@ -1133,7 +1133,7 @@ func newCmdNodeList() *cobra.Command { return cmd } -func newCmdNodePRKey() *cobra.Command { +func newCmdNodeSCSIPRKey() *cobra.Command { var options commands.CmdNodePRKey cmd := &cobra.Command{ Use: "prkey", @@ -3586,6 +3586,12 @@ func newCmdDataStoreRename(kind string) *cobra.Command { return cmd } +func newCmdNodePRKey() *cobra.Command { + cmd := newCmdNodeSCSIPRKey() + cmd.Hidden = true + return cmd +} + func newCmdNodeScanscsi() *cobra.Command { cmd := newCmdNodeSCSIScan() cmd.Use = "scanscsi" diff --git a/core/om/node.go b/core/om/node.go index c906b0064..ec38daf82 100644 --- a/core/om/node.go +++ b/core/om/node.go @@ -224,6 +224,7 @@ func init() { ) cmdNodeSCSI.AddCommand( newCmdNodeSCSIScan(), + newCmdNodeSCSIPRKey(), ) cmdNodeSchedule.AddCommand( newCmdNodeScheduleList(), diff --git a/core/ox/factory.go b/core/ox/factory.go index 43a001da8..702eb655c 100644 --- a/core/ox/factory.go +++ b/core/ox/factory.go @@ -1252,7 +1252,7 @@ func newCmdNodeList() *cobra.Command { return cmd } -func newCmdNodePRKey() *cobra.Command { +func newCmdNodeSCSIPRKey() *cobra.Command { var options commands.CmdNodePRKey cmd := &cobra.Command{ Use: "prkey", diff --git a/core/ox/node.go b/core/ox/node.go index f76eaaa5e..d99029f2e 100644 --- a/core/ox/node.go +++ b/core/ox/node.go @@ -93,6 +93,7 @@ func init() { cmdNode.AddCommand(cmdNodeSCSI) cmdNodeSCSI.AddCommand( newCmdNodeSCSIScan(), + newCmdNodeSCSIPRKey(), ) cmdNode.AddCommand(cmdNodeCollector) cmdNodeCollector.AddCommand(cmdNodeCollectorTag) @@ -177,7 +178,6 @@ func init() { newCmdNodeDrivers(), newCmdNodeLogs(), newCmdNodeList(), - newCmdNodePRKey(), newCmdNodePing(), newCmdNodeFreeze(), newCmdNodeGet(), diff --git a/core/oxcmd/node_prkey.go b/core/oxcmd/node_prkey.go index 61908edfd..ecce9b9dd 100644 --- a/core/oxcmd/node_prkey.go +++ b/core/oxcmd/node_prkey.go @@ -1,7 +1,12 @@ package oxcmd import ( - "github.com/opensvc/om3/v3/core/nodeaction" + "context" + "fmt" + + "github.com/opensvc/om3/v3/core/client" + "github.com/opensvc/om3/v3/core/output" + "github.com/opensvc/om3/v3/daemon/api" ) type ( @@ -12,9 +17,38 @@ type ( ) func (t *CmdNodePRKey) Run() error { - return nodeaction.New( - nodeaction.WithFormat(t.Output), - nodeaction.WithColor(t.Color), - nodeaction.WithRemoteNodes(t.NodeSelector), - ).Do() + var ( + err error + selector string + ) + c, err := client.New() + if err != nil { + return err + } + if t.NodeSelector == "" { + selector = "*" + } else { + selector = t.NodeSelector + } + resp, err := c.GetNodesWithResponse(context.Background(), &api.GetNodesParams{Node: &selector}) + if err != nil { + return err + } + switch resp.StatusCode() { + case 200: + case 401: + return fmt.Errorf("%s", *resp.JSON401) + case 403: + return fmt.Errorf("%s", *resp.JSON403) + default: + return fmt.Errorf("unexpected statuscode: %s", resp.Status()) + } + + output.Renderer{ + DefaultOutput: "tab=NAME:meta.node,PRKEY:data.config.prkey", + Output: t.Output, + Color: t.Color, + Data: *resp.JSON200, + }.Print() + return nil } From 520552df37d54d31f6be7fe47995f134c2c10564 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Fri, 3 Apr 2026 16:10:04 +0200 Subject: [PATCH 6/9] Add the "subsystem" command group to "o[mx] node" And flag command groups as "subsystem". --- core/om/factory.go | 1 + core/om/node.go | 109 +++++++++++++++++++++++++++------------------ core/ox/factory.go | 1 + core/ox/node.go | 80 ++++++++++++++++++++------------- 4 files changed, 117 insertions(+), 74 deletions(-) diff --git a/core/om/factory.go b/core/om/factory.go index a9caaa4bf..f0fd65c2e 100644 --- a/core/om/factory.go +++ b/core/om/factory.go @@ -944,6 +944,7 @@ func newCmdNodeDrain() *cobra.Command { func newCmdNodeDrivers() *cobra.Command { var options commands.CmdNodeDrivers cmd := &cobra.Command{ + GroupID: commoncmd.GroupIDQuery, Use: "drivers", Short: "list builtin drivers", Aliases: []string{"driver", "drive", "driv", "drv", "dr"}, diff --git a/core/om/node.go b/core/om/node.go index ec38daf82..65f8e5df0 100644 --- a/core/om/node.go +++ b/core/om/node.go @@ -11,88 +11,101 @@ import ( var ( cmdNode = commoncmd.NewCmdNode() cmdNodeCapabilities = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, Use: "capabilities", Short: "scan and list what the node is capable of", Aliases: []string{"capa", "caps", "cap"}, } cmdNodeCollector = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, Use: "collector", Short: "node collector data management commands", Aliases: []string{"coll"}, } cmdNodeCollectorTag = &cobra.Command{ - Use: "tag", - Short: "collector tags management commands", + GroupID: commoncmd.GroupIDSubsystems, + Use: "tag", + Short: "collector tags management commands", } cmdNodeCompliance = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, Use: "compliance", Short: "node configuration manager commands", Aliases: []string{"comp"}, } - cmdNodeComplianceAttach = &cobra.Command{ - Use: "attach", - Short: "attach modulesets and rulesets to the node", - Aliases: []string{"atta", "att", "at"}, - } - cmdNodeComplianceDetach = &cobra.Command{ - Use: "detach", - Short: "detach modulesets and rulesets from the node", - Aliases: []string{"deta", "det", "de"}, - } - cmdNodeComplianceList = &cobra.Command{ - Use: "list", - Short: "list modules, modulesets and rulesets available", - Aliases: []string{"lis", "li", "ls", "l"}, - } - cmdNodeComplianceShow = &cobra.Command{ - Use: "show", - Short: "show modules, modulesets, rulesets, modules, attachments", - Aliases: []string{"sho", "sh", "s"}, - } cmdNodeConfig = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, Use: "config", Short: "configuration commands", Aliases: []string{"conf", "c", "cf", "cfg"}, } cmdNodeSCSI = &cobra.Command{ - Use: "scsi", - Short: "scsi subsystem commands", - } - cmdNodePrint = &cobra.Command{ - Use: "print", - Hidden: true, - Aliases: []string{"prin", "pri", "pr"}, - } - cmdNodePush = &cobra.Command{ - Use: "push", - Short: "push node discover information to the collector", + GroupID: commoncmd.GroupIDSubsystems, + Use: "scsi", + Short: "scsi commands", } cmdNodeRelay = &cobra.Command{ - Use: "relay", - Short: "relay subsystem commands", + GroupID: commoncmd.GroupIDSubsystems, + Use: "relay", + Short: "relay commands", } cmdNodeScan = &cobra.Command{ Use: "scan", Hidden: true, } cmdNodeSchedule = &cobra.Command{ - Use: "schedule", - Short: "node scheduler commands", + GroupID: commoncmd.GroupIDSubsystems, + Use: "schedule", + Short: "scheduler commands", } cmdNodeSSH = &cobra.Command{ - Use: "ssh", - Short: "ssh subsystem commands", + GroupID: commoncmd.GroupIDSubsystems, + Use: "ssh", + Short: "ssh commands", + } + + // Backward compat + + cmdNodeEdit = newCmdNodeEdit() + cmdNodeValidate = newCmdNodeValidate() + + cmdNodeUpdateSSH = &cobra.Command{ + Use: "ssh", + Hidden: true, + } + cmdNodePrint = &cobra.Command{ + Use: "print", + Hidden: true, + Aliases: []string{"prin", "pri", "pr"}, + } + cmdNodePush = &cobra.Command{ + Use: "push", + Short: "push node discover information to the collector", } cmdNodeUpdate = &cobra.Command{ Use: "update", Hidden: true, } - cmdNodeUpdateSSH = &cobra.Command{ - Use: "ssh", - Hidden: true, + cmdNodeComplianceAttach = &cobra.Command{ + Use: "attach", + Short: "attach modulesets and rulesets to the node", + Aliases: []string{"atta", "att", "at"}, + } + cmdNodeComplianceDetach = &cobra.Command{ + Use: "detach", + Short: "detach modulesets and rulesets from the node", + Aliases: []string{"deta", "det", "de"}, + } + cmdNodeComplianceList = &cobra.Command{ + Use: "list", + Short: "list modules, modulesets and rulesets available", + Aliases: []string{"lis", "li", "ls", "l"}, + } + cmdNodeComplianceShow = &cobra.Command{ + Use: "show", + Short: "show modules, modulesets, rulesets, modules, attachments", + Aliases: []string{"sho", "sh", "s"}, } - cmdNodeEdit = newCmdNodeEdit() - cmdNodeValidate = newCmdNodeValidate() ) // getCmdNodeWithVersion returns cmdNode with --version (for backward compatibility with b2.1) @@ -112,6 +125,14 @@ func getCmdNodeWithVersion() *cobra.Command { func init() { // Add backward compatibility for --version flag cmdNode = getCmdNodeWithVersion() + cmdNode.AddGroup( + // commoncmd.NewGroupOrchestratedActions(), + // commoncmd.NewGroupQuery(), + commoncmd.NewGroupSubsystems(), + ) + cmdNodeCollector.AddGroup( + commoncmd.NewGroupSubsystems(), + ) root.AddCommand(cmdNode) cmdNode.AddCommand(cmdNodeCapabilities) diff --git a/core/ox/factory.go b/core/ox/factory.go index 702eb655c..e45fed9ed 100644 --- a/core/ox/factory.go +++ b/core/ox/factory.go @@ -850,6 +850,7 @@ func newCmdNodeDrain() *cobra.Command { func newCmdNodeDrivers() *cobra.Command { var options commands.CmdNodeDrivers cmd := &cobra.Command{ + GroupID: commoncmd.GroupIDQuery, Use: "drivers", Short: "list builtin drivers", Aliases: []string{"driver", "drive", "driv", "drv", "dr"}, diff --git a/core/ox/node.go b/core/ox/node.go index d99029f2e..dbad51de7 100644 --- a/core/ox/node.go +++ b/core/ox/node.go @@ -9,28 +9,66 @@ import ( var ( cmdNode = commoncmd.NewCmdNode() cmdNodeCapabilities = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, Use: "capabilities", Short: "scan and list what the node is capable of", Aliases: []string{"capa", "caps", "cap"}, } cmdNodeCollector = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, Use: "collector", Short: "node collector data management commands", Aliases: []string{"coll"}, } cmdNodeSCSI = &cobra.Command{ - Use: "scsi", - Short: "scsi subsystem commands", + GroupID: commoncmd.GroupIDSubsystems, + Use: "scsi", + Short: "scsi commands", } cmdNodeCollectorTag = &cobra.Command{ - Use: "tag", - Short: "collector tags management commands", + GroupID: commoncmd.GroupIDSubsystems, + Use: "tag", + Short: "collector tags commands", } cmdNodeCompliance = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, Use: "compliance", Short: "node configuration manager commands", Aliases: []string{"comp"}, } + cmdNodeRelay = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, + Use: "relay", + Short: "relay commands", + } + cmdNodeSchedule = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, + Use: "schedule", + Short: "scheduler commands", + Aliases: []string{"sched"}, + } + cmdNodeSSH = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, + Use: "ssh", + Short: "ssh commands", + } + cmdNodeConfig = &cobra.Command{ + GroupID: commoncmd.GroupIDSubsystems, + Use: "config", + Short: "node configuration commands", + } + + // backward compat + + cmdNodePrint = &cobra.Command{ + Use: "print", + Short: "print node discover information", + Aliases: []string{"prin", "pri", "pr"}, + } + cmdNodePush = &cobra.Command{ + Use: "push", + Short: "push node discover information to the collector", + } cmdNodeComplianceAttach = &cobra.Command{ Use: "attach", Short: "attach modulesets and rulesets to the node", @@ -51,32 +89,7 @@ var ( Short: "show modules, modulesets, rulesets, modules, attachments", Aliases: []string{"sho", "sh", "s"}, } - cmdNodePrint = &cobra.Command{ - Use: "print", - Short: "print node discover information", - Aliases: []string{"prin", "pri", "pr"}, - } - cmdNodePush = &cobra.Command{ - Use: "push", - Short: "push node discover information to the collector", - } - cmdNodeRelay = &cobra.Command{ - Use: "relay", - Short: "relay subsystem commands", - } - cmdNodeSchedule = &cobra.Command{ - Use: "schedule", - Short: "node scheduler commands", - Aliases: []string{"sched"}, - } - cmdNodeSSH = &cobra.Command{ - Use: "ssh", - Short: "ssh subsystem commands", - } - cmdNodeConfig = &cobra.Command{ - Use: "config", - Short: "node configuration commands", - } + cmdNodeSystem = newCmdNodeSystem() cmdNodeSystemSAN = newCmdNodeSystemSAN() cmdNodeEdit = newCmdNodeEdit() @@ -86,6 +99,13 @@ var ( func init() { root.AddCommand(cmdNode) cmdNode.AddCommand(cmdNodeCapabilities) + cmdNode.AddGroup( + commoncmd.NewGroupSubsystems(), + ) + cmdNodeCollector.AddGroup( + commoncmd.NewGroupSubsystems(), + ) + cmdNodeCapabilities.AddCommand( newCmdNodeCapabilitiesList(), newCmdNodeCapabilitiesScan(), From bd68db1171f3b191166332d8217898fba690bff3 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Fri, 3 Apr 2026 17:15:07 +0200 Subject: [PATCH 7/9] Remove overzealous command aliases * "schedul", "schedu", "sched", "sche", "sch", "sc" => "sched" * drop all "scsi scan" aliases * drop all "cap scan" aliases --- core/om/factory.go | 14 ++++++-------- core/ox/factory.go | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/core/om/factory.go b/core/om/factory.go index f0fd65c2e..d5179f81a 100644 --- a/core/om/factory.go +++ b/core/om/factory.go @@ -504,9 +504,8 @@ func newCmdNodeCapabilitiesList() *cobra.Command { func newCmdNodeCapabilitiesScan() *cobra.Command { var options commands.CmdNodeCapabilitiesScan cmd := &cobra.Command{ - Use: "scan", - Short: "scan the node capabilities", - Aliases: []string{"sca", "sc"}, + Use: "scan", + Short: "scan the node capabilities", Long: `Scan the node for capabilities. Capabilities are normally scanned at daemon startup and when the installed @@ -525,9 +524,8 @@ installed software to be discovered without restarting the daemon.`, func newCmdNodeSCSIScan() *cobra.Command { var options commands.CmdNodeSCSIScan cmd := &cobra.Command{ - Use: "scan", - Short: "scan the scsi hosts in search of new disks", - Aliases: []string{"sca", "sc"}, + Use: "scan", + Short: "scan the scsi hosts in search of new disks", Long: `Scan the scsi hosts in search of new disks. This command scans SCSI hosts for new block devices. You can specify specific HBA, target, and LUN to scan.`, @@ -3297,7 +3295,7 @@ func newCmdNodePrintSchedule() *cobra.Command { cmd := newCmdNodeScheduleList() cmd.Hidden = true cmd.Use = "schedule" - cmd.Aliases = []string{"schedul", "schedu", "sched", "sche", "sch", "sc"} + cmd.Aliases = []string{"sched"} return cmd } @@ -3413,7 +3411,7 @@ func newCmdObjectPrintSchedule(kind string) *cobra.Command { cmd := newCmdObjectScheduleList(kind) cmd.Hidden = true cmd.Use = "schedule" - cmd.Aliases = []string{"schedul", "schedu", "sched", "sche", "sch", "sc"} + cmd.Aliases = []string{"sched"} return cmd } diff --git a/core/ox/factory.go b/core/ox/factory.go index e45fed9ed..8bfb4a49e 100644 --- a/core/ox/factory.go +++ b/core/ox/factory.go @@ -408,9 +408,8 @@ func newCmdNodeCapabilitiesList() *cobra.Command { func newCmdNodeCapabilitiesScan() *cobra.Command { var options commands.CmdNodeCapabilitiesScan cmd := &cobra.Command{ - Use: "scan", - Short: "scan the node capabilities", - Aliases: []string{"sca", "sc"}, + Use: "scan", + Short: "scan the node capabilities", Long: `Scan the node for capabilities. Capabilities are normally scanned at daemon startup and when the installed @@ -429,9 +428,8 @@ installed software to be discovered without restarting the daemon.`, func newCmdNodeSCSIScan() *cobra.Command { var options commands.CmdNodeSCSIScan cmd := &cobra.Command{ - Use: "scan", - Short: "scan the scsi hosts in search of new disks", - Aliases: []string{"sca", "sc"}, + Use: "scan", + Short: "scan the scsi hosts in search of new disks", Long: `Scan the scsi hosts in search of new disks. This command scans SCSI hosts for new block devices. You can specify specific HBA, target, and LUN to scan.`, @@ -3018,7 +3016,7 @@ func newCmdNodePrintSchedule() *cobra.Command { cmd := newCmdNodeScheduleList() cmd.Hidden = true cmd.Use = "schedule" - cmd.Aliases = []string{"schedul", "schedu", "sched", "sche", "sch", "sc"} + cmd.Aliases = []string{"sched"} return cmd } @@ -3201,7 +3199,7 @@ func newCmdObjectPrintSchedule(kind string) *cobra.Command { cmd := newCmdObjectScheduleList(kind) cmd.Hidden = true cmd.Use = "schedule" - cmd.Aliases = []string{"schedul", "schedu", "sched", "sche", "sch", "sc"} + cmd.Aliases = []string{"sched"} return cmd } From 5672b9a100e3cb817997a4272c819d655fe2b522 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Fri, 3 Apr 2026 17:44:19 +0200 Subject: [PATCH 8/9] Normalize the prkey command codefile name --- core/omcmd/{node_prkey.go => node_scsi_prkey.go} | 0 core/oxcmd/{node_prkey.go => node_scsi_prkey.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename core/omcmd/{node_prkey.go => node_scsi_prkey.go} (100%) rename core/oxcmd/{node_prkey.go => node_scsi_prkey.go} (100%) diff --git a/core/omcmd/node_prkey.go b/core/omcmd/node_scsi_prkey.go similarity index 100% rename from core/omcmd/node_prkey.go rename to core/omcmd/node_scsi_prkey.go diff --git a/core/oxcmd/node_prkey.go b/core/oxcmd/node_scsi_prkey.go similarity index 100% rename from core/oxcmd/node_prkey.go rename to core/oxcmd/node_scsi_prkey.go From f22c25c522ae657eb674f255243ba2e03d7dd7f8 Mon Sep 17 00:00:00 2001 From: Christophe Varoqui Date: Fri, 3 Apr 2026 17:51:35 +0200 Subject: [PATCH 9/9] Remove "om node drivers" command aliases New command, no aliases, autocompletion works. --- core/om/factory.go | 1 - core/ox/factory.go | 1 - 2 files changed, 2 deletions(-) diff --git a/core/om/factory.go b/core/om/factory.go index d5179f81a..efb073908 100644 --- a/core/om/factory.go +++ b/core/om/factory.go @@ -945,7 +945,6 @@ func newCmdNodeDrivers() *cobra.Command { GroupID: commoncmd.GroupIDQuery, Use: "drivers", Short: "list builtin drivers", - Aliases: []string{"driver", "drive", "driv", "drv", "dr"}, RunE: func(cmd *cobra.Command, args []string) error { return options.Run() }, diff --git a/core/ox/factory.go b/core/ox/factory.go index 8bfb4a49e..ba358a996 100644 --- a/core/ox/factory.go +++ b/core/ox/factory.go @@ -851,7 +851,6 @@ func newCmdNodeDrivers() *cobra.Command { GroupID: commoncmd.GroupIDQuery, Use: "drivers", Short: "list builtin drivers", - Aliases: []string{"driver", "drive", "driv", "drv", "dr"}, RunE: func(cmd *cobra.Command, args []string) error { return options.Run() },