diff --git a/cmd/admin/v2/machine.go b/cmd/admin/v2/machine.go index 8693db7..c8cb0a3 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") + helpers.AddMachineQueryFlags(cmd, c.Completion) - 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") @@ -144,8 +132,14 @@ 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: "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) @@ -156,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) }, @@ -178,19 +167,11 @@ 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)) + helpers.AddMachineQueryFlags(bmcListCmd, c.Completion) + genericcli.AddSortFlag(bmcListCmd, sorters.MachineBmcSorter()) - bmcCmd.AddCommand(bmcGetCmd, bmcListCmd) + bmcCmd.AddCommand(bmcDescribeCmd, bmcListCmd, bmcCommandCmd) lockCmd := &cobra.Command{ Use: "lock", @@ -247,7 +228,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 +279,13 @@ 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")), - } + query, err := helpers.MachineQuery(true) + if err != nil { + return nil, err } 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: query, }) if err != nil { return nil, err @@ -445,19 +390,32 @@ 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{} + sortKeys, err := genericcli.ParseSortFlags() + if err != nil { + return err + } - resp, err := c.c.Client.Adminv2().Machine().ListBMC(ctx, req) + query, err := helpers.MachineQuery(true) if err != nil { return err } - return c.c.ListPrinter.Print(resp.BmcReports) + resp, err := c.c.Client.Adminv2().Machine().ListBMC(ctx, &adminv2.MachineServiceListBMCRequest{ + Query: query, + }) + if err != nil { + return err + } + + if err := sorters.MachineBmcSorter().SortBy(resp.BmcDetails, sortKeys...); err != nil { + return err + } + + return c.c.ListPrinter.Print(resp.BmcDetails) } func (c *machine) consolePassword(ctx context.Context, args []string) error { @@ -515,7 +473,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 diff --git a/cmd/api/v2/machine.go b/cmd/api/v2/machine.go index a9ff019..cc7845e 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,20 +39,9 @@ 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.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") @@ -147,24 +135,14 @@ 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")), - } + 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: &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: query, }) if err != nil { return nil, err 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/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/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..86458ba 100644 --- a/cmd/tableprinters/machine.go +++ b/cmd/tableprinters/machine.go @@ -110,72 +110,77 @@ 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 + // 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)) } 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..0fbabd4 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 get](metalctlv2_admin_machine_bmc_get.md) - get the bmc of a machine +* [metalctlv2 admin machine bmc command](metalctlv2_admin_machine_bmc_command.md) - send a command to 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-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_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 a8524d1..923358a 100644 --- a/docs/admin/metalctlv2_admin_machine_bmc_list.md +++ b/docs/admin/metalctlv2_admin_machine_bmc_list.md @@ -9,12 +9,54 @@ metalctlv2 admin machine bmc list [flags] ### Options ``` - -h, --help help for list - --id string id of machine which should be listed - --image string image - --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 + --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_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/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_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/docs/metalctlv2_machine_list.md b/docs/metalctlv2_machine_list.md index 0dd9063..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 - --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 + --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/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..026f6ce 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" @@ -154,6 +156,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 +197,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 @@ -229,6 +236,212 @@ 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") + 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)) + 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 +// unscoped indicates an unscoped request made from the admin API, which has certain effects on building the query +func MachineQuery(unscoped bool) (*apiv2.MachineQuery, error) { + var allocation *apiv2.MachineAllocationQuery + + 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")), + 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"), + } + } + } + + if unscoped && viper.IsSet("project") { + if allocation == nil { + allocation = &apiv2.MachineAllocationQuery{} + } + + allocation.Project = pointer.PointerOrNil(viper.GetString("project")) + } + + 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: intSliceToUint[uint64](viper.GetIntSlice("network-vrfs")), + Asns: intSliceToUint[uint32](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: intSliceToUint[uint64](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(viper.GetBool("waiting")), + Preallocated: pointer.PointerOrNil(viper.GetBool("preallocated")), + NotAllocated: pointer.PointerOrNil(viper.GetBool("not-allocated")), + }, nil +} + var defaultSSHKeys = [...]string{"id_ed25519", "id_ecdsa", "id_rsa", "id_dsa"} func SearchSSHKey() (string, error) { @@ -317,6 +530,14 @@ func parseNetworks(possibleNetworks []string) ([]*apiv2.MachineAllocationNetwork return result, nil } +func intSliceToUint[T ~uint32 | ~uint64](values []int) []T { + result := make([]T, 0, len(values)) + for _, v := range values { + result = append(result, T(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]") @@ -342,7 +563,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..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) @@ -168,7 +290,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 +330,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 +670,238 @@ func Test_MachineCmd_Apply(t *testing.T) { tt.TestCmd(t) } } + +func Test_MachineCmd_BmcList(t *testing.T) { + tests := []*e2e.Test[adminv2.MachineServiceListBMCResponse, apiv2.MachineBMCDetails]{ + { + Name: "list", + CmdArgs: []string{"admin", "machine", "bmc", "list", + "--name", "name", + "--hostname", "hostname", + "--image", "image", + "--partition", "partition", + "--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"}, + 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"), + 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 { + 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 + 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 + 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(` +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 | + |---------------------------------------|---|--------|--------------|-------------------|-------------------|-------|-------|---------------|-------------|--------|---------| + | 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 | + `), + }, + } + for _, tt := range tests { + 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) + } +} 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..0dd77ea 100644 --- a/tests/e2e/testresources/machine.go +++ b/tests/e2e/testresources/machine.go @@ -179,4 +179,111 @@ 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: 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, + 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: 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, + 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)), + }, + } ) 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,