From 49059e6871497f456ea54cde801f2d28d6a00e21 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 10:23:42 +0200 Subject: [PATCH 1/9] Adapt to API v0.5.0. --- cmd/admin/v2/machine.go | 153 +++++++++--------- cmd/api/v2/machine.go | 2 +- cmd/tableprinters/common.go | 5 +- cmd/tableprinters/machine.go | 98 +++++------ docs/admin/metalctlv2_admin_component.md | 1 + .../admin/metalctlv2_admin_component_prune.md | 37 +++++ docs/admin/metalctlv2_admin_machine.md | 1 - docs/admin/metalctlv2_admin_machine_bmc.md | 1 + ...> metalctlv2_admin_machine_bmc_command.md} | 8 +- .../metalctlv2_admin_machine_bmc_list.md | 2 + docs/admin/metalctlv2_admin_machine_create.md | 2 +- docs/metalctlv2_machine_create.md | 2 +- go.mod | 6 +- go.sum | 12 +- pkg/helpers/emoji.go | 2 +- pkg/helpers/machine.go | 9 +- tests/e2e/admin/machine_test.go | 78 ++++++++- tests/e2e/api/machine_test.go | 9 +- tests/e2e/testresources/machine.go | 113 +++++++++++++ tests/e2e/testresources/switch.go | 4 +- 20 files changed, 393 insertions(+), 152 deletions(-) create mode 100644 docs/admin/metalctlv2_admin_component_prune.md rename docs/admin/{metalctlv2_admin_machine_bmc-command.md => metalctlv2_admin_machine_bmc_command.md} (79%) diff --git a/cmd/admin/v2/machine.go b/cmd/admin/v2/machine.go index 8693db7..078bef4 100644 --- a/cmd/admin/v2/machine.go +++ b/cmd/admin/v2/machine.go @@ -46,21 +46,9 @@ func newMachineCmd(c *config.Config) *cobra.Command { DescribePrinter: func() printers.Printer { return c.DescribePrinter }, ListPrinter: func() printers.Printer { return c.ListPrinter }, ListCmdMutateFn: func(cmd *cobra.Command) { - cmd.Flags().String("id", "", "id of machine which should be listed") - cmd.Flags().String("name", "", "name from machines which should be listed") - cmd.Flags().String("hostname", "", "hostname from machines which should be listed") - cmd.Flags().String("size", "", "size from machines which should be listed") - cmd.Flags().String("image", "", "image") - cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") - cmd.Flags().StringP("partition", "", "", "partition from where machines should be listed") + w.addMachineQueryFlags(cmd) - genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("size", c.Completion.Size)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("image", c.Completion.Image)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", c.Completion.Partition)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("id", c.Completion.AdminMachine)) - - cmd.Long = cmd.Short + "\n" + helpers.EmojiHelpText() + cmd.Long = cmd.Short + "\n" + helpers.MachineListEmojiHelpText() }, DescribeCmdMutateFn: func(cmd *cobra.Command) { cmd.Flags().StringP("project", "p", "", "project of the machine") @@ -145,7 +133,7 @@ If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picke } bmcCommandCmd := &cobra.Command{ - Use: "bmc-command", + Use: "command", Short: "send a command to the bmc of a machine", RunE: func(cmd *cobra.Command, args []string) error { return w.bmcCommand(args) @@ -178,19 +166,10 @@ If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picke return w.bmcList(cmd.Context()) }, } - bmcListCmd.Flags().String("id", "", "id of machine which should be listed") - bmcListCmd.Flags().String("size", "", "size from machines which should be listed") - bmcListCmd.Flags().String("image", "", "image") - bmcListCmd.Flags().StringP("project", "p", "", "project from where machines should be listed") - bmcListCmd.Flags().StringP("partition", "", "", "partition from where machines should be listed") - genericcli.Must(bmcListCmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) - genericcli.Must(bmcListCmd.RegisterFlagCompletionFunc("size", c.Completion.Size)) - genericcli.Must(bmcListCmd.RegisterFlagCompletionFunc("image", c.Completion.Image)) - genericcli.Must(bmcListCmd.RegisterFlagCompletionFunc("partition", c.Completion.Partition)) - genericcli.Must(bmcListCmd.RegisterFlagCompletionFunc("id", c.Completion.AdminMachine)) + w.addMachineQueryFlags(bmcListCmd) - bmcCmd.AddCommand(bmcGetCmd, bmcListCmd) + bmcCmd.AddCommand(bmcGetCmd, bmcListCmd, bmcCommandCmd) lockCmd := &cobra.Command{ Use: "lock", @@ -247,7 +226,7 @@ If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picke firewallSSHCmd.Flags().StringP("identity", "i", "~/.ssh/id_rsa", "specify identity file to SSH to the firewall like: -i path/to/id_rsa") firewallSSHCmd.Flags().String("reason", "", "the reason why to connect to the firewall through SSH") - return genericcli.NewCmds(cmdsConfig, bmcCommandCmd, bmcCmd, lockCmd, taintCmd, consoleCmd, consolePasswordCmd, firewallSSHCmd) + return genericcli.NewCmds(cmdsConfig, bmcCmd, lockCmd, taintCmd, consoleCmd, consolePasswordCmd, firewallSSHCmd) } func (c *machine) Create(rq *apiv2.MachineServiceCreateRequest) (*apiv2.Machine, error) { @@ -298,49 +277,8 @@ func (c *machine) List() ([]*apiv2.Machine, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() - var allocation *apiv2.MachineAllocationQuery - - if viper.IsSet("hostname") || viper.IsSet("name") || viper.IsSet("project") || viper.IsSet("image") { - allocation = &apiv2.MachineAllocationQuery{ - Hostname: pointer.PointerOrNil(viper.GetString("hostname")), - Name: pointer.PointerOrNil(viper.GetString("name")), - Project: pointer.PointerOrNil(viper.GetString("project")), - Image: pointer.PointerOrNil(viper.GetString("image")), - } - } - resp, err := c.c.Client.Adminv2().Machine().List(ctx, &adminv2.MachineServiceListRequest{ - Query: &apiv2.MachineQuery{ - Uuid: pointer.PointerOrNil(viper.GetString("id")), - Partition: pointer.PointerOrNil(viper.GetString("partition")), - Size: pointer.PointerOrNil(viper.GetString("size")), - Allocation: allocation, - // Rack: pointer.PointerOrNil(viper.GetString("rack")), - // Labels: &apiv2.Labels{ - // Labels: tag.NewTagMap(viper.GetStringSlice("labels")), - // }, - // Bmc: &apiv2.MachineBMCQuery{ - // Address: pointer.PointerOrNil(viper.GetString("bmc-address")), - // Mac: pointer.PointerOrNil(viper.GetString("bmc-mac")), - // User: pointer.PointerOrNil(viper.GetString("bmc-user")), - // Interface: pointer.PointerOrNil(viper.GetString("bmc-interface")), - // }, - // Fru: &apiv2.MachineFRUQuery{ - // ChassisPartNumber: pointer.PointerOrNil(viper.GetString("chassis-part-number")), - // ChassisPartSerial: pointer.PointerOrNil(viper.GetString("chassis-part-serial")), - // BoardMfg: pointer.PointerOrNil(viper.GetString("board-mfg")), - // BoardSerial: pointer.PointerOrNil(viper.GetString("board-serial")), - // BoardPartNumber: pointer.PointerOrNil(viper.GetString("board-part-number")), - // ProductManufacturer: pointer.PointerOrNil(viper.GetString("product-manufacturer")), - // ProductPartNumber: pointer.PointerOrNil(viper.GetString("product-part-number")), - // ProductSerial: pointer.PointerOrNil(viper.GetString("product-serial")), - // }, - // Hardware: &apiv2.MachineHardwareQuery{ - // Memory: pointer.PointerOrNil(viper.GetUint64("memory")), - // CpuCores: pointer.PointerOrNil(viper.GetUint32("cpu-cores")), - // }, - // State: &0, - }, + Query: machineQuery(), }) if err != nil { return nil, err @@ -445,19 +383,20 @@ func (c *machine) bmcGet(ctx context.Context, args []string) error { return err } - return c.c.DescribePrinter.Print(resp) + return c.c.DescribePrinter.Print(resp.BmcDetails) } func (c *machine) bmcList(ctx context.Context) error { - // FIXME api contains the wrong query here, must be a MachineQuery instead of a MachineBMCQuery - req := &adminv2.MachineServiceListBMCRequest{} + req := &adminv2.MachineServiceListBMCRequest{ + Query: machineQuery(), + } resp, err := c.c.Client.Adminv2().Machine().ListBMC(ctx, req) if err != nil { return err } - return c.c.ListPrinter.Print(resp.BmcReports) + return c.c.ListPrinter.Print(resp.BmcDetails) } func (c *machine) consolePassword(ctx context.Context, args []string) error { @@ -515,7 +454,11 @@ func (c *machine) impitool(ctx context.Context, id string) error { return err } - bmc := resp.Bmc.Bmc + if resp.BmcDetails == nil || resp.BmcDetails.BmcReport == nil || resp.BmcDetails.BmcReport.Bmc == nil { + return fmt.Errorf("no bmc details present") + } + + bmc := resp.BmcDetails.BmcReport.Bmc intf := "lanplus" // -I lanplus -H 192.168.2.19 -U ADMIN -P ADMIN sol activate @@ -667,3 +610,65 @@ func sshClient(user, keyfile, host string, port int, idToken *string, passwordAu return s.Connect(env) } + +func (c *machine) addMachineQueryFlags(cmd *cobra.Command) { + cmd.Flags().String("id", "", "id of machine which should be listed") + cmd.Flags().String("name", "", "name from machines which should be listed") + cmd.Flags().String("hostname", "", "hostname from machines which should be listed") + cmd.Flags().String("size", "", "size from machines which should be listed") + cmd.Flags().String("image", "", "image") + cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") + cmd.Flags().StringP("partition", "", "", "partition from where machines should be listed") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.c.Completion.Project)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("size", c.c.Completion.Size)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("image", c.c.Completion.Image)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", c.c.Completion.Partition)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("id", c.c.Completion.AdminMachine)) + +} + +func machineQuery() *apiv2.MachineQuery { + var allocation *apiv2.MachineAllocationQuery + + if viper.IsSet("hostname") || viper.IsSet("name") || viper.IsSet("project") || viper.IsSet("image") { + allocation = &apiv2.MachineAllocationQuery{ + Hostname: pointer.PointerOrNil(viper.GetString("hostname")), + Name: pointer.PointerOrNil(viper.GetString("name")), + Project: pointer.PointerOrNil(viper.GetString("project")), + Image: pointer.PointerOrNil(viper.GetString("image")), + } + } + + return &apiv2.MachineQuery{ + Uuid: pointer.PointerOrNil(viper.GetString("id")), + Partition: pointer.PointerOrNil(viper.GetString("partition")), + Size: pointer.PointerOrNil(viper.GetString("size")), + Allocation: allocation, + // Rack: pointer.PointerOrNil(viper.GetString("rack")), + // Labels: &apiv2.Labels{ + // Labels: tag.NewTagMap(viper.GetStringSlice("labels")), + // }, + // Bmc: &apiv2.MachineBMCQuery{ + // Address: pointer.PointerOrNil(viper.GetString("bmc-address")), + // Mac: pointer.PointerOrNil(viper.GetString("bmc-mac")), + // User: pointer.PointerOrNil(viper.GetString("bmc-user")), + // Interface: pointer.PointerOrNil(viper.GetString("bmc-interface")), + // }, + // Fru: &apiv2.MachineFRUQuery{ + // ChassisPartNumber: pointer.PointerOrNil(viper.GetString("chassis-part-number")), + // ChassisPartSerial: pointer.PointerOrNil(viper.GetString("chassis-part-serial")), + // BoardMfg: pointer.PointerOrNil(viper.GetString("board-mfg")), + // BoardSerial: pointer.PointerOrNil(viper.GetString("board-serial")), + // BoardPartNumber: pointer.PointerOrNil(viper.GetString("board-part-number")), + // ProductManufacturer: pointer.PointerOrNil(viper.GetString("product-manufacturer")), + // ProductPartNumber: pointer.PointerOrNil(viper.GetString("product-part-number")), + // ProductSerial: pointer.PointerOrNil(viper.GetString("product-serial")), + // }, + // Hardware: &apiv2.MachineHardwareQuery{ + // Memory: pointer.PointerOrNil(viper.GetUint64("memory")), + // CpuCores: pointer.PointerOrNil(viper.GetUint32("cpu-cores")), + // }, + // State: &0, + } +} diff --git a/cmd/api/v2/machine.go b/cmd/api/v2/machine.go index a9ff019..d5154ce 100644 --- a/cmd/api/v2/machine.go +++ b/cmd/api/v2/machine.go @@ -53,7 +53,7 @@ func newMachineCmd(c *config.Config) *cobra.Command { genericcli.Must(cmd.RegisterFlagCompletionFunc("image", c.Completion.Image)) genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", c.Completion.Partition)) - cmd.Long = cmd.Short + "\n" + helpers.EmojiHelpText() + cmd.Long = cmd.Short + "\n" + helpers.MachineListEmojiHelpText() }, UpdateCmdMutateFn: func(cmd *cobra.Command) { cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") diff --git a/cmd/tableprinters/common.go b/cmd/tableprinters/common.go index 0de0a57..f7c820f 100644 --- a/cmd/tableprinters/common.go +++ b/cmd/tableprinters/common.go @@ -20,6 +20,7 @@ const ( threequarterpie = "◕" poweron = "⏻" powersleep = "⏾" + ledon = "🟒" ) type TablePrinter struct { @@ -65,7 +66,9 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin case []*apiv2.Machine: return t.MachineTable(d, wide) - case map[string]*apiv2.MachineBMCReport: + case *apiv2.MachineBMCDetails: + return t.MachineBMCTable(pointer.WrapInSlice(d), wide) + case []*apiv2.MachineBMCDetails: return t.MachineBMCTable(d, wide) case *apiv2.IP: diff --git a/cmd/tableprinters/machine.go b/cmd/tableprinters/machine.go index 92f436d..54bda35 100644 --- a/cmd/tableprinters/machine.go +++ b/cmd/tableprinters/machine.go @@ -110,72 +110,76 @@ func (t *TablePrinter) MachineTable(data []*apiv2.Machine, wide bool) ([]string, return header, rows, nil } -func (t *TablePrinter) MachineBMCTable(data map[string]*apiv2.MachineBMCReport, wide bool) ([]string, [][]string, error) { +func (t *TablePrinter) MachineBMCTable(data []*apiv2.MachineBMCDetails, wide bool) ([]string, [][]string, error) { var ( rows [][]string - header = []string{"ID", "Power", "IP", "Mac", "Board Part Number", "Bios", "BMC", "Size", "Partition", "Rack", "Updated"} + header = []string{"ID", "", "Power", "IP", "Mac", "Board Part Number", "Bios", "BMC", "Size", "Partition", "Rack", "Updated"} ) if wide { - header = []string{"ID", "Power", "IP", "Mac", "Board Part Number", "Chassis Serial", "Product Serial", "Bios Version", "BMC Version", "Size", "Partition", "Rack", "Updated"} + header = []string{"ID", "LED", "Power", "IP", "Mac", "Board Part Number", "Chassis Serial", "Product Serial", "Bios Version", "BMC Version", "Size", "Partition", "Rack", "Updated"} } - for machineID, report := range data { - // partition := pointer.SafeDeref(machine.).ID - // size := pointer.SafeDeref(pointer.SafeDeref(machine.Size).ID) - - if report.LedState != nil && report.LedState.Value == "LED-ON" { - blue := color.New(color.FgBlue).SprintFunc() - machineID = blue(machineID) - } - + for _, detail := range data { var ( - // FIXME these are not provided by machineBMCReport - // FIXME Events are also not provided. - size = "" - partition = "" - rack = "" - - ipAddress = "" - mac = "" - bpn = "" - cs = "" - ps = "" - bmcVersion = "" - bmc = report.Bmc - fru = report.Fru - lastUpdated = "never" - bios = report.Bios - biosVersion = "" + size = detail.Size + partition = detail.Partition + rack = detail.Rack + machineID = detail.Uuid + + ipAddress = "" + mac = "" + bpn = "" + chassisSerial = "" + productSerial = "" + bmcVersion = "" + lastUpdated = "never" + biosVersion = "" + power = "" + powerText = "" + ledState = "" + + emojis []string ) - if fru != nil { - bpn = pointer.SafeDeref(fru.BoardPartNumber) - cs = pointer.SafeDeref(fru.ChassisPartSerial) - ps = pointer.SafeDeref(fru.ProductSerial) - } - if bmc != nil { - ipAddress = bmc.Address - mac = bmc.Mac - bmcVersion = bmc.Version + if report := detail.BmcReport; report != nil { + if led := report.LedState; led != nil { + ledState = led.Value + } - } - power, powerText := extractPowerState(report) + if fru := report.Fru; fru != nil { + bpn = pointer.SafeDeref(fru.BoardPartNumber) + chassisSerial = pointer.SafeDeref(fru.ChassisPartSerial) + productSerial = pointer.SafeDeref(fru.ProductSerial) + } - if report.UpdatedAt != nil && !report.UpdatedAt.AsTime().IsZero() { - lastUpdated = fmt.Sprintf("%s ago", humanizeDuration(time.Since(report.UpdatedAt.AsTime()))) + if bmc := report.Bmc; bmc != nil { + ipAddress = bmc.Address + mac = bmc.Mac + bmcVersion = bmc.Version + } + + power, powerText = extractPowerState(report) + + if report.UpdatedAt != nil && !report.UpdatedAt.AsTime().IsZero() { + lastUpdated = fmt.Sprintf("%s ago", humanizeDuration(time.Since(report.UpdatedAt.AsTime()))) + } + + if bios := report.Bios; bios != nil { + biosVersion = bios.Version + } } - if bios != nil { - biosVersion = bios.Version + if strings.EqualFold(ledState, "LED-ON") { + blue := color.New(color.FgBlue).SprintFunc() + emojis = append(emojis, blue(ledon)) } if wide { - rows = append(rows, []string{machineID, powerText, ipAddress, mac, bpn, cs, ps, biosVersion, bmcVersion, size, partition, rack, lastUpdated}) + rows = append(rows, []string{machineID, ledState, powerText, ipAddress, mac, bpn, chassisSerial, productSerial, biosVersion, bmcVersion, size, partition, rack, lastUpdated}) } else { - rows = append(rows, []string{machineID, power, ipAddress, mac, bpn, biosVersion, bmcVersion, size, partition, rack, lastUpdated}) + rows = append(rows, []string{machineID, strings.Join(emojis, nbr), power, ipAddress, mac, bpn, biosVersion, bmcVersion, size, partition, rack, lastUpdated}) } - } t.t.DisableAutoWrap(false) diff --git a/docs/admin/metalctlv2_admin_component.md b/docs/admin/metalctlv2_admin_component.md index 6fb1bf4..d1a9fd8 100644 --- a/docs/admin/metalctlv2_admin_component.md +++ b/docs/admin/metalctlv2_admin_component.md @@ -31,4 +31,5 @@ list status of components, e.g. microservices connected to the metal-apiserver * [metalctlv2 admin component delete](metalctlv2_admin_component_delete.md) - deletes the component * [metalctlv2 admin component describe](metalctlv2_admin_component_describe.md) - describes the component * [metalctlv2 admin component list](metalctlv2_admin_component_list.md) - list all components +* [metalctlv2 admin component prune](metalctlv2_admin_component_prune.md) - prune outdated component entries diff --git a/docs/admin/metalctlv2_admin_component_prune.md b/docs/admin/metalctlv2_admin_component_prune.md new file mode 100644 index 0000000..497c133 --- /dev/null +++ b/docs/admin/metalctlv2_admin_component_prune.md @@ -0,0 +1,37 @@ +## metalctlv2 admin component prune + +prune outdated component entries + +### Synopsis + +prune outdated component entries to shorten the component list + +``` +metalctlv2 admin component prune [flags] +``` + +### Options + +``` + -h, --help help for prune + --max-age duration prune all components which are older than max-age and not active anymore (default 12h0m0s) + --type string prune only component of this type +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template), wide is a table with more columns. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 admin component](metalctlv2_admin_component.md) - manage component entities + diff --git a/docs/admin/metalctlv2_admin_machine.md b/docs/admin/metalctlv2_admin_machine.md index 9c58e9b..cbbd5db 100644 --- a/docs/admin/metalctlv2_admin_machine.md +++ b/docs/admin/metalctlv2_admin_machine.md @@ -30,7 +30,6 @@ manage machines * [metalctlv2 admin](metalctlv2_admin.md) - admin commands * [metalctlv2 admin machine apply](metalctlv2_admin_machine_apply.md) - applies one or more machines from a given file * [metalctlv2 admin machine bmc](metalctlv2_admin_machine_bmc.md) - get and list machine bmc/ipmi information -* [metalctlv2 admin machine bmc-command](metalctlv2_admin_machine_bmc-command.md) - send a command to the bmc of a machine * [metalctlv2 admin machine console](metalctlv2_admin_machine_console.md) - establishes a connection to the serial console of a machine. for authentication at the metal-console it uses the token such that no machine ssh key is required for access (unlike the corresponding user API command). * [metalctlv2 admin machine consolepassword](metalctlv2_admin_machine_consolepassword.md) - fetch the consolepassword of a machine * [metalctlv2 admin machine create](metalctlv2_admin_machine_create.md) - creates the machine diff --git a/docs/admin/metalctlv2_admin_machine_bmc.md b/docs/admin/metalctlv2_admin_machine_bmc.md index 6a8ae22..e0180ff 100644 --- a/docs/admin/metalctlv2_admin_machine_bmc.md +++ b/docs/admin/metalctlv2_admin_machine_bmc.md @@ -24,6 +24,7 @@ get and list machine bmc/ipmi information ### SEE ALSO * [metalctlv2 admin machine](metalctlv2_admin_machine.md) - manage machine entities +* [metalctlv2 admin machine bmc command](metalctlv2_admin_machine_bmc_command.md) - send a command to the bmc of a machine * [metalctlv2 admin machine bmc get](metalctlv2_admin_machine_bmc_get.md) - get the bmc of a machine * [metalctlv2 admin machine bmc list](metalctlv2_admin_machine_bmc_list.md) - list the bmc of machines diff --git a/docs/admin/metalctlv2_admin_machine_bmc-command.md b/docs/admin/metalctlv2_admin_machine_bmc_command.md similarity index 79% rename from docs/admin/metalctlv2_admin_machine_bmc-command.md rename to docs/admin/metalctlv2_admin_machine_bmc_command.md index 66a1d2d..8269867 100644 --- a/docs/admin/metalctlv2_admin_machine_bmc-command.md +++ b/docs/admin/metalctlv2_admin_machine_bmc_command.md @@ -1,16 +1,16 @@ -## metalctlv2 admin machine bmc-command +## metalctlv2 admin machine bmc command send a command to the bmc of a machine ``` -metalctlv2 admin machine bmc-command [flags] +metalctlv2 admin machine bmc command [flags] ``` ### Options ``` --command string the actual command to send to the machine - -h, --help help for bmc-command + -h, --help help for command ``` ### Options inherited from parent commands @@ -28,5 +28,5 @@ metalctlv2 admin machine bmc-command [flags] ### SEE ALSO -* [metalctlv2 admin machine](metalctlv2_admin_machine.md) - manage machine entities +* [metalctlv2 admin machine bmc](metalctlv2_admin_machine_bmc.md) - get and list machine bmc/ipmi information diff --git a/docs/admin/metalctlv2_admin_machine_bmc_list.md b/docs/admin/metalctlv2_admin_machine_bmc_list.md index a8524d1..c87f530 100644 --- a/docs/admin/metalctlv2_admin_machine_bmc_list.md +++ b/docs/admin/metalctlv2_admin_machine_bmc_list.md @@ -10,8 +10,10 @@ metalctlv2 admin machine bmc list [flags] ``` -h, --help help for list + --hostname string hostname from machines which should be listed --id string id of machine which should be listed --image string image + --name string name from machines which should be listed --partition string partition from where machines should be listed -p, --project string project from where machines should be listed --size string size from machines which should be listed diff --git a/docs/admin/metalctlv2_admin_machine_create.md b/docs/admin/metalctlv2_admin_machine_create.md index 8145e72..2076e74 100644 --- a/docs/admin/metalctlv2_admin_machine_create.md +++ b/docs/admin/metalctlv2_admin_machine_create.md @@ -78,7 +78,7 @@ Once created the machine installation can not be modified anymore. --ntp-servers strings ntp servers to add to the machine or firewall. [optional] --partition string partition/datacenter where the machine is created. [required, except for reserved machines] - --placement-tags strings placement tags used for rack spreading + --placement-labels strings placement tags used for rack spreading -p, --project string Project where the machine should belong to. [required] --size string Size of the machine. [required, except for reserved machines] --skip-security-prompts skips security prompt for bulk operations diff --git a/docs/metalctlv2_machine_create.md b/docs/metalctlv2_machine_create.md index e789919..325fcf1 100644 --- a/docs/metalctlv2_machine_create.md +++ b/docs/metalctlv2_machine_create.md @@ -37,7 +37,7 @@ metalctlv2 machine create [flags] --ntp-servers strings ntp servers to add to the machine or firewall. [optional] --partition string partition/datacenter where the machine is created. [required, except for reserved machines] - --placement-tags strings placement tags used for rack spreading + --placement-labels strings placement tags used for rack spreading -p, --project string Project where the machine should belong to. [required] --size string Size of the machine. [required, except for reserved machines] --skip-security-prompts skips security prompt for bulk operations diff --git a/go.mod b/go.mod index eb2fabe..b6beb7d 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/fatih/color v1.19.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/metal-stack/api v0.4.4 + github.com/metal-stack/api v0.5.0 github.com/metal-stack/metal-lib v0.26.3 github.com/metal-stack/v v1.0.3 github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.0 google.golang.org/grpc v1.83.0 google.golang.org/protobuf v1.36.12 sigs.k8s.io/yaml v1.6.0 @@ -37,7 +37,6 @@ require ( github.com/coder/websocket v1.8.15 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/creachadair/msync v0.10.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dblohm7/wingoes v0.0.0-20260526185140-fb298caac7ca // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/fxamacker/cbor/v2 v2.9.2 // indirect @@ -74,7 +73,6 @@ require ( github.com/olekukonko/tablewriter v1.1.4 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/pires/go-proxyproto v0.15.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/safchain/ethtool v0.7.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect diff --git a/go.sum b/go.sum index 11b6c18..52f203c 100644 --- a/go.sum +++ b/go.sum @@ -85,8 +85,6 @@ github.com/creachadair/taskgroup v0.13.2 h1:3KyqakBuFsm3KkXi/9XIb0QcA8tEzLHLgaoi github.com/creachadair/taskgroup v0.13.2/go.mod h1:i3V1Zx7H8RjwljUEeUWYT30Lmb9poewSb2XI1yTwD0g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dblohm7/wingoes v0.0.0-20260526185140-fb298caac7ca h1:h1Awca4lQOspNR/2eeo04Ricn5NixDX9mb17WSAgLhQ= github.com/dblohm7/wingoes v0.0.0-20260526185140-fb298caac7ca/go.mod h1:2TGl1jRJrRpbzykmg7asHm3h08TqutUgQqY5v9k/g3c= github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8= @@ -193,8 +191,8 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A= github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg= -github.com/metal-stack/api v0.4.4 h1:NwJKCFHnbmsU7DjHLtBmpdPuPniXc2wDabHjdpXk3qA= -github.com/metal-stack/api v0.4.4/go.mod h1:E7f2GkKNSr4vBxhQwWrr/Mjnf4ctPqCX2dpDtXBqkBk= +github.com/metal-stack/api v0.5.0 h1:AtQUo2s4UjVCVXyOJQDxDaQnDmyzsS/TRTHe75nhibs= +github.com/metal-stack/api v0.5.0/go.mod h1:QbDG4YRFIkDf1z6cGmeu0m/791IAMyk8/9U/lsrxH8E= github.com/metal-stack/metal-lib v0.26.3 h1:K5gLoD65m6p3l6qCPrfavIdvNdWfmF2QdXrvU2URaZs= github.com/metal-stack/metal-lib v0.26.3/go.mod h1:cNXjPBs8SFnjqfBobuSbm5mDk6E/jS8PVeIOrV/7POE= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= @@ -228,8 +226,6 @@ github.com/pires/go-proxyproto v0.15.0 h1:dTshmNbFm/D+0+sbrxUuddPOZ5Y0B7c5NhtsBk github.com/pires/go-proxyproto v0.15.0/go.mod h1:OXsCrKwrK2tXS9YrI5tkHx5xaQlO8FH3lFW76orFh24= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.69.0 h1:OA85nJQS/T/MaYh/Q2CcgDKSGWqNIgrBDvDH85CuiNk= @@ -257,8 +253,8 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI= +github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw= github.com/studio-b12/gowebdav v0.13.0 h1:OcwSg6IQHOFNdYHn3bPOHwSE8looG8N56Y5xTT1asqQ= github.com/studio-b12/gowebdav v0.13.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= diff --git a/pkg/helpers/emoji.go b/pkg/helpers/emoji.go index 8914516..70f7d42 100644 --- a/pkg/helpers/emoji.go +++ b/pkg/helpers/emoji.go @@ -11,7 +11,7 @@ const ( VPN = "🛡" ) -func EmojiHelpText() string { +func MachineListEmojiHelpText() string { return ` Meaning of the emojis: diff --git a/pkg/helpers/machine.go b/pkg/helpers/machine.go index 1cd547d..71c4a68 100644 --- a/pkg/helpers/machine.go +++ b/pkg/helpers/machine.go @@ -154,6 +154,11 @@ func MachineCreateRequestFromCLI(c *config.Config) (*apiv2.MachineServiceCreateR return nil, err } + placementLabels, err := LabelsFromSlice(viper.GetStringSlice("placement-labels")) + if err != nil { + return nil, err + } + var filesystemlayout *string if viper.IsSet("filesystem-layout") { filesystemlayout = new(viper.GetString("filesystem-layout")) @@ -190,7 +195,7 @@ func MachineCreateRequestFromCLI(c *config.Config) (*apiv2.MachineServiceCreateR DnsServers: dnsServers, NtpServers: ntpServers, FilesystemLayout: filesystemlayout, - PlacementTags: viper.GetStringSlice("placement-tags"), + PlacementLabels: placementLabels, AllocationType: allocationType, FirewallSpec: firewallSpec, }, nil @@ -342,7 +347,7 @@ Can be either the userdata as string, or pointing to the userdata file to use e. NETWORK specifies the name or id of an existing network. IPs can be added per network colon separated, these ips must be already allocated upfront. If no ip(s) are specified per network, one ip per network is allocated. `) - cmd.Flags().StringSlice("placement-tags", []string{}, "placement tags used for rack spreading") + cmd.Flags().StringSlice("placement-labels", []string{}, "placement tags used for rack spreading") cmd.MarkFlagsMutuallyExclusive("file", "project") cmd.MarkFlagsRequiredTogether("project", "networks", "hostname", "image") diff --git a/tests/e2e/admin/machine_test.go b/tests/e2e/admin/machine_test.go index efa33e3..06ab5fa 100644 --- a/tests/e2e/admin/machine_test.go +++ b/tests/e2e/admin/machine_test.go @@ -168,7 +168,7 @@ func Test_MachineCmd_Create(t *testing.T) { "--ssh-public-key", "@.ssh/id_rsa.pub", "--labels", "a=b", "--userdata", "@ignition.json", - "--placement-tags", "cluster-id=cluster-uuid", + "--placement-labels", "cluster-id=cluster-uuid", }, AssertExhaustiveArgs: true, AssertExhaustiveExcludes: e2e.CommonExcludedFileArgs(), @@ -208,7 +208,11 @@ func Test_MachineCmd_Create(t *testing.T) { return nws }(), - PlacementTags: []string{"cluster-id=cluster-uuid"}, + PlacementLabels: &apiv2.Labels{ + Labels: map[string]string{ + "cluster-id": "cluster-uuid", + }, + }, DnsServers: []*apiv2.DNSServer{{Ip: "1.1.1.1"}}, NtpServers: []*apiv2.NTPServer{{Address: "2.2.2.2"}, {Address: "3.3.3.3"}}, AllocationType: apiv2.MachineAllocationType_MACHINE_ALLOCATION_TYPE_MACHINE, @@ -544,3 +548,73 @@ func Test_MachineCmd_Apply(t *testing.T) { tt.TestCmd(t) } } + +func Test_MachineCmd_BmcList(t *testing.T) { + tests := []*e2e.Test[apiv2.MachineServiceListResponse, apiv2.Machine]{ + { + Name: "list", + CmdArgs: []string{"admin", "machine", "bmc", "list", + "--name", "name", + "--hostname", "hostname", + "--image", "image", + "--partition", "partition", + "--project", "project", + "--size", "size", + "--id", "uuid", + }, + AssertExhaustiveArgs: true, + AssertExhaustiveExcludes: []string{"sort-by"}, + NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.MachineServiceListBMCRequest{ + Query: &apiv2.MachineQuery{ + Allocation: &apiv2.MachineAllocationQuery{ + Hostname: new("hostname"), + Name: new("name"), + Image: new("image"), + Project: new("project"), + }, + Partition: new("partition"), + Size: new("size"), + Uuid: new("uuid"), + }, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.MachineServiceListBMCResponse{ + BmcDetails: []*apiv2.MachineBMCDetails{ + testresources.Machine2BmcDetails, + testresources.Machine1BmcDetails, + }, + }) + }, + }, + }, + }), + WantTable: new(` + ID POWER IP MAC BOARD PART NUMBER BIOS BMC SIZE PARTITION RACK UPDATED + 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd ⏾ 0W 10.0.0.2:623 02:00:00:00:00:02 Board-PN-2 2.0.0 2.4.0 v1-medium-x86 partition-2 rack-1 2m ago + 5fa2bbe1-407c-4142-92d5-e4419daf9646 🟒 ⏾ 120W 10.0.0.1:623 02:00:00:00:00:01 Board-PN-1 1.5.6 3.1.1 v1-medium-x86 partition-1 rack-1 1m ago + `), + WantWideTable: new(` + ID LED POWER IP MAC BOARD PART NUMBER CHASSIS SERIAL PRODUCT SERIAL BIOS VERSION BMC VERSION SIZE PARTITION RACK UPDATED + 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd LED-OFF off Power Supply Warning Absent 0W 10.0.0.2:623 02:00:00:00:00:02 Board-PN-2 Chassis-SN-2 Product-SN-2 2.0.0 2.4.0 v1-medium-x86 partition-2 rack-1 2m ago + 5fa2bbe1-407c-4142-92d5-e4419daf9646 LED-ON on On On 120W 10.0.0.1:623 02:00:00:00:00:01 Board-PN-1 Chassis-SN-1 Product-SN-1 1.5.6 3.1.1 v1-medium-x86 partition-1 rack-1 1m ago + `), + Template: new("{{ .uuid }} {{ .bmc_report.bmc.address }}"), + WantTemplate: new(` +673fc473-63ca-4ea4-b9dd-b45cb2127a6fd 10.0.0.2:623 +5fa2bbe1-407c-4142-92d5-e4419daf9646 10.0.0.1:623 + `), + WantMarkdown: new(` + | ID | | POWER | IP | MAC | BOARD PART NUMBER | BIOS | BMC | SIZE | PARTITION | RACK | UPDATED | + |---------------------------------------|---|--------|--------------|-------------------|-------------------|-------|-------|---------------|-------------|--------|---------| + | 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd | | ⏾ 0W | 10.0.0.2:623 | 02:00:00:00:00:02 | Board-PN-2 | 2.0.0 | 2.4.0 | v1-medium-x86 | partition-2 | rack-1 | 2m ago | + | 5fa2bbe1-407c-4142-92d5-e4419daf9646 | 🟒 | ⏾ 120W | 10.0.0.1:623 | 02:00:00:00:00:01 | Board-PN-1 | 1.5.6 | 3.1.1 | v1-medium-x86 | partition-1 | rack-1 | 1m ago | + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} diff --git a/tests/e2e/api/machine_test.go b/tests/e2e/api/machine_test.go index 079a713..97acf60 100644 --- a/tests/e2e/api/machine_test.go +++ b/tests/e2e/api/machine_test.go @@ -143,7 +143,7 @@ func Test_MachineCmd_Create(t *testing.T) { "--ssh-public-key", "@.ssh/id_rsa.pub", "--labels", "a=b", "--userdata", "@ignition.json", - "--placement-tags", "cluster-id=cluster-uuid", + "--placement-labels", "cluster-id=cluster-uuid", }, AssertExhaustiveArgs: true, AssertExhaustiveExcludes: e2e.CommonExcludedFileArgs(), @@ -182,8 +182,11 @@ func Test_MachineCmd_Create(t *testing.T) { return nws }(), - PlacementTags: []string{"cluster-id=cluster-uuid"}, - DnsServers: []*apiv2.DNSServer{{Ip: "1.1.1.1"}}, + PlacementLabels: &apiv2.Labels{ + Labels: map[string]string{ + "cluster-id": "cluster-uuid", + }, + }, DnsServers: []*apiv2.DNSServer{{Ip: "1.1.1.1"}}, NtpServers: []*apiv2.NTPServer{{Address: "2.2.2.2"}, {Address: "3.3.3.3"}}, AllocationType: apiv2.MachineAllocationType_MACHINE_ALLOCATION_TYPE_MACHINE, FirewallSpec: nil, diff --git a/tests/e2e/testresources/machine.go b/tests/e2e/testresources/machine.go index 68089c9..fb4fb4f 100644 --- a/tests/e2e/testresources/machine.go +++ b/tests/e2e/testresources/machine.go @@ -179,4 +179,117 @@ var ( }, } } + + Machine1BmcDetails = &apiv2.MachineBMCDetails{ + Uuid: Machine1().Uuid, + Partition: Machine1().Partition.Id, + Rack: Machine1().Rack, + Room: Machine1().Room, + Size: Machine1().Size.Id, + BmcReport: &apiv2.MachineBMCReport{ + Uuid: Machine1().Uuid, + Bmc: &apiv2.MachineBMC{ + Address: "10.0.0.1:623", + Mac: "02:00:00:00:00:01", + User: "admin", + Password: "password", + Interface: "lanplus", + Version: "3.1.1", + PowerState: "on", + }, + Bios: &apiv2.MachineBios{ + Version: "1.5.6", + Vendor: "American Megatrends Inc.", + Date: "04/01/2024", + }, + Fru: &apiv2.MachineFRU{ + ChassisPartNumber: newString("Chassis-PN-1"), + ChassisPartSerial: newString("Chassis-SN-1"), + BoardMfg: newString("Supermicro"), + BoardMfgSerial: newString("Board-SN-1"), + BoardPartNumber: newString("Board-PN-1"), + ProductManufacturer: newString("Dell"), + ProductPartNumber: newString("Product-PN-1"), + ProductSerial: newString("Product-SN-1"), + }, + PowerMetric: &apiv2.MachinePowerMetric{ + AverageConsumedWatts: 120.5, + IntervalInMin: 5, + MaxConsumedWatts: 180.2, + MinConsumedWatts: 95.3, + }, + PowerSupplies: []*apiv2.MachinePowerSupply{ + { + Health: "OK", + State: "On", + }, + { + Health: "OK", + State: "On", + }, + }, + LedState: &apiv2.MachineChassisIdentifyLEDState{ + Value: "LED-ON", + Description: "User initiated chassis identify", + }, + UpdatedAt: timestamppb.New(e2e.TimeBubbleStartTime().Add(-1 * time.Minute)), + }, + } + Machine2BmcDetails = &apiv2.MachineBMCDetails{ + Uuid: Machine2().Uuid, + Partition: Machine2().Partition.Id, + Rack: Machine2().Rack, + Room: Machine2().Room, + Size: Machine2().Size.Id, + BmcReport: &apiv2.MachineBMCReport{ + Uuid: Machine2().Uuid, + Bmc: &apiv2.MachineBMC{ + Address: "10.0.0.2:623", + Mac: "02:00:00:00:00:02", + User: "root", + Password: "changeme", + Interface: "lanplus", + Version: "2.4.0", + PowerState: "off", + }, + Bios: &apiv2.MachineBios{ + Version: "2.0.0", + Vendor: "Supermicro", + Date: "10/15/2023", + }, + Fru: &apiv2.MachineFRU{ + ChassisPartNumber: newString("Chassis-PN-2"), + ChassisPartSerial: newString("Chassis-SN-2"), + BoardMfg: newString("Supermicro"), + BoardMfgSerial: newString("Board-SN-2"), + BoardPartNumber: newString("Board-PN-2"), + ProductManufacturer: newString("HPE"), + ProductPartNumber: newString("Product-PN-2"), + ProductSerial: newString("Product-SN-2"), + }, + PowerMetric: &apiv2.MachinePowerMetric{ + AverageConsumedWatts: 0, + IntervalInMin: 0, + MaxConsumedWatts: 0, + MinConsumedWatts: 0, + }, + PowerSupplies: []*apiv2.MachinePowerSupply{ + { + Health: "Warning", + State: "Absent", + }, + }, + LedState: &apiv2.MachineChassisIdentifyLEDState{ + Value: "LED-OFF", + Description: "", + }, + UpdatedAt: timestamppb.New(e2e.TimeBubbleStartTime().Add(-2 * time.Minute)), + }, + } ) + +func newString(v string) *string { + s := new(string) + *s = v + return s +} diff --git a/tests/e2e/testresources/switch.go b/tests/e2e/testresources/switch.go index 09ad012..ac51006 100644 --- a/tests/e2e/testresources/switch.go +++ b/tests/e2e/testresources/switch.go @@ -68,7 +68,7 @@ var ( return &apiv2.SwitchNic{ Name: "Ethernet0", Identifier: "oid:0x1000000000001", - Mac: "52:54:00:ab:cd:01", + Mac: new("52:54:00:ab:cd:01"), Vrf: new("default"), State: &apiv2.NicState{ Desired: new(apiv2.SwitchPortStatus_SWITCH_PORT_STATUS_UP), @@ -99,7 +99,7 @@ var ( return &apiv2.SwitchNic{ Name: "Ethernet4", Identifier: "oid:0x1000000000002", - Mac: "52:54:00:ab:cd:02", + Mac: new("52:54:00:ab:cd:02"), State: &apiv2.NicState{ Desired: new(apiv2.SwitchPortStatus_SWITCH_PORT_STATUS_UP), Actual: apiv2.SwitchPortStatus_SWITCH_PORT_STATUS_DOWN, From 1143a6edbcbff93d014e8f5c4a5aae26b8277136 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 10:48:18 +0200 Subject: [PATCH 2/9] Comment. --- cmd/tableprinters/machine.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/tableprinters/machine.go b/cmd/tableprinters/machine.go index 54bda35..86458ba 100644 --- a/cmd/tableprinters/machine.go +++ b/cmd/tableprinters/machine.go @@ -170,6 +170,7 @@ func (t *TablePrinter) MachineBMCTable(data []*apiv2.MachineBMCDetails, wide boo } } + // this condition is quite brittle, unfortunately there is no enum in the api for this field if strings.EqualFold(ledState, "LED-ON") { blue := color.New(color.FgBlue).SprintFunc() emojis = append(emojis, blue(ledon)) From 88506cbe9956df44d0a6f63a40b256c3c07a5691 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 11:02:38 +0200 Subject: [PATCH 3/9] More tests and features. --- cmd/admin/v2/machine.go | 36 +++++---- cmd/sorters/machine.go | 20 +++++ docs/admin/metalctlv2_admin_machine_bmc.md | 2 +- ... metalctlv2_admin_machine_bmc_describe.md} | 6 +- .../metalctlv2_admin_machine_bmc_list.md | 1 + tests/e2e/admin/machine_test.go | 79 +++++++++++++++++-- 6 files changed, 121 insertions(+), 23 deletions(-) rename docs/admin/{metalctlv2_admin_machine_bmc_get.md => metalctlv2_admin_machine_bmc_describe.md} (88%) diff --git a/cmd/admin/v2/machine.go b/cmd/admin/v2/machine.go index 078bef4..8f87c00 100644 --- a/cmd/admin/v2/machine.go +++ b/cmd/admin/v2/machine.go @@ -132,6 +132,12 @@ If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picke ValidArgsFn: c.Completion.AdminMachine, } + bmcCmd := &cobra.Command{ + Use: "bmc", + Aliases: []string{"ipmi"}, + Short: "get and list machine bmc/ipmi information", + } + bmcCommandCmd := &cobra.Command{ Use: "command", Short: "send a command to the bmc of a machine", @@ -144,15 +150,10 @@ If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picke genericcli.Must(bmcCommandCmd.RegisterFlagCompletionFunc("command", c.Completion.BMCCommands)) genericcli.Must(bmcCommandCmd.MarkFlagRequired("command")) - bmcCmd := &cobra.Command{ - Use: "bmc", - Aliases: []string{"ipmi"}, - Short: "get and list machine bmc/ipmi information", - } - - bmcGetCmd := &cobra.Command{ - Use: "get", - Short: "get the bmc of a machine", + bmcDescribeCmd := &cobra.Command{ + Use: "describe", + Aliases: []string{"get"}, + Short: "get the bmc of a machine", RunE: func(cmd *cobra.Command, args []string) error { return w.bmcGet(cmd.Context(), args) }, @@ -168,8 +169,9 @@ If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picke } w.addMachineQueryFlags(bmcListCmd) + genericcli.AddSortFlag(bmcListCmd, sorters.MachineBmcSorter()) - bmcCmd.AddCommand(bmcGetCmd, bmcListCmd, bmcCommandCmd) + bmcCmd.AddCommand(bmcDescribeCmd, bmcListCmd, bmcCommandCmd) lockCmd := &cobra.Command{ Use: "lock", @@ -387,15 +389,22 @@ func (c *machine) bmcGet(ctx context.Context, args []string) error { } func (c *machine) bmcList(ctx context.Context) error { - req := &adminv2.MachineServiceListBMCRequest{ - Query: machineQuery(), + sortKeys, err := genericcli.ParseSortFlags() + if err != nil { + return err } - resp, err := c.c.Client.Adminv2().Machine().ListBMC(ctx, req) + resp, err := c.c.Client.Adminv2().Machine().ListBMC(ctx, &adminv2.MachineServiceListBMCRequest{ + Query: machineQuery(), + }) if err != nil { return err } + if err := sorters.MachineBmcSorter().SortBy(resp.BmcDetails, sortKeys...); err != nil { + return err + } + return c.c.ListPrinter.Print(resp.BmcDetails) } @@ -625,7 +634,6 @@ func (c *machine) addMachineQueryFlags(cmd *cobra.Command) { genericcli.Must(cmd.RegisterFlagCompletionFunc("image", c.c.Completion.Image)) genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", c.c.Completion.Partition)) genericcli.Must(cmd.RegisterFlagCompletionFunc("id", c.c.Completion.AdminMachine)) - } func machineQuery() *apiv2.MachineQuery { diff --git a/cmd/sorters/machine.go b/cmd/sorters/machine.go index 064ed1d..f0058ca 100644 --- a/cmd/sorters/machine.go +++ b/cmd/sorters/machine.go @@ -35,3 +35,23 @@ func MachineSorter() *multisort.Sorter[*apiv2.Machine] { }, }, multisort.Keys{{ID: "partition"}, {ID: "size"}, {ID: "project"}, {ID: "uuid"}}) } + +func MachineBmcSorter() *multisort.Sorter[*apiv2.MachineBMCDetails] { + return multisort.New(multisort.FieldMap[*apiv2.MachineBMCDetails]{ + "uuid": func(a, b *apiv2.MachineBMCDetails, descending bool) multisort.CompareResult { + return multisort.Compare(a.Uuid, b.Uuid, descending) + }, + "partition": func(a, b *apiv2.MachineBMCDetails, descending bool) multisort.CompareResult { + return multisort.Compare(a.Partition, b.Partition, descending) + }, + "size": func(a, b *apiv2.MachineBMCDetails, descending bool) multisort.CompareResult { + return multisort.Compare(a.Size, b.Size, descending) + }, + "rack": func(a, b *apiv2.MachineBMCDetails, descending bool) multisort.CompareResult { + return multisort.Compare(a.Rack, b.Rack, descending) + }, + "room": func(a, b *apiv2.MachineBMCDetails, descending bool) multisort.CompareResult { + return multisort.Compare(a.Room, b.Room, descending) + }, + }, multisort.Keys{{ID: "partition"}, {ID: "size"}, {ID: "uuid"}}) +} diff --git a/docs/admin/metalctlv2_admin_machine_bmc.md b/docs/admin/metalctlv2_admin_machine_bmc.md index e0180ff..0fbabd4 100644 --- a/docs/admin/metalctlv2_admin_machine_bmc.md +++ b/docs/admin/metalctlv2_admin_machine_bmc.md @@ -25,6 +25,6 @@ get and list machine bmc/ipmi information * [metalctlv2 admin machine](metalctlv2_admin_machine.md) - manage machine entities * [metalctlv2 admin machine bmc command](metalctlv2_admin_machine_bmc_command.md) - send a command to the bmc of a machine -* [metalctlv2 admin machine bmc get](metalctlv2_admin_machine_bmc_get.md) - get the bmc of a machine +* [metalctlv2 admin machine bmc describe](metalctlv2_admin_machine_bmc_describe.md) - get the bmc of a machine * [metalctlv2 admin machine bmc list](metalctlv2_admin_machine_bmc_list.md) - list the bmc of machines diff --git a/docs/admin/metalctlv2_admin_machine_bmc_get.md b/docs/admin/metalctlv2_admin_machine_bmc_describe.md similarity index 88% rename from docs/admin/metalctlv2_admin_machine_bmc_get.md rename to docs/admin/metalctlv2_admin_machine_bmc_describe.md index 3ad6f5e..66b490b 100644 --- a/docs/admin/metalctlv2_admin_machine_bmc_get.md +++ b/docs/admin/metalctlv2_admin_machine_bmc_describe.md @@ -1,15 +1,15 @@ -## metalctlv2 admin machine bmc get +## metalctlv2 admin machine bmc describe get the bmc of a machine ``` -metalctlv2 admin machine bmc get [flags] +metalctlv2 admin machine bmc describe [flags] ``` ### Options ``` - -h, --help help for get + -h, --help help for describe ``` ### Options inherited from parent commands diff --git a/docs/admin/metalctlv2_admin_machine_bmc_list.md b/docs/admin/metalctlv2_admin_machine_bmc_list.md index c87f530..e7f9d52 100644 --- a/docs/admin/metalctlv2_admin_machine_bmc_list.md +++ b/docs/admin/metalctlv2_admin_machine_bmc_list.md @@ -17,6 +17,7 @@ metalctlv2 admin machine bmc list [flags] --partition string partition from where machines should be listed -p, --project string project from where machines should be listed --size string size from machines which should be listed + --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: partition|rack|room|size|uuid ``` ### Options inherited from parent commands diff --git a/tests/e2e/admin/machine_test.go b/tests/e2e/admin/machine_test.go index 06ab5fa..7e2afb2 100644 --- a/tests/e2e/admin/machine_test.go +++ b/tests/e2e/admin/machine_test.go @@ -550,7 +550,7 @@ func Test_MachineCmd_Apply(t *testing.T) { } func Test_MachineCmd_BmcList(t *testing.T) { - tests := []*e2e.Test[apiv2.MachineServiceListResponse, apiv2.Machine]{ + tests := []*e2e.Test[adminv2.MachineServiceListBMCResponse, apiv2.MachineBMCDetails]{ { Name: "list", CmdArgs: []string{"admin", "machine", "bmc", "list", @@ -593,24 +593,67 @@ func Test_MachineCmd_BmcList(t *testing.T) { }), WantTable: new(` ID POWER IP MAC BOARD PART NUMBER BIOS BMC SIZE PARTITION RACK UPDATED - 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd ⏾ 0W 10.0.0.2:623 02:00:00:00:00:02 Board-PN-2 2.0.0 2.4.0 v1-medium-x86 partition-2 rack-1 2m ago 5fa2bbe1-407c-4142-92d5-e4419daf9646 🟒 ⏾ 120W 10.0.0.1:623 02:00:00:00:00:01 Board-PN-1 1.5.6 3.1.1 v1-medium-x86 partition-1 rack-1 1m ago + 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd ⏾ 0W 10.0.0.2:623 02:00:00:00:00:02 Board-PN-2 2.0.0 2.4.0 v1-medium-x86 partition-2 rack-1 2m ago `), WantWideTable: new(` ID LED POWER IP MAC BOARD PART NUMBER CHASSIS SERIAL PRODUCT SERIAL BIOS VERSION BMC VERSION SIZE PARTITION RACK UPDATED - 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd LED-OFF off Power Supply Warning Absent 0W 10.0.0.2:623 02:00:00:00:00:02 Board-PN-2 Chassis-SN-2 Product-SN-2 2.0.0 2.4.0 v1-medium-x86 partition-2 rack-1 2m ago 5fa2bbe1-407c-4142-92d5-e4419daf9646 LED-ON on On On 120W 10.0.0.1:623 02:00:00:00:00:01 Board-PN-1 Chassis-SN-1 Product-SN-1 1.5.6 3.1.1 v1-medium-x86 partition-1 rack-1 1m ago + 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd LED-OFF off Power Supply Warning Absent 0W 10.0.0.2:623 02:00:00:00:00:02 Board-PN-2 Chassis-SN-2 Product-SN-2 2.0.0 2.4.0 v1-medium-x86 partition-2 rack-1 2m ago `), Template: new("{{ .uuid }} {{ .bmc_report.bmc.address }}"), WantTemplate: new(` -673fc473-63ca-4ea4-b9dd-b45cb2127a6fd 10.0.0.2:623 5fa2bbe1-407c-4142-92d5-e4419daf9646 10.0.0.1:623 +673fc473-63ca-4ea4-b9dd-b45cb2127a6fd 10.0.0.2:623 `), WantMarkdown: new(` | ID | | POWER | IP | MAC | BOARD PART NUMBER | BIOS | BMC | SIZE | PARTITION | RACK | UPDATED | |---------------------------------------|---|--------|--------------|-------------------|-------------------|-------|-------|---------------|-------------|--------|---------| - | 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd | | ⏾ 0W | 10.0.0.2:623 | 02:00:00:00:00:02 | Board-PN-2 | 2.0.0 | 2.4.0 | v1-medium-x86 | partition-2 | rack-1 | 2m ago | | 5fa2bbe1-407c-4142-92d5-e4419daf9646 | 🟒 | ⏾ 120W | 10.0.0.1:623 | 02:00:00:00:00:01 | Board-PN-1 | 1.5.6 | 3.1.1 | v1-medium-x86 | partition-1 | rack-1 | 1m ago | + | 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd | | ⏾ 0W | 10.0.0.2:623 | 02:00:00:00:00:02 | Board-PN-2 | 2.0.0 | 2.4.0 | v1-medium-x86 | partition-2 | rack-1 | 2m ago | + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_MachineCmd_BmcDescribe(t *testing.T) { + tests := []*e2e.Test[adminv2.MachineServiceListBMCResponse, apiv2.MachineBMCDetails]{ + { + Name: "describe", + CmdArgs: []string{"admin", "machine", "bmc", "describe", testresources.Machine1().Uuid}, + NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.MachineServiceGetBMCRequest{ + Uuid: testresources.Machine1().Uuid, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.MachineServiceGetBMCResponse{ + BmcDetails: testresources.Machine1BmcDetails, + }) + }, + }, + }, + }), + WantTable: new(` + ID POWER IP MAC BOARD PART NUMBER BIOS BMC SIZE PARTITION RACK UPDATED + 5fa2bbe1-407c-4142-92d5-e4419daf9646 🟒 ⏾ 120W 10.0.0.1:623 02:00:00:00:00:01 Board-PN-1 1.5.6 3.1.1 v1-medium-x86 partition-1 rack-1 1m ago + `), + WantWideTable: new(` + ID LED POWER IP MAC BOARD PART NUMBER CHASSIS SERIAL PRODUCT SERIAL BIOS VERSION BMC VERSION SIZE PARTITION RACK UPDATED + 5fa2bbe1-407c-4142-92d5-e4419daf9646 LED-ON on On On 120W 10.0.0.1:623 02:00:00:00:00:01 Board-PN-1 Chassis-SN-1 Product-SN-1 1.5.6 3.1.1 v1-medium-x86 partition-1 rack-1 1m ago + `), + Template: new("{{ .uuid }} {{ .bmc_report.bmc.address }}"), + WantTemplate: new(` + 5fa2bbe1-407c-4142-92d5-e4419daf9646 10.0.0.1:623 + `), + WantMarkdown: new(` + | ID | | POWER | IP | MAC | BOARD PART NUMBER | BIOS | BMC | SIZE | PARTITION | RACK | UPDATED | + |--------------------------------------|---|--------|--------------|-------------------|-------------------|-------|-------|---------------|-------------|--------|---------| + | 5fa2bbe1-407c-4142-92d5-e4419daf9646 | 🟒 | ⏾ 120W | 10.0.0.1:623 | 02:00:00:00:00:01 | Board-PN-1 | 1.5.6 | 3.1.1 | v1-medium-x86 | partition-1 | rack-1 | 1m ago | `), }, } @@ -618,3 +661,29 @@ func Test_MachineCmd_BmcList(t *testing.T) { tt.TestCmd(t) } } + +func Test_MachineCmd_BmcCommand(t *testing.T) { + tests := []*e2e.Test[adminv2.MachineServiceListBMCResponse, apiv2.MachineBMCDetails]{ + { + Name: "bmc command", + CmdArgs: []string{"admin", "machine", "bmc", "command", testresources.Machine1().Uuid, "--command", "MACHINE_BMC_COMMAND_ON"}, + NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.MachineServiceBMCCommandRequest{ + Uuid: testresources.Machine1().Uuid, + Command: apiv2.MachineBMCCommand_MACHINE_BMC_COMMAND_ON, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.MachineServiceBMCCommandResponse{}) + }, + }, + }, + }), + WantDefault: new(``), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} From 6bacb492f76567834459f8c33e50076ad9e5c511 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 11:13:31 +0200 Subject: [PATCH 4/9] Factor out machine query into helpers. --- cmd/admin/v2/machine.go | 69 ++------------------------------ cmd/api/v2/machine.go | 31 +-------------- docs/metalctlv2_machine_list.md | 2 +- pkg/helpers/machine.go | 70 +++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 95 deletions(-) diff --git a/cmd/admin/v2/machine.go b/cmd/admin/v2/machine.go index 8f87c00..5595548 100644 --- a/cmd/admin/v2/machine.go +++ b/cmd/admin/v2/machine.go @@ -46,7 +46,7 @@ func newMachineCmd(c *config.Config) *cobra.Command { DescribePrinter: func() printers.Printer { return c.DescribePrinter }, ListPrinter: func() printers.Printer { return c.ListPrinter }, ListCmdMutateFn: func(cmd *cobra.Command) { - w.addMachineQueryFlags(cmd) + helpers.AddMachineQueryFlags(cmd, c.Completion) cmd.Long = cmd.Short + "\n" + helpers.MachineListEmojiHelpText() }, @@ -168,7 +168,7 @@ If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picke }, } - w.addMachineQueryFlags(bmcListCmd) + helpers.AddMachineQueryFlags(bmcListCmd, c.Completion) genericcli.AddSortFlag(bmcListCmd, sorters.MachineBmcSorter()) bmcCmd.AddCommand(bmcDescribeCmd, bmcListCmd, bmcCommandCmd) @@ -280,7 +280,7 @@ func (c *machine) List() ([]*apiv2.Machine, error) { defer cancel() resp, err := c.c.Client.Adminv2().Machine().List(ctx, &adminv2.MachineServiceListRequest{ - Query: machineQuery(), + Query: helpers.MachineQuery(true), }) if err != nil { return nil, err @@ -395,7 +395,7 @@ func (c *machine) bmcList(ctx context.Context) error { } resp, err := c.c.Client.Adminv2().Machine().ListBMC(ctx, &adminv2.MachineServiceListBMCRequest{ - Query: machineQuery(), + Query: helpers.MachineQuery(true), }) if err != nil { return err @@ -619,64 +619,3 @@ func sshClient(user, keyfile, host string, port int, idToken *string, passwordAu return s.Connect(env) } - -func (c *machine) addMachineQueryFlags(cmd *cobra.Command) { - cmd.Flags().String("id", "", "id of machine which should be listed") - cmd.Flags().String("name", "", "name from machines which should be listed") - cmd.Flags().String("hostname", "", "hostname from machines which should be listed") - cmd.Flags().String("size", "", "size from machines which should be listed") - cmd.Flags().String("image", "", "image") - cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") - cmd.Flags().StringP("partition", "", "", "partition from where machines should be listed") - - genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.c.Completion.Project)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("size", c.c.Completion.Size)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("image", c.c.Completion.Image)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", c.c.Completion.Partition)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("id", c.c.Completion.AdminMachine)) -} - -func machineQuery() *apiv2.MachineQuery { - var allocation *apiv2.MachineAllocationQuery - - if viper.IsSet("hostname") || viper.IsSet("name") || viper.IsSet("project") || viper.IsSet("image") { - allocation = &apiv2.MachineAllocationQuery{ - Hostname: pointer.PointerOrNil(viper.GetString("hostname")), - Name: pointer.PointerOrNil(viper.GetString("name")), - Project: pointer.PointerOrNil(viper.GetString("project")), - Image: pointer.PointerOrNil(viper.GetString("image")), - } - } - - return &apiv2.MachineQuery{ - Uuid: pointer.PointerOrNil(viper.GetString("id")), - Partition: pointer.PointerOrNil(viper.GetString("partition")), - Size: pointer.PointerOrNil(viper.GetString("size")), - Allocation: allocation, - // Rack: pointer.PointerOrNil(viper.GetString("rack")), - // Labels: &apiv2.Labels{ - // Labels: tag.NewTagMap(viper.GetStringSlice("labels")), - // }, - // Bmc: &apiv2.MachineBMCQuery{ - // Address: pointer.PointerOrNil(viper.GetString("bmc-address")), - // Mac: pointer.PointerOrNil(viper.GetString("bmc-mac")), - // User: pointer.PointerOrNil(viper.GetString("bmc-user")), - // Interface: pointer.PointerOrNil(viper.GetString("bmc-interface")), - // }, - // Fru: &apiv2.MachineFRUQuery{ - // ChassisPartNumber: pointer.PointerOrNil(viper.GetString("chassis-part-number")), - // ChassisPartSerial: pointer.PointerOrNil(viper.GetString("chassis-part-serial")), - // BoardMfg: pointer.PointerOrNil(viper.GetString("board-mfg")), - // BoardSerial: pointer.PointerOrNil(viper.GetString("board-serial")), - // BoardPartNumber: pointer.PointerOrNil(viper.GetString("board-part-number")), - // ProductManufacturer: pointer.PointerOrNil(viper.GetString("product-manufacturer")), - // ProductPartNumber: pointer.PointerOrNil(viper.GetString("product-part-number")), - // ProductSerial: pointer.PointerOrNil(viper.GetString("product-serial")), - // }, - // Hardware: &apiv2.MachineHardwareQuery{ - // Memory: pointer.PointerOrNil(viper.GetUint64("memory")), - // CpuCores: pointer.PointerOrNil(viper.GetUint32("cpu-cores")), - // }, - // State: &0, - } -} diff --git a/cmd/api/v2/machine.go b/cmd/api/v2/machine.go index d5154ce..61a90dc 100644 --- a/cmd/api/v2/machine.go +++ b/cmd/api/v2/machine.go @@ -8,7 +8,6 @@ import ( "github.com/metal-stack/cli/pkg/helpers" "github.com/metal-stack/metal-lib/pkg/genericcli" "github.com/metal-stack/metal-lib/pkg/genericcli/printers" - "github.com/metal-stack/metal-lib/pkg/pointer" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -40,18 +39,7 @@ func newMachineCmd(c *config.Config) *cobra.Command { cmd.Aliases = []string{"allocate"} }, ListCmdMutateFn: func(cmd *cobra.Command) { - cmd.Flags().String("uuid", "", "allocation uuid of machine which should be listed") - cmd.Flags().String("name", "", "name from machines which should be listed") - cmd.Flags().String("hostname", "", "hostname from machines which should be listed") - cmd.Flags().String("size", "", "size from machines which should be listed") - cmd.Flags().String("image", "", "image") - cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") - cmd.Flags().StringP("partition", "", "", "partition from where machines should be listed") - - genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("size", c.Completion.Size)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("image", c.Completion.Image)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", c.Completion.Partition)) + helpers.AddMachineQueryFlags(cmd, c.Completion) cmd.Long = cmd.Short + "\n" + helpers.MachineListEmojiHelpText() }, @@ -147,24 +135,9 @@ func (c *machine) List() ([]*apiv2.Machine, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() - var allocation *apiv2.MachineAllocationQuery - - if viper.IsSet("hostname") || viper.IsSet("image") { - allocation = &apiv2.MachineAllocationQuery{ - Hostname: pointer.PointerOrNil(viper.GetString("hostname")), - Image: pointer.PointerOrNil(viper.GetString("image")), - } - } - resp, err := c.c.Client.Apiv2().Machine().List(ctx, &apiv2.MachineServiceListRequest{ Project: c.c.GetProject(), - Query: &apiv2.MachineQuery{ - Uuid: pointer.PointerOrNil(viper.GetString("id")), - Name: pointer.PointerOrNil(viper.GetString("name")), - Partition: pointer.PointerOrNil(viper.GetString("partition")), - Size: pointer.PointerOrNil(viper.GetString("size")), - Allocation: allocation, - }, + Query: helpers.MachineQuery(false), }) if err != nil { return nil, err diff --git a/docs/metalctlv2_machine_list.md b/docs/metalctlv2_machine_list.md index 0dd9063..9454f27 100644 --- a/docs/metalctlv2_machine_list.md +++ b/docs/metalctlv2_machine_list.md @@ -27,13 +27,13 @@ metalctlv2 machine list [flags] ``` -h, --help help for list --hostname string hostname from machines which should be listed + --id string id of machine which should be listed --image string image --name string name from machines which should be listed --partition string partition from where machines should be listed -p, --project string project from where machines should be listed --size string size from machines which should be listed --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: age|image|partition|project|rack|size|uuid - --uuid string allocation uuid of machine which should be listed ``` ### Options inherited from parent commands diff --git a/pkg/helpers/machine.go b/pkg/helpers/machine.go index 71c4a68..cd072ec 100644 --- a/pkg/helpers/machine.go +++ b/pkg/helpers/machine.go @@ -234,6 +234,76 @@ func MachineUpdateRequestFromCLI(c *config.Config, args []string) (*apiv2.Machin }, nil } +func AddMachineQueryFlags(cmd *cobra.Command, completion *completion.Completion) { + cmd.Flags().String("id", "", "id of machine which should be listed") + cmd.Flags().String("name", "", "name from machines which should be listed") + cmd.Flags().String("hostname", "", "hostname from machines which should be listed") + cmd.Flags().String("size", "", "size from machines which should be listed") + cmd.Flags().String("image", "", "image") + cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") + cmd.Flags().StringP("partition", "", "", "partition from where machines should be listed") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", completion.Project)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("size", completion.Size)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("image", completion.Image)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", completion.Partition)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("id", completion.AdminMachine)) +} + +// MachineQuery returns a machine query from the cmd flags added through AddMachineQueryFlags +// unscoped indicates an unscoped request made from the admin API, which has certain effects on building the query +func MachineQuery(unscoped bool) *apiv2.MachineQuery { + var allocation *apiv2.MachineAllocationQuery + + if viper.IsSet("hostname") || viper.IsSet("name") || viper.IsSet("image") { + allocation = &apiv2.MachineAllocationQuery{ + Hostname: pointer.PointerOrNil(viper.GetString("hostname")), + Name: pointer.PointerOrNil(viper.GetString("name")), + Image: pointer.PointerOrNil(viper.GetString("image")), + } + } + + if unscoped && viper.IsSet("project") { + if allocation == nil { + allocation = &apiv2.MachineAllocationQuery{} + } + + allocation.Project = pointer.PointerOrNil(viper.GetString("project")) + } + + return &apiv2.MachineQuery{ + Uuid: pointer.PointerOrNil(viper.GetString("id")), + Partition: pointer.PointerOrNil(viper.GetString("partition")), + Size: pointer.PointerOrNil(viper.GetString("size")), + Allocation: allocation, + // Rack: pointer.PointerOrNil(viper.GetString("rack")), + // Labels: &apiv2.Labels{ + // Labels: tag.NewTagMap(viper.GetStringSlice("labels")), + // }, + // Bmc: &apiv2.MachineBMCQuery{ + // Address: pointer.PointerOrNil(viper.GetString("bmc-address")), + // Mac: pointer.PointerOrNil(viper.GetString("bmc-mac")), + // User: pointer.PointerOrNil(viper.GetString("bmc-user")), + // Interface: pointer.PointerOrNil(viper.GetString("bmc-interface")), + // }, + // Fru: &apiv2.MachineFRUQuery{ + // ChassisPartNumber: pointer.PointerOrNil(viper.GetString("chassis-part-number")), + // ChassisPartSerial: pointer.PointerOrNil(viper.GetString("chassis-part-serial")), + // BoardMfg: pointer.PointerOrNil(viper.GetString("board-mfg")), + // BoardSerial: pointer.PointerOrNil(viper.GetString("board-serial")), + // BoardPartNumber: pointer.PointerOrNil(viper.GetString("board-part-number")), + // ProductManufacturer: pointer.PointerOrNil(viper.GetString("product-manufacturer")), + // ProductPartNumber: pointer.PointerOrNil(viper.GetString("product-part-number")), + // ProductSerial: pointer.PointerOrNil(viper.GetString("product-serial")), + // }, + // Hardware: &apiv2.MachineHardwareQuery{ + // Memory: pointer.PointerOrNil(viper.GetUint64("memory")), + // CpuCores: pointer.PointerOrNil(viper.GetUint32("cpu-cores")), + // }, + // State: &0, + } +} + var defaultSSHKeys = [...]string{"id_ed25519", "id_ecdsa", "id_rsa", "id_dsa"} func SearchSSHKey() (string, error) { From 5e609c55555b4c091cb187029ce3325dc9744fdd Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 11:27:13 +0200 Subject: [PATCH 5/9] Complete machine query. --- cmd/admin/v2/machine.go | 14 +- cmd/api/v2/machine.go | 7 +- .../metalctlv2_admin_machine_bmc_list.md | 57 ++++- docs/admin/metalctlv2_admin_machine_list.md | 57 ++++- docs/metalctlv2_machine_list.md | 57 ++++- pkg/helpers/machine.go | 220 +++++++++++++--- tests/e2e/admin/machine_test.go | 234 +++++++++++++++++- 7 files changed, 573 insertions(+), 73 deletions(-) diff --git a/cmd/admin/v2/machine.go b/cmd/admin/v2/machine.go index 5595548..c8cb0a3 100644 --- a/cmd/admin/v2/machine.go +++ b/cmd/admin/v2/machine.go @@ -279,8 +279,13 @@ func (c *machine) List() ([]*apiv2.Machine, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() + query, err := helpers.MachineQuery(true) + if err != nil { + return nil, err + } + resp, err := c.c.Client.Adminv2().Machine().List(ctx, &adminv2.MachineServiceListRequest{ - Query: helpers.MachineQuery(true), + Query: query, }) if err != nil { return nil, err @@ -394,8 +399,13 @@ func (c *machine) bmcList(ctx context.Context) error { return err } + query, err := helpers.MachineQuery(true) + if err != nil { + return err + } + resp, err := c.c.Client.Adminv2().Machine().ListBMC(ctx, &adminv2.MachineServiceListBMCRequest{ - Query: helpers.MachineQuery(true), + Query: query, }) if err != nil { return err diff --git a/cmd/api/v2/machine.go b/cmd/api/v2/machine.go index 61a90dc..cc7845e 100644 --- a/cmd/api/v2/machine.go +++ b/cmd/api/v2/machine.go @@ -135,9 +135,14 @@ func (c *machine) List() ([]*apiv2.Machine, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() + query, err := helpers.MachineQuery(false) + if err != nil { + return nil, err + } + resp, err := c.c.Client.Apiv2().Machine().List(ctx, &apiv2.MachineServiceListRequest{ Project: c.c.GetProject(), - Query: helpers.MachineQuery(false), + Query: query, }) if err != nil { return nil, err diff --git a/docs/admin/metalctlv2_admin_machine_bmc_list.md b/docs/admin/metalctlv2_admin_machine_bmc_list.md index e7f9d52..923358a 100644 --- a/docs/admin/metalctlv2_admin_machine_bmc_list.md +++ b/docs/admin/metalctlv2_admin_machine_bmc_list.md @@ -9,15 +9,54 @@ metalctlv2 admin machine bmc list [flags] ### Options ``` - -h, --help help for list - --hostname string hostname from machines which should be listed - --id string id of machine which should be listed - --image string image - --name string name from machines which should be listed - --partition string partition from where machines should be listed - -p, --project string project from where machines should be listed - --size string size from machines which should be listed - --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: partition|rack|room|size|uuid + --allocation-type string allocation type from machines which should be listed, e.g. machine|firewall + --bmc-address string bmc address from machines which should be listed + --bmc-interface string bmc interface from machines which should be listed + --bmc-mac string bmc mac from machines which should be listed + --bmc-user string bmc user from machines which should be listed + --board-mfg string board manufacturer from machines which should be listed + --board-part-number string board part number from machines which should be listed + --board-serial string board serial from machines which should be listed + --chassis-part-number string chassis part number from machines which should be listed + --chassis-part-serial string chassis part serial from machines which should be listed + --cpu-cores uint32 cpu cores from machines which should be listed + --disk-names strings disk names which machines should have + --disk-sizes ints disk sizes which machines should have + --filesystem-layout string filesystem layout from machines which should be listed + -h, --help help for list + --hostname string hostname from machines which should be listed + --id string id of machine which should be listed + --image string image + --labels strings labels to filter machines by, use it like: --labels "a=b" or --labels "a=". + --memory uint memory in bytes from machines which should be listed + --name string name from machines which should be listed + --network-asns ints network asns to which machines should be connected + --network-destination-prefixes strings network destination prefixes to which machines should be connected + --network-ips strings network ips which machines should have + --network-names strings network names to which machines should be connected + --network-prefixes strings network prefixes to which machines should be connected + --network-vrfs ints network vrfs to which machines should be connected + --nic-macs strings nic macs which machines should have + --nic-names strings nic names which machines should have + --nic-neighbor-macs strings nic neighbor macs which machines should have + --nic-neighbor-names strings nic neighbor names which machines should have + --not-allocated only list not allocated machines. [admin only] + --partition string partition from where machines should be listed + --preallocated only list preallocated machines. [admin only] + --product-manufacturer string product manufacturer from machines which should be listed + --product-part-number string product part number from machines which should be listed + --product-serial string product serial from machines which should be listed + -p, --project string project from where machines should be listed + --rack string rack from where machines should be listed + --room string room from where machines should be listed + --size string size from machines which should be listed + --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: partition|rack|room|size|uuid + --state string state from machines which should be listed, e.g. available|tainted|locked + --vpn-auth-key string vpn auth key from machines which should be listed + --vpn-connected only list machines which are connected to the vpn + --vpn-control-plane-address string vpn control plane address from machines which should be listed + --vpn-ips strings vpn ips which machines should have + --waiting only list waiting machines. [admin only] ``` ### Options inherited from parent commands diff --git a/docs/admin/metalctlv2_admin_machine_list.md b/docs/admin/metalctlv2_admin_machine_list.md index f2aac8a..46c8b5f 100644 --- a/docs/admin/metalctlv2_admin_machine_list.md +++ b/docs/admin/metalctlv2_admin_machine_list.md @@ -25,15 +25,54 @@ metalctlv2 admin machine list [flags] ### Options ``` - -h, --help help for list - --hostname string hostname from machines which should be listed - --id string id of machine which should be listed - --image string image - --name string name from machines which should be listed - --partition string partition from where machines should be listed - -p, --project string project from where machines should be listed - --size string size from machines which should be listed - --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: age|image|partition|project|rack|size|uuid + --allocation-type string allocation type from machines which should be listed, e.g. machine|firewall + --bmc-address string bmc address from machines which should be listed + --bmc-interface string bmc interface from machines which should be listed + --bmc-mac string bmc mac from machines which should be listed + --bmc-user string bmc user from machines which should be listed + --board-mfg string board manufacturer from machines which should be listed + --board-part-number string board part number from machines which should be listed + --board-serial string board serial from machines which should be listed + --chassis-part-number string chassis part number from machines which should be listed + --chassis-part-serial string chassis part serial from machines which should be listed + --cpu-cores uint32 cpu cores from machines which should be listed + --disk-names strings disk names which machines should have + --disk-sizes ints disk sizes which machines should have + --filesystem-layout string filesystem layout from machines which should be listed + -h, --help help for list + --hostname string hostname from machines which should be listed + --id string id of machine which should be listed + --image string image + --labels strings labels to filter machines by, use it like: --labels "a=b" or --labels "a=". + --memory uint memory in bytes from machines which should be listed + --name string name from machines which should be listed + --network-asns ints network asns to which machines should be connected + --network-destination-prefixes strings network destination prefixes to which machines should be connected + --network-ips strings network ips which machines should have + --network-names strings network names to which machines should be connected + --network-prefixes strings network prefixes to which machines should be connected + --network-vrfs ints network vrfs to which machines should be connected + --nic-macs strings nic macs which machines should have + --nic-names strings nic names which machines should have + --nic-neighbor-macs strings nic neighbor macs which machines should have + --nic-neighbor-names strings nic neighbor names which machines should have + --not-allocated only list not allocated machines. [admin only] + --partition string partition from where machines should be listed + --preallocated only list preallocated machines. [admin only] + --product-manufacturer string product manufacturer from machines which should be listed + --product-part-number string product part number from machines which should be listed + --product-serial string product serial from machines which should be listed + -p, --project string project from where machines should be listed + --rack string rack from where machines should be listed + --room string room from where machines should be listed + --size string size from machines which should be listed + --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: age|image|partition|project|rack|size|uuid + --state string state from machines which should be listed, e.g. available|tainted|locked + --vpn-auth-key string vpn auth key from machines which should be listed + --vpn-connected only list machines which are connected to the vpn + --vpn-control-plane-address string vpn control plane address from machines which should be listed + --vpn-ips strings vpn ips which machines should have + --waiting only list waiting machines. [admin only] ``` ### Options inherited from parent commands diff --git a/docs/metalctlv2_machine_list.md b/docs/metalctlv2_machine_list.md index 9454f27..832137c 100644 --- a/docs/metalctlv2_machine_list.md +++ b/docs/metalctlv2_machine_list.md @@ -25,15 +25,54 @@ metalctlv2 machine list [flags] ### Options ``` - -h, --help help for list - --hostname string hostname from machines which should be listed - --id string id of machine which should be listed - --image string image - --name string name from machines which should be listed - --partition string partition from where machines should be listed - -p, --project string project from where machines should be listed - --size string size from machines which should be listed - --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: age|image|partition|project|rack|size|uuid + --allocation-type string allocation type from machines which should be listed, e.g. machine|firewall + --bmc-address string bmc address from machines which should be listed + --bmc-interface string bmc interface from machines which should be listed + --bmc-mac string bmc mac from machines which should be listed + --bmc-user string bmc user from machines which should be listed + --board-mfg string board manufacturer from machines which should be listed + --board-part-number string board part number from machines which should be listed + --board-serial string board serial from machines which should be listed + --chassis-part-number string chassis part number from machines which should be listed + --chassis-part-serial string chassis part serial from machines which should be listed + --cpu-cores uint32 cpu cores from machines which should be listed + --disk-names strings disk names which machines should have + --disk-sizes ints disk sizes which machines should have + --filesystem-layout string filesystem layout from machines which should be listed + -h, --help help for list + --hostname string hostname from machines which should be listed + --id string id of machine which should be listed + --image string image + --labels strings labels to filter machines by, use it like: --labels "a=b" or --labels "a=". + --memory uint memory in bytes from machines which should be listed + --name string name from machines which should be listed + --network-asns ints network asns to which machines should be connected + --network-destination-prefixes strings network destination prefixes to which machines should be connected + --network-ips strings network ips which machines should have + --network-names strings network names to which machines should be connected + --network-prefixes strings network prefixes to which machines should be connected + --network-vrfs ints network vrfs to which machines should be connected + --nic-macs strings nic macs which machines should have + --nic-names strings nic names which machines should have + --nic-neighbor-macs strings nic neighbor macs which machines should have + --nic-neighbor-names strings nic neighbor names which machines should have + --not-allocated only list not allocated machines. [admin only] + --partition string partition from where machines should be listed + --preallocated only list preallocated machines. [admin only] + --product-manufacturer string product manufacturer from machines which should be listed + --product-part-number string product part number from machines which should be listed + --product-serial string product serial from machines which should be listed + -p, --project string project from where machines should be listed + --rack string rack from where machines should be listed + --room string room from where machines should be listed + --size string size from machines which should be listed + --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: age|image|partition|project|rack|size|uuid + --state string state from machines which should be listed, e.g. available|tainted|locked + --vpn-auth-key string vpn auth key from machines which should be listed + --vpn-connected only list machines which are connected to the vpn + --vpn-control-plane-address string vpn control plane address from machines which should be listed + --vpn-ips strings vpn ips which machines should have + --waiting only list waiting machines. [admin only] ``` ### Options inherited from parent commands diff --git a/pkg/helpers/machine.go b/pkg/helpers/machine.go index cd072ec..0be30c4 100644 --- a/pkg/helpers/machine.go +++ b/pkg/helpers/machine.go @@ -9,11 +9,13 @@ import ( "path/filepath" "strings" + "github.com/metal-stack/api/go/enum" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" "github.com/metal-stack/cli/cmd/completion" "github.com/metal-stack/cli/cmd/config" "github.com/metal-stack/metal-lib/pkg/genericcli" "github.com/metal-stack/metal-lib/pkg/pointer" + tag "github.com/metal-stack/metal-lib/pkg/tag" "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -242,6 +244,45 @@ func AddMachineQueryFlags(cmd *cobra.Command, completion *completion.Completion) cmd.Flags().String("image", "", "image") cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") cmd.Flags().StringP("partition", "", "", "partition from where machines should be listed") + cmd.Flags().String("rack", "", "rack from where machines should be listed") + cmd.Flags().String("room", "", "room from where machines should be listed") + cmd.Flags().StringSlice("labels", []string{}, "labels to filter machines by, use it like: --labels \"a=b\" or --labels \"a=\".") + cmd.Flags().String("state", "", "state from machines which should be listed, e.g. available|tainted|locked") + cmd.Flags().String("filesystem-layout", "", "filesystem layout from machines which should be listed") + cmd.Flags().String("allocation-type", "", "allocation type from machines which should be listed, e.g. machine|firewall") + cmd.Flags().String("vpn-control-plane-address", "", "vpn control plane address from machines which should be listed") + cmd.Flags().String("vpn-auth-key", "", "vpn auth key from machines which should be listed") + cmd.Flags().Bool("vpn-connected", false, "only list machines which are connected to the vpn") + cmd.Flags().StringSlice("vpn-ips", []string{}, "vpn ips which machines should have") + cmd.Flags().Bool("waiting", false, "only list waiting machines. [admin only]") + cmd.Flags().Bool("preallocated", false, "only list preallocated machines. [admin only]") + cmd.Flags().Bool("not-allocated", false, "only list not allocated machines. [admin only]") + cmd.Flags().String("bmc-address", "", "bmc address from machines which should be listed") + cmd.Flags().String("bmc-mac", "", "bmc mac from machines which should be listed") + cmd.Flags().String("bmc-user", "", "bmc user from machines which should be listed") + cmd.Flags().String("bmc-interface", "", "bmc interface from machines which should be listed") + cmd.Flags().String("chassis-part-number", "", "chassis part number from machines which should be listed") + cmd.Flags().String("chassis-part-serial", "", "chassis part serial from machines which should be listed") + cmd.Flags().String("board-mfg", "", "board manufacturer from machines which should be listed") + cmd.Flags().String("board-serial", "", "board serial from machines which should be listed") + cmd.Flags().String("board-part-number", "", "board part number from machines which should be listed") + cmd.Flags().String("product-manufacturer", "", "product manufacturer from machines which should be listed") + cmd.Flags().String("product-part-number", "", "product part number from machines which should be listed") + cmd.Flags().String("product-serial", "", "product serial from machines which should be listed") + cmd.Flags().Uint64("memory", 0, "memory in bytes from machines which should be listed") + cmd.Flags().Uint32("cpu-cores", 0, "cpu cores from machines which should be listed") + cmd.Flags().StringSlice("network-names", []string{}, "network names to which machines should be connected") + cmd.Flags().StringSlice("network-prefixes", []string{}, "network prefixes to which machines should be connected") + cmd.Flags().StringSlice("network-destination-prefixes", []string{}, "network destination prefixes to which machines should be connected") + cmd.Flags().StringSlice("network-ips", []string{}, "network ips which machines should have") + cmd.Flags().IntSlice("network-vrfs", []int{}, "network vrfs to which machines should be connected") + cmd.Flags().IntSlice("network-asns", []int{}, "network asns to which machines should be connected") + cmd.Flags().StringSlice("nic-macs", []string{}, "nic macs which machines should have") + cmd.Flags().StringSlice("nic-names", []string{}, "nic names which machines should have") + cmd.Flags().StringSlice("nic-neighbor-macs", []string{}, "nic neighbor macs which machines should have") + cmd.Flags().StringSlice("nic-neighbor-names", []string{}, "nic neighbor names which machines should have") + cmd.Flags().StringSlice("disk-names", []string{}, "disk names which machines should have") + cmd.Flags().IntSlice("disk-sizes", []int{}, "disk sizes which machines should have") genericcli.Must(cmd.RegisterFlagCompletionFunc("project", completion.Project)) genericcli.Must(cmd.RegisterFlagCompletionFunc("size", completion.Size)) @@ -252,14 +293,36 @@ func AddMachineQueryFlags(cmd *cobra.Command, completion *completion.Completion) // MachineQuery returns a machine query from the cmd flags added through AddMachineQueryFlags // unscoped indicates an unscoped request made from the admin API, which has certain effects on building the query -func MachineQuery(unscoped bool) *apiv2.MachineQuery { +func MachineQuery(unscoped bool) (*apiv2.MachineQuery, error) { var allocation *apiv2.MachineAllocationQuery - if viper.IsSet("hostname") || viper.IsSet("name") || viper.IsSet("image") { + if viper.IsSet("hostname") || viper.IsSet("name") || viper.IsSet("image") || + viper.IsSet("filesystem-layout") || viper.IsSet("allocation-type") || + viper.IsSet("vpn-control-plane-address") || viper.IsSet("vpn-auth-key") || + viper.IsSet("vpn-connected") || len(viper.GetStringSlice("vpn-ips")) > 0 { allocation = &apiv2.MachineAllocationQuery{ - Hostname: pointer.PointerOrNil(viper.GetString("hostname")), - Name: pointer.PointerOrNil(viper.GetString("name")), - Image: pointer.PointerOrNil(viper.GetString("image")), + Hostname: pointer.PointerOrNil(viper.GetString("hostname")), + Name: pointer.PointerOrNil(viper.GetString("name")), + Image: pointer.PointerOrNil(viper.GetString("image")), + FilesystemLayout: pointer.PointerOrNil(viper.GetString("filesystem-layout")), + } + + if viper.IsSet("allocation-type") { + at, err := enum.GetEnum[apiv2.MachineAllocationType](strings.ToLower(viper.GetString("allocation-type"))) + if err != nil { + return nil, err + } + allocation.AllocationType = at.Enum() + } + + if viper.IsSet("vpn-control-plane-address") || viper.IsSet("vpn-auth-key") || + viper.IsSet("vpn-connected") || len(viper.GetStringSlice("vpn-ips")) > 0 { + allocation.Vpn = &apiv2.MachineVPN{ + ControlPlaneAddress: viper.GetString("vpn-control-plane-address"), + AuthKey: viper.GetString("vpn-auth-key"), + Connected: viper.GetBool("vpn-connected"), + Ips: viper.GetStringSlice("vpn-ips"), + } } } @@ -271,37 +334,108 @@ func MachineQuery(unscoped bool) *apiv2.MachineQuery { allocation.Project = pointer.PointerOrNil(viper.GetString("project")) } - return &apiv2.MachineQuery{ - Uuid: pointer.PointerOrNil(viper.GetString("id")), - Partition: pointer.PointerOrNil(viper.GetString("partition")), - Size: pointer.PointerOrNil(viper.GetString("size")), - Allocation: allocation, - // Rack: pointer.PointerOrNil(viper.GetString("rack")), - // Labels: &apiv2.Labels{ - // Labels: tag.NewTagMap(viper.GetStringSlice("labels")), - // }, - // Bmc: &apiv2.MachineBMCQuery{ - // Address: pointer.PointerOrNil(viper.GetString("bmc-address")), - // Mac: pointer.PointerOrNil(viper.GetString("bmc-mac")), - // User: pointer.PointerOrNil(viper.GetString("bmc-user")), - // Interface: pointer.PointerOrNil(viper.GetString("bmc-interface")), - // }, - // Fru: &apiv2.MachineFRUQuery{ - // ChassisPartNumber: pointer.PointerOrNil(viper.GetString("chassis-part-number")), - // ChassisPartSerial: pointer.PointerOrNil(viper.GetString("chassis-part-serial")), - // BoardMfg: pointer.PointerOrNil(viper.GetString("board-mfg")), - // BoardSerial: pointer.PointerOrNil(viper.GetString("board-serial")), - // BoardPartNumber: pointer.PointerOrNil(viper.GetString("board-part-number")), - // ProductManufacturer: pointer.PointerOrNil(viper.GetString("product-manufacturer")), - // ProductPartNumber: pointer.PointerOrNil(viper.GetString("product-part-number")), - // ProductSerial: pointer.PointerOrNil(viper.GetString("product-serial")), - // }, - // Hardware: &apiv2.MachineHardwareQuery{ - // Memory: pointer.PointerOrNil(viper.GetUint64("memory")), - // CpuCores: pointer.PointerOrNil(viper.GetUint32("cpu-cores")), - // }, - // State: &0, + var labels *apiv2.Labels + if len(viper.GetStringSlice("labels")) > 0 { + labels = &apiv2.Labels{ + Labels: tag.NewTagMap(viper.GetStringSlice("labels")), + } + } + + var bmc *apiv2.MachineBMCQuery + if viper.IsSet("bmc-address") || viper.IsSet("bmc-mac") || viper.IsSet("bmc-user") || viper.IsSet("bmc-interface") { + bmc = &apiv2.MachineBMCQuery{ + Address: pointer.PointerOrNil(viper.GetString("bmc-address")), + Mac: pointer.PointerOrNil(viper.GetString("bmc-mac")), + User: pointer.PointerOrNil(viper.GetString("bmc-user")), + Interface: pointer.PointerOrNil(viper.GetString("bmc-interface")), + } + } + + var fru *apiv2.MachineFRUQuery + if viper.IsSet("chassis-part-number") || viper.IsSet("chassis-part-serial") || + viper.IsSet("board-mfg") || viper.IsSet("board-serial") || viper.IsSet("board-part-number") || + viper.IsSet("product-manufacturer") || viper.IsSet("product-part-number") || viper.IsSet("product-serial") { + fru = &apiv2.MachineFRUQuery{ + ChassisPartNumber: pointer.PointerOrNil(viper.GetString("chassis-part-number")), + ChassisPartSerial: pointer.PointerOrNil(viper.GetString("chassis-part-serial")), + BoardMfg: pointer.PointerOrNil(viper.GetString("board-mfg")), + BoardSerial: pointer.PointerOrNil(viper.GetString("board-serial")), + BoardPartNumber: pointer.PointerOrNil(viper.GetString("board-part-number")), + ProductManufacturer: pointer.PointerOrNil(viper.GetString("product-manufacturer")), + ProductPartNumber: pointer.PointerOrNil(viper.GetString("product-part-number")), + ProductSerial: pointer.PointerOrNil(viper.GetString("product-serial")), + } + } + + var hardware *apiv2.MachineHardwareQuery + if viper.IsSet("memory") || viper.IsSet("cpu-cores") { + hardware = &apiv2.MachineHardwareQuery{ + Memory: pointer.PointerOrNil(viper.GetUint64("memory")), + CpuCores: pointer.PointerOrNil(viper.GetUint32("cpu-cores")), + } + } + + var network *apiv2.MachineNetworkQuery + if len(viper.GetStringSlice("network-names")) > 0 || len(viper.GetStringSlice("network-prefixes")) > 0 || + len(viper.GetStringSlice("network-destination-prefixes")) > 0 || len(viper.GetStringSlice("network-ips")) > 0 || + len(viper.GetIntSlice("network-vrfs")) > 0 || len(viper.GetIntSlice("network-asns")) > 0 { + network = &apiv2.MachineNetworkQuery{ + Networks: viper.GetStringSlice("network-names"), + Prefixes: viper.GetStringSlice("network-prefixes"), + DestinationPrefixes: viper.GetStringSlice("network-destination-prefixes"), + Ips: viper.GetStringSlice("network-ips"), + Vrfs: intSliceToUint64(viper.GetIntSlice("network-vrfs")), + Asns: intSliceToUint32(viper.GetIntSlice("network-asns")), + } } + + var nic *apiv2.MachineNicQuery + if len(viper.GetStringSlice("nic-macs")) > 0 || len(viper.GetStringSlice("nic-names")) > 0 || + len(viper.GetStringSlice("nic-neighbor-macs")) > 0 || len(viper.GetStringSlice("nic-neighbor-names")) > 0 { + nic = &apiv2.MachineNicQuery{ + Macs: viper.GetStringSlice("nic-macs"), + Names: viper.GetStringSlice("nic-names"), + NeighborMacs: viper.GetStringSlice("nic-neighbor-macs"), + NeighborNames: viper.GetStringSlice("nic-neighbor-names"), + } + } + + var disk *apiv2.MachineDiskQuery + if len(viper.GetStringSlice("disk-names")) > 0 || len(viper.GetIntSlice("disk-sizes")) > 0 { + disk = &apiv2.MachineDiskQuery{ + Names: viper.GetStringSlice("disk-names"), + Sizes: intSliceToUint64(viper.GetIntSlice("disk-sizes")), + } + } + + var state *apiv2.MachineState + if viper.IsSet("state") { + s, err := enum.GetEnum[apiv2.MachineState](strings.ToLower(viper.GetString("state"))) + if err != nil { + return nil, err + } + state = s.Enum() + } + + return &apiv2.MachineQuery{ + Uuid: pointer.PointerOrNil(viper.GetString("id")), + Partition: pointer.PointerOrNil(viper.GetString("partition")), + Size: pointer.PointerOrNil(viper.GetString("size")), + Rack: pointer.PointerOrNil(viper.GetString("rack")), + Room: pointer.PointerOrNil(viper.GetString("room")), + Labels: labels, + Allocation: allocation, + Network: network, + Nic: nic, + Disk: disk, + Bmc: bmc, + Fru: fru, + Hardware: hardware, + State: state, + Waiting: pointer.PointerOrNil(unscoped && viper.GetBool("waiting")), + Preallocated: pointer.PointerOrNil(unscoped && viper.GetBool("preallocated")), + NotAllocated: pointer.PointerOrNil(unscoped && viper.GetBool("not-allocated")), + }, nil } var defaultSSHKeys = [...]string{"id_ed25519", "id_ecdsa", "id_rsa", "id_dsa"} @@ -392,6 +526,22 @@ func parseNetworks(possibleNetworks []string) ([]*apiv2.MachineAllocationNetwork return result, nil } +func intSliceToUint64(values []int) []uint64 { + result := make([]uint64, 0, len(values)) + for _, v := range values { + result = append(result, uint64(v)) + } + return result +} + +func intSliceToUint32(values []int) []uint32 { + result := make([]uint32, 0, len(values)) + for _, v := range values { + result = append(result, uint32(v)) + } + return result +} + func AddMachineCreateFlags(cmd *cobra.Command, name string, completion *completion.Completion) { cmd.Flags().String("description", "", "Description of the "+name+" to create. [optional]") cmd.Flags().String("partition", "", "partition/datacenter where the "+name+" is created. [required, except for reserved machines]") diff --git a/tests/e2e/admin/machine_test.go b/tests/e2e/admin/machine_test.go index 7e2afb2..28b694f 100644 --- a/tests/e2e/admin/machine_test.go +++ b/tests/e2e/admin/machine_test.go @@ -31,6 +31,45 @@ func Test_MachineCmd_List(t *testing.T) { "--project", "project", "--size", "size", "--id", "uuid", + "--rack", "rack", + "--labels", "a=b", + "--state", "available", + "--bmc-address", "bmc-address", + "--bmc-mac", "bmc-mac", + "--bmc-user", "bmc-user", + "--bmc-interface", "bmc-interface", + "--chassis-part-number", "chassis-part-number", + "--chassis-part-serial", "chassis-part-serial", + "--board-mfg", "board-mfg", + "--board-serial", "board-serial", + "--board-part-number", "board-part-number", + "--product-manufacturer", "product-manufacturer", + "--product-part-number", "product-part-number", + "--product-serial", "product-serial", + "--memory", "1024", + "--cpu-cores", "4", + "--room", "room", + "--filesystem-layout", "fsl", + "--allocation-type", "firewall", + "--vpn-control-plane-address", "vpn-cp", + "--vpn-auth-key", "vpn-auth", + "--vpn-connected", + "--vpn-ips", "10.0.0.1", + "--waiting", + "--preallocated", + "--not-allocated", + "--network-names", "network-name", + "--network-prefixes", "network-prefix", + "--network-destination-prefixes", "network-dest-prefix", + "--network-ips", "1.2.3.4", + "--network-vrfs", "1", + "--network-asns", "2", + "--nic-macs", "nic-mac", + "--nic-names", "nic-name", + "--nic-neighbor-macs", "nic-neighbor-mac", + "--nic-neighbor-names", "nic-neighbor-name", + "--disk-names", "disk-name", + "--disk-sizes", "1024", }, AssertExhaustiveArgs: true, AssertExhaustiveExcludes: []string{"sort-by"}, @@ -40,14 +79,71 @@ func Test_MachineCmd_List(t *testing.T) { WantRequest: &adminv2.MachineServiceListRequest{ Query: &apiv2.MachineQuery{ Allocation: &apiv2.MachineAllocationQuery{ - Hostname: new("hostname"), - Name: new("name"), - Image: new("image"), - Project: new("project"), + Hostname: new("hostname"), + Name: new("name"), + Image: new("image"), + Project: new("project"), + FilesystemLayout: new("fsl"), + AllocationType: apiv2.MachineAllocationType_MACHINE_ALLOCATION_TYPE_FIREWALL.Enum(), + Vpn: &apiv2.MachineVPN{ + ControlPlaneAddress: "vpn-cp", + AuthKey: "vpn-auth", + Connected: true, + Ips: []string{"10.0.0.1"}, + }, }, Partition: new("partition"), Size: new("size"), Uuid: new("uuid"), + Rack: new("rack"), + Room: new("room"), + Labels: &apiv2.Labels{ + Labels: map[string]string{ + "a": "b", + }, + }, + State: apiv2.MachineState_MACHINE_STATE_AVAILABLE.Enum(), + Waiting: new(true), + Preallocated: new(true), + NotAllocated: new(true), + Network: &apiv2.MachineNetworkQuery{ + Networks: []string{"network-name"}, + Prefixes: []string{"network-prefix"}, + DestinationPrefixes: []string{"network-dest-prefix"}, + Ips: []string{"1.2.3.4"}, + Vrfs: []uint64{1}, + Asns: []uint32{2}, + }, + Nic: &apiv2.MachineNicQuery{ + Macs: []string{"nic-mac"}, + Names: []string{"nic-name"}, + NeighborMacs: []string{"nic-neighbor-mac"}, + NeighborNames: []string{"nic-neighbor-name"}, + }, + Disk: &apiv2.MachineDiskQuery{ + Names: []string{"disk-name"}, + Sizes: []uint64{1024}, + }, + Bmc: &apiv2.MachineBMCQuery{ + Address: new("bmc-address"), + Mac: new("bmc-mac"), + User: new("bmc-user"), + Interface: new("bmc-interface"), + }, + Fru: &apiv2.MachineFRUQuery{ + ChassisPartNumber: new("chassis-part-number"), + ChassisPartSerial: new("chassis-part-serial"), + BoardMfg: new("board-mfg"), + BoardSerial: new("board-serial"), + BoardPartNumber: new("board-part-number"), + ProductManufacturer: new("product-manufacturer"), + ProductPartNumber: new("product-part-number"), + ProductSerial: new("product-serial"), + }, + Hardware: &apiv2.MachineHardwareQuery{ + Memory: new(uint64(1024)), + CpuCores: new(uint32(4)), + }, }, }, WantResponse: func() connect.AnyResponse { @@ -84,6 +180,32 @@ func Test_MachineCmd_List(t *testing.T) { | 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd | 🛡 | Phoned Home | 1m | 1m | machine-2 | f3b4e6a1-2c8d-4e5f-a7b9-1d3e5f7a9b0c | v1-medium-x86 | Ubuntu 24.04 | partition-2 | rack-1 | `), }, + { + Name: "no flags", + CmdArgs: []string{"admin", "machine", "list"}, + NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.MachineServiceListRequest{ + Query: &apiv2.MachineQuery{}, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.MachineServiceListResponse{ + Machines: []*apiv2.Machine{ + testresources.Machine2(), + testresources.Machine1(), + }, + }) + }, + }, + }, + }), + WantTable: new(` + ID LAST EVENT WHEN AGE HOSTNAME PROJECT SIZE IMAGE PARTITION RACK + 5fa2bbe1-407c-4142-92d5-e4419daf9646 Alive 1m v1-medium-x86 partition-1 rack-1 + 673fc473-63ca-4ea4-b9dd-b45cb2127a6fd 🛡 Phoned Home 1m 1m machine-2 f3b4e6a1-2c8d-4e5f-a7b9-1d3e5f7a9b0c v1-medium-x86 Ubuntu 24.04 partition-2 rack-1 + `), + }, } for _, tt := range tests { tt.TestCmd(t) @@ -561,6 +683,45 @@ func Test_MachineCmd_BmcList(t *testing.T) { "--project", "project", "--size", "size", "--id", "uuid", + "--rack", "rack", + "--labels", "a=b", + "--state", "available", + "--bmc-address", "bmc-address", + "--bmc-mac", "bmc-mac", + "--bmc-user", "bmc-user", + "--bmc-interface", "bmc-interface", + "--chassis-part-number", "chassis-part-number", + "--chassis-part-serial", "chassis-part-serial", + "--board-mfg", "board-mfg", + "--board-serial", "board-serial", + "--board-part-number", "board-part-number", + "--product-manufacturer", "product-manufacturer", + "--product-part-number", "product-part-number", + "--product-serial", "product-serial", + "--memory", "1024", + "--cpu-cores", "4", + "--room", "room", + "--filesystem-layout", "fsl", + "--allocation-type", "firewall", + "--vpn-control-plane-address", "vpn-cp", + "--vpn-auth-key", "vpn-auth", + "--vpn-connected", + "--vpn-ips", "10.0.0.1", + "--waiting", + "--preallocated", + "--not-allocated", + "--network-names", "network-name", + "--network-prefixes", "network-prefix", + "--network-destination-prefixes", "network-dest-prefix", + "--network-ips", "1.2.3.4", + "--network-vrfs", "1", + "--network-asns", "2", + "--nic-macs", "nic-mac", + "--nic-names", "nic-name", + "--nic-neighbor-macs", "nic-neighbor-mac", + "--nic-neighbor-names", "nic-neighbor-name", + "--disk-names", "disk-name", + "--disk-sizes", "1024", }, AssertExhaustiveArgs: true, AssertExhaustiveExcludes: []string{"sort-by"}, @@ -570,14 +731,71 @@ func Test_MachineCmd_BmcList(t *testing.T) { WantRequest: &adminv2.MachineServiceListBMCRequest{ Query: &apiv2.MachineQuery{ Allocation: &apiv2.MachineAllocationQuery{ - Hostname: new("hostname"), - Name: new("name"), - Image: new("image"), - Project: new("project"), + Hostname: new("hostname"), + Name: new("name"), + Image: new("image"), + Project: new("project"), + FilesystemLayout: new("fsl"), + AllocationType: apiv2.MachineAllocationType_MACHINE_ALLOCATION_TYPE_FIREWALL.Enum(), + Vpn: &apiv2.MachineVPN{ + ControlPlaneAddress: "vpn-cp", + AuthKey: "vpn-auth", + Connected: true, + Ips: []string{"10.0.0.1"}, + }, }, Partition: new("partition"), Size: new("size"), Uuid: new("uuid"), + Rack: new("rack"), + Room: new("room"), + Labels: &apiv2.Labels{ + Labels: map[string]string{ + "a": "b", + }, + }, + State: apiv2.MachineState_MACHINE_STATE_AVAILABLE.Enum(), + Waiting: new(true), + Preallocated: new(true), + NotAllocated: new(true), + Network: &apiv2.MachineNetworkQuery{ + Networks: []string{"network-name"}, + Prefixes: []string{"network-prefix"}, + DestinationPrefixes: []string{"network-dest-prefix"}, + Ips: []string{"1.2.3.4"}, + Vrfs: []uint64{1}, + Asns: []uint32{2}, + }, + Nic: &apiv2.MachineNicQuery{ + Macs: []string{"nic-mac"}, + Names: []string{"nic-name"}, + NeighborMacs: []string{"nic-neighbor-mac"}, + NeighborNames: []string{"nic-neighbor-name"}, + }, + Disk: &apiv2.MachineDiskQuery{ + Names: []string{"disk-name"}, + Sizes: []uint64{1024}, + }, + Bmc: &apiv2.MachineBMCQuery{ + Address: new("bmc-address"), + Mac: new("bmc-mac"), + User: new("bmc-user"), + Interface: new("bmc-interface"), + }, + Fru: &apiv2.MachineFRUQuery{ + ChassisPartNumber: new("chassis-part-number"), + ChassisPartSerial: new("chassis-part-serial"), + BoardMfg: new("board-mfg"), + BoardSerial: new("board-serial"), + BoardPartNumber: new("board-part-number"), + ProductManufacturer: new("product-manufacturer"), + ProductPartNumber: new("product-part-number"), + ProductSerial: new("product-serial"), + }, + Hardware: &apiv2.MachineHardwareQuery{ + Memory: new(uint64(1024)), + CpuCores: new(uint32(4)), + }, }, }, WantResponse: func() connect.AnyResponse { From 17b6a813d30cad8764af5bf6bcd47cf8dfa9163e Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 11:45:16 +0200 Subject: [PATCH 6/9] Completions. --- cmd/completion/machine.go | 8 ++++++++ cmd/completion/switch.go | 14 ++++++++++++++ pkg/helpers/machine.go | 4 ++++ 3 files changed, 26 insertions(+) diff --git a/cmd/completion/machine.go b/cmd/completion/machine.go index 5cb2735..5f85edb 100644 --- a/cmd/completion/machine.go +++ b/cmd/completion/machine.go @@ -71,3 +71,11 @@ func (c *Completion) Firewall(cmd *cobra.Command, args []string, toComplete stri func (c *Completion) BMCCommands(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return c.genericEnums(apiv2.MachineBMCCommand_name) } + +func (c *Completion) MachineState(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return c.genericEnums(apiv2.MachineState_name) +} + +func (c *Completion) MachineAllocationType(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return c.genericEnums(apiv2.MachineAllocationType_name) +} diff --git a/cmd/completion/switch.go b/cmd/completion/switch.go index a1e33b4..897ebe9 100644 --- a/cmd/completion/switch.go +++ b/cmd/completion/switch.go @@ -48,6 +48,20 @@ func (c *Completion) SwitchRack(cmd *cobra.Command, args []string, toComplete st return racks, cobra.ShellCompDirectiveNoFileComp } +func (c *Completion) SwitchRoom(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + resp, err := c.Client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var rooms []string + for _, s := range resp.Switches { + rooms = append(rooms, pointer.SafeDeref(s.Room)) + } + + return rooms, cobra.ShellCompDirectiveNoFileComp +} + func (c *Completion) SwitchOSVendor(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { resp, err := c.Client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) if err != nil { diff --git a/pkg/helpers/machine.go b/pkg/helpers/machine.go index 0be30c4..4a8fe8e 100644 --- a/pkg/helpers/machine.go +++ b/pkg/helpers/machine.go @@ -289,6 +289,10 @@ func AddMachineQueryFlags(cmd *cobra.Command, completion *completion.Completion) genericcli.Must(cmd.RegisterFlagCompletionFunc("image", completion.Image)) genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", completion.Partition)) genericcli.Must(cmd.RegisterFlagCompletionFunc("id", completion.AdminMachine)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("rack", completion.SwitchRack)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("room", completion.SwitchRoom)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("state", completion.MachineState)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("allocation-type", completion.MachineAllocationType)) } // MachineQuery returns a machine query from the cmd flags added through AddMachineQueryFlags From afa5d4631dd4db5004659ef0101ed93bd654c534 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 11:47:11 +0200 Subject: [PATCH 7/9] Remove helper. --- tests/e2e/testresources/machine.go | 38 +++++++++++++----------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/tests/e2e/testresources/machine.go b/tests/e2e/testresources/machine.go index fb4fb4f..0dd77ea 100644 --- a/tests/e2e/testresources/machine.go +++ b/tests/e2e/testresources/machine.go @@ -203,14 +203,14 @@ var ( Date: "04/01/2024", }, Fru: &apiv2.MachineFRU{ - ChassisPartNumber: newString("Chassis-PN-1"), - ChassisPartSerial: newString("Chassis-SN-1"), - BoardMfg: newString("Supermicro"), - BoardMfgSerial: newString("Board-SN-1"), - BoardPartNumber: newString("Board-PN-1"), - ProductManufacturer: newString("Dell"), - ProductPartNumber: newString("Product-PN-1"), - ProductSerial: newString("Product-SN-1"), + ChassisPartNumber: new("Chassis-PN-1"), + ChassisPartSerial: new("Chassis-SN-1"), + BoardMfg: new("Supermicro"), + BoardMfgSerial: new("Board-SN-1"), + BoardPartNumber: new("Board-PN-1"), + ProductManufacturer: new("Dell"), + ProductPartNumber: new("Product-PN-1"), + ProductSerial: new("Product-SN-1"), }, PowerMetric: &apiv2.MachinePowerMetric{ AverageConsumedWatts: 120.5, @@ -258,14 +258,14 @@ var ( Date: "10/15/2023", }, Fru: &apiv2.MachineFRU{ - ChassisPartNumber: newString("Chassis-PN-2"), - ChassisPartSerial: newString("Chassis-SN-2"), - BoardMfg: newString("Supermicro"), - BoardMfgSerial: newString("Board-SN-2"), - BoardPartNumber: newString("Board-PN-2"), - ProductManufacturer: newString("HPE"), - ProductPartNumber: newString("Product-PN-2"), - ProductSerial: newString("Product-SN-2"), + ChassisPartNumber: new("Chassis-PN-2"), + ChassisPartSerial: new("Chassis-SN-2"), + BoardMfg: new("Supermicro"), + BoardMfgSerial: new("Board-SN-2"), + BoardPartNumber: new("Board-PN-2"), + ProductManufacturer: new("HPE"), + ProductPartNumber: new("Product-PN-2"), + ProductSerial: new("Product-SN-2"), }, PowerMetric: &apiv2.MachinePowerMetric{ AverageConsumedWatts: 0, @@ -287,9 +287,3 @@ var ( }, } ) - -func newString(v string) *string { - s := new(string) - *s = v - return s -} From c81fa829900c250f7d6fab941eb8a25965b3e041 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 11:49:38 +0200 Subject: [PATCH 8/9] Use generics. --- pkg/helpers/machine.go | 54 ++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/pkg/helpers/machine.go b/pkg/helpers/machine.go index 4a8fe8e..7fb1ceb 100644 --- a/pkg/helpers/machine.go +++ b/pkg/helpers/machine.go @@ -388,8 +388,8 @@ func MachineQuery(unscoped bool) (*apiv2.MachineQuery, error) { Prefixes: viper.GetStringSlice("network-prefixes"), DestinationPrefixes: viper.GetStringSlice("network-destination-prefixes"), Ips: viper.GetStringSlice("network-ips"), - Vrfs: intSliceToUint64(viper.GetIntSlice("network-vrfs")), - Asns: intSliceToUint32(viper.GetIntSlice("network-asns")), + Vrfs: intSliceToUint[uint64](viper.GetIntSlice("network-vrfs")), + Asns: intSliceToUint[uint32](viper.GetIntSlice("network-asns")), } } @@ -408,7 +408,7 @@ func MachineQuery(unscoped bool) (*apiv2.MachineQuery, error) { if len(viper.GetStringSlice("disk-names")) > 0 || len(viper.GetIntSlice("disk-sizes")) > 0 { disk = &apiv2.MachineDiskQuery{ Names: viper.GetStringSlice("disk-names"), - Sizes: intSliceToUint64(viper.GetIntSlice("disk-sizes")), + Sizes: intSliceToUint[uint64](viper.GetIntSlice("disk-sizes")), } } @@ -422,23 +422,23 @@ func MachineQuery(unscoped bool) (*apiv2.MachineQuery, error) { } return &apiv2.MachineQuery{ - Uuid: pointer.PointerOrNil(viper.GetString("id")), - Partition: pointer.PointerOrNil(viper.GetString("partition")), - Size: pointer.PointerOrNil(viper.GetString("size")), - Rack: pointer.PointerOrNil(viper.GetString("rack")), - Room: pointer.PointerOrNil(viper.GetString("room")), - Labels: labels, - Allocation: allocation, - Network: network, - Nic: nic, - Disk: disk, - Bmc: bmc, - Fru: fru, - Hardware: hardware, - State: state, - Waiting: pointer.PointerOrNil(unscoped && viper.GetBool("waiting")), - Preallocated: pointer.PointerOrNil(unscoped && viper.GetBool("preallocated")), - NotAllocated: pointer.PointerOrNil(unscoped && viper.GetBool("not-allocated")), + Uuid: pointer.PointerOrNil(viper.GetString("id")), + Partition: pointer.PointerOrNil(viper.GetString("partition")), + Size: pointer.PointerOrNil(viper.GetString("size")), + Rack: pointer.PointerOrNil(viper.GetString("rack")), + Room: pointer.PointerOrNil(viper.GetString("room")), + Labels: labels, + Allocation: allocation, + Network: network, + Nic: nic, + Disk: disk, + Bmc: bmc, + Fru: fru, + Hardware: hardware, + State: state, + Waiting: pointer.PointerOrNil(unscoped && viper.GetBool("waiting")), + Preallocated: pointer.PointerOrNil(unscoped && viper.GetBool("preallocated")), + NotAllocated: pointer.PointerOrNil(unscoped && viper.GetBool("not-allocated")), }, nil } @@ -530,18 +530,10 @@ func parseNetworks(possibleNetworks []string) ([]*apiv2.MachineAllocationNetwork return result, nil } -func intSliceToUint64(values []int) []uint64 { - result := make([]uint64, 0, len(values)) +func intSliceToUint[T ~uint32 | ~uint64](values []int) []T { + result := make([]T, 0, len(values)) for _, v := range values { - result = append(result, uint64(v)) - } - return result -} - -func intSliceToUint32(values []int) []uint32 { - result := make([]uint32, 0, len(values)) - for _, v := range values { - result = append(result, uint32(v)) + result = append(result, T(v)) } return result } From 91a49d032f434eef1014b5ed84a1beda9df2ebec Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 18 Aug 2026 11:52:52 +0200 Subject: [PATCH 9/9] Fix. --- pkg/helpers/machine.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/helpers/machine.go b/pkg/helpers/machine.go index 7fb1ceb..026f6ce 100644 --- a/pkg/helpers/machine.go +++ b/pkg/helpers/machine.go @@ -436,9 +436,9 @@ func MachineQuery(unscoped bool) (*apiv2.MachineQuery, error) { Fru: fru, Hardware: hardware, State: state, - Waiting: pointer.PointerOrNil(unscoped && viper.GetBool("waiting")), - Preallocated: pointer.PointerOrNil(unscoped && viper.GetBool("preallocated")), - NotAllocated: pointer.PointerOrNil(unscoped && viper.GetBool("not-allocated")), + Waiting: pointer.PointerOrNil(viper.GetBool("waiting")), + Preallocated: pointer.PointerOrNil(viper.GetBool("preallocated")), + NotAllocated: pointer.PointerOrNil(viper.GetBool("not-allocated")), }, nil }