diff --git a/cmd/pdu-collect.go b/cmd/pdu-collect.go new file mode 100644 index 0000000..c8e0590 --- /dev/null +++ b/cmd/pdu-collect.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "encoding/json" + "fmt" + + "github.com/OpenCHAMI/magellan/pkg/jaws" + "github.com/OpenCHAMI/magellan/pkg/pdu" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" +) + +var pduCollectCmd = &cobra.Command{ + Use: "collect [hosts...]", + Short: "Collect inventory from JAWS-based PDUs", + Long: `Connects to one or more PDUs with a JAWS interface to collect hardware inventory.`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + log.Error().Msg("no PDU hosts provided") + return + } + + if username == "" || password == "" { + log.Error().Msg("--username and --password are required for PDU collection") + return + } + + collection := make([]*pdu.PDUInventory, 0) + for _, host := range args { + log.Info().Msgf("Collecting from PDU: %s", host) + config := jaws.CrawlerConfig{ + URI: host, + Username: username, + Password: password, + } + + inventory, err := jaws.CrawlPDU(config) + if err != nil { + log.Error().Err(err).Msgf("failed to crawl PDU %s", host) + continue + } + collection = append(collection, inventory) + } + + output, err := json.MarshalIndent(collection, "", " ") + if err != nil { + log.Error().Err(err).Msgf("failed to marshal PDU collection to JSON") + } + fmt.Println(string(output)) + }, +} + +func init() { + PduCmd.AddCommand(pduCollectCmd) + + pduCollectCmd.Flags().StringVarP(&username, "username", "u", "", "Set the PDU username") + pduCollectCmd.Flags().StringVarP(&password, "password", "p", "", "Set the PDU password") +} diff --git a/cmd/pdu.go b/cmd/pdu.go new file mode 100644 index 0000000..bfe00f2 --- /dev/null +++ b/cmd/pdu.go @@ -0,0 +1,15 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var PduCmd = &cobra.Command{ + Use: "pdu", + Short: "Perform actions on Power Distribution Units (PDUs)", + Long: `A collection of commands to discover and manage PDUs that may not use the Redfish protocol.`, +} + +func init() { + rootCmd.AddCommand(PduCmd) +} diff --git a/pkg/jaws/pdu-crawler.go b/pkg/jaws/pdu-crawler.go new file mode 100644 index 0000000..a4faba2 --- /dev/null +++ b/pkg/jaws/pdu-crawler.go @@ -0,0 +1,52 @@ +package jaws + +import ( + "fmt" + "net/http" + "time" + + "github.com/OpenCHAMI/magellan/pkg/pdu" +) + +type CrawlerConfig struct { + URI string + Username string + Password string + Insecure bool + Timeout time.Duration +} + +// CrawlPDU connects to a single JAWS PDU and collects its inventory. +func CrawlPDU(config CrawlerConfig) (*pdu.PDUInventory, error) { + client := &http.Client{ + Timeout: config.Timeout, + } + _ = client + + inventory := &pdu.PDUInventory{ + Hostname: config.URI, + } + + // 1. Get System Info + // Should call /jaws/config/info/system + // Create a temporary struct to unmarshal the response + // and then populate PDU inventory, like is done in CSM. + + // 2. Get Outlet Status + // Should call /jaws/outlet/status or similar endpoint + // It will return a list of outlets to parse + + fmt.Printf("Crawling JAWS PDU at %s...\n", config.URI) + + return inventory, nil +} + +/* +func getSystemInfo(client *http.Client, config CrawlerConfig) (*SystemInfo, error) { + // GET to /jaws/config/info/system +} + +func getOutletStatus(client *http.Client, config CrawlerConfig) ([]pdu.PDUOutlet, error) { + // GET to /jaws/outlet/status +} +*/ diff --git a/pkg/pdu/README.md b/pkg/pdu/README.md new file mode 100644 index 0000000..b077941 --- /dev/null +++ b/pkg/pdu/README.md @@ -0,0 +1 @@ +./magellan pdu collect x3000m0 --username admn --password admn diff --git a/pkg/pdu/pdu-inventory.go b/pkg/pdu/pdu-inventory.go new file mode 100644 index 0000000..4e02972 --- /dev/null +++ b/pkg/pdu/pdu-inventory.go @@ -0,0 +1,15 @@ +package pdu + +type PDUOutlet struct { + ID string `json:"id"` // e.g., "35" or "BA35" + Name string `json:"name"` // e.g., "Link1_Outlet_35" + PowerState string `json:"power_state"` // e.g., "ON" or "OFF" +} + +type PDUInventory struct { + Hostname string `json:"hostname"` + Model string `json:"model,omitempty"` + SerialNumber string `json:"serial_number,omitempty"` + FirmwareVersion string `json:"firmware_version,omitempty"` + Outlets []PDUOutlet `json:"outlets"` +}