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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions cmd/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,15 @@ In case the machine did not register properly a direct ipmi console access is av
},
ValidArgsFunction: c.comp.MachineListCompletion,
}
machineIpmiChassisCmd := &cobra.Command{
Use: "chassis-list",
Short: `display ipmi machines grouped by chassis serial`,
Long: `display ipmi machines grouped by chassis serial.` + "\n" + api.EmojiHelpText(),
RunE: func(cmd *cobra.Command, args []string) error {
return w.machineIpmiChassis(args)
},
ValidArgsFunction: c.comp.MachineListCompletion,
}
machineIssuesCmd := &cobra.Command{
Use: "issues [<machine ID>]",
Short: `display machines which are in a potential bad state`,
Expand Down Expand Up @@ -395,6 +404,10 @@ In case the machine did not register properly a direct ipmi console access is av
w.listCmdFlags(machineIpmiCmd, 1*time.Hour)
genericcli.AddSortFlag(machineIpmiCmd, sorters.MachineIPMISorter())

w.listCmdFlags(machineIpmiChassisCmd, 1*time.Hour)
genericcli.AddSortFlag(machineIpmiChassisCmd, sorters.MachineIPMISorter())
machineIpmiCmd.AddCommand(machineIpmiChassisCmd)

w.listCmdFlags(machineIssuesCmd, 0)
genericcli.AddSortFlag(machineIssuesCmd, sorters.MachineIPMISorter())

Expand Down Expand Up @@ -1327,6 +1340,65 @@ func (c *machineCmd) machineIpmi(args []string) error {
return c.listPrinter.Print(resp.Payload)
}

func (c *machineCmd) machineIpmiChassis(args []string) error {
var machines []*models.V1MachineIPMIResponse

if len(args) > 0 {
id, err := genericcli.GetExactlyOneArg(args)
if err != nil {
return err
}

resp, err := c.client.Machine().FindIPMIMachine(machine.NewFindIPMIMachineParams().WithID(id), nil)
if err != nil {
return err
}

machines = pointer.WrapInSlice(resp.Payload)
} else {
resp, err := c.client.Machine().FindIPMIMachines(machine.NewFindIPMIMachinesParams().WithBody(machineFindRequestFromCLI()), nil)
if err != nil {
return err
}

machines = append(machines, resp.Payload...)
}

sortKeys, err := genericcli.ParseSortFlags()
if err != nil {
return err
}

err = sorters.MachineIPMISorter().SortBy(machines, sortKeys...)
if err != nil {
return err
}

byChassis := tableprinters.MachineIpmiChassisTable{}
for _, m := range machines {
var (
serial = pointer.SafeDeref(pointer.SafeDeref(m.Ipmi).Fru).ChassisPartSerial
number = pointer.SafeDeref(pointer.SafeDeref(m.Ipmi).Fru).ChassisPartNumber

idx = slices.IndexFunc(byChassis, func(c *tableprinters.MachineIpmiChassis) bool {
return serial == c.ChassisPartSerial
})
)

if idx < 0 {
byChassis = append(byChassis, &tableprinters.MachineIpmiChassis{
ChassisPartNumber: number,
ChassisPartSerial: serial,
Machines: []*models.V1MachineIPMIResponse{m},
})
} else {
byChassis[idx].Machines = append(byChassis[idx].Machines, m)
}
}

return c.listPrinter.Print(byChassis)
}

func (c *machineCmd) machineIssuesList() error {
issuesResp, err := c.client.Machine().ListIssues(machine.NewListIssuesParams(), nil)
if err != nil {
Expand Down
69 changes: 69 additions & 0 deletions cmd/tableprinters/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,72 @@ func (t *TablePrinter) MachineIssuesTable(data *MachinesAndIssues, wide bool) ([

return header, rows, nil
}

type MachineIpmiChassisTable []*MachineIpmiChassis

type MachineIpmiChassis struct {
ChassisPartNumber string `json:"chassis_part_number,omitempty" yaml:"chassis_part_number,omitempty"`
ChassisPartSerial string `json:"chassis_part_serial,omitempty" yaml:"chassis_part_serial,omitempty"`
Machines []*models.V1MachineIPMIResponse `json:"machines" yaml:"machines"`
}

func (t *TablePrinter) MachineIpmiChassisTable(data MachineIpmiChassisTable, wide bool) ([]string, [][]string, error) {
var (
rows [][]string
)

header := []string{"ID", "", "Partition", "Rack", "Size", "Product Serial", "Hostname"}
// if wide {
// no particular wide view yet
// }

for _, chassis := range data {
chassisID := chassis.ChassisPartSerial
if chassisID == "" {
chassisID = "<no-chassis-serial-reported>"
}
if chassis.ChassisPartNumber != "" {
chassisID = fmt.Sprintf("%s (%s)", chassisID, chassis.ChassisPartNumber)
}

rows = append(rows, []string{chassisID})

for i, m := range chassis.Machines {
var (
id = pointer.SafeDeref(m.ID)
partition = pointer.SafeDeref(pointer.SafeDeref(m.Partition).ID)
size = pointer.SafeDeref(pointer.SafeDeref(m.Size).ID)
ps = ""
ipmi = m.Ipmi
rack = m.Rackid
hostname = pointer.SafeDeref(pointer.SafeDeref(m.Allocation).Hostname)
)

if ipmi != nil {
fru := ipmi.Fru

if fru != nil {
ps = fru.ProductSerial
}
}

emojis, wideEmojis := t.getMachineStatusEmojis(m.Liveliness, m.Events, m.State, nil)

prefix := "├"
if i == len(chassis.Machines)-1 {
prefix = "└"
}
prefix += "─╴"

id = fmt.Sprintf("%s %s", prefix, id)

if wide {
rows = append(rows, []string{id, wideEmojis, partition, rack, size, ps, hostname})
} else {
rows = append(rows, []string{id, emojis, partition, rack, size, ps, hostname})
}
}
}

return header, rows, nil
}
2 changes: 2 additions & 0 deletions cmd/tableprinters/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin
return t.MachineIPMITable(d, wide)
case *models.V1MachineIPMIResponse:
return t.MachineIPMITable(pointer.WrapInSlice(d), wide)
case MachineIpmiChassisTable:
return t.MachineIpmiChassisTable(d, wide)
case []*models.V1MachineProvisioningEvent:
return t.MachineLogsTable(d, wide)
case *models.V1MachineProvisioningEvent:
Expand Down
1 change: 1 addition & 0 deletions docs/metalctl_machine_ipmi.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,6 @@ metalctl machine ipmi [<machine ID>] [flags]
### SEE ALSO

* [metalctl machine](metalctl_machine.md) - manage machine entities
* [metalctl machine ipmi chassis-list](metalctl_machine_ipmi_chassis-list.md) - display ipmi machines grouped by chassis serial
* [metalctl machine ipmi events](metalctl_machine_ipmi_events.md) - display machine hardware events

85 changes: 85 additions & 0 deletions docs/metalctl_machine_ipmi_chassis-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
## metalctl machine ipmi chassis-list

display ipmi machines grouped by chassis serial

### Synopsis

display ipmi machines grouped by chassis serial

Meaning of the emojis:

🚧 Machine is reserved. Reserved machines are not considered for random allocation until the reservation flag is removed.
🔒 Machine is locked. Locked machines can not be deleted until the lock is removed.
💀 Machine is dead. The metal-api does not receive any events from this machine.
❗ Machine has a last event error. The machine has recently encountered an error during the provisioning lifecycle.
❓ Machine is in unknown condition. The metal-api does not receive phoned home events anymore or has never booted successfully.
⭕ Machine is in a provisioning crash loop. Flag can be reset through an API-triggered reboot or when the machine reaches the phoned home state.
🚑 Machine reclaim has failed. The machine was deleted but it is not going back into the available machine pool.
🛡 Machine is connected to our VPN, ssh access only possible via this VPN.


```
metalctl machine ipmi chassis-list [flags]
```

### Options

```
--bmc-address string bmc ipmi address (needs to include port) to filter [optional]
--bmc-mac string bmc mac address to filter [optional]
--board-part-number string fru board part number to filter [optional]
-h, --help help for chassis-list
--hostname string allocation hostname to filter [optional]
--id string ID to filter [optional]
--image string allocation image to filter [optional]
--last-event-error-threshold duration the duration up to how long in the past a machine last event error will be counted as an issue [optional] (default 1h0m0s)
--mac string mac to filter [optional]
--manufacturer string fru manufacturer to filter [optional]
--name string allocation name to filter [optional]
--network-destination-prefixes string network destination prefixes to filter [optional]
--network-ids string network ids to filter [optional]
--network-ips string network ips to filter [optional]
--partition string partition to filter [optional]
--product-part-number string fru product part number to filter [optional]
--product-serial string fru product serial to filter [optional]
--project string allocation project to filter [optional]
--rack string rack to filter [optional]
--role string allocation role to filter [optional]
--size string size to filter [optional]
--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|bios|bmc|event|id|liveliness|partition|project|rack|size|when
--state string state to filter [optional]
--tags strings tags to filter, use it like: --tags "tag1,tag2" or --tags "tag3".
```

### Options inherited from parent commands

```
--api-token string api token to authenticate. Can be specified with METALCTL_API_TOKEN environment variable.
--api-url string api server address. Can be specified with METALCTL_API_URL environment variable.
-c, --config string alternative config file path, (default is ~/.metalctl/config.yaml).
Example config.yaml:

---
apitoken: "alongtoken"
...


--debug debug output
--force-color force colored output even without tty
--kubeconfig string Path to the kube-config to use for authentication and authorization. Is updated by login. Uses default path if not specified.
--no-headers do not print headers of table output format (default print headers)
-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.
Example for machines:

metalctl machine list -o template --template "{{ .id }}:{{ .size.id }}"


--yes-i-really-mean-it skips security prompts (which can be dangerous to set blindly because actions can lead to data loss or additional costs)
```

### SEE ALSO

* [metalctl machine ipmi](metalctl_machine_ipmi.md) - display ipmi details of the machine, if no machine ID is given all ipmi addresses are returned.