Add support for scanning PDU components by probing JAWS as well as redfish

Signed-off-by: Ben McDonald <ben.mcdonald@hpe.com>
This commit is contained in:
Ben McDonald 2025-07-03 12:45:02 -07:00
parent 0aa72ce70d
commit 4cfd48da63
No known key found for this signature in database
2 changed files with 46 additions and 21 deletions

View file

@ -66,7 +66,7 @@ var ScanCmd = &cobra.Command{
"specified. The `--scheme` flag works similarly and the default value is 'https' in the host URL or with the\n" + "specified. The `--scheme` flag works similarly and the default value is 'https' in the host URL or with the\n" +
"'--protocol' flag.\n\n" + "'--protocol' flag.\n\n" +
"If the '--disable-probe` flag is used, the tool will not send another request to probe for available.\n" + "If the '--disable-probe` flag is used, the tool will not send another request to probe for available.\n" +
"Redfish services. This is not recommended, since the extra request makes the scan a bit more reliable\n" + "Redfish and JAWS services. This is not recommended, since the extra request makes the scan a bit more reliable\n" +
"for determining which hosts to collect inventory data.\n\n", "for determining which hosts to collect inventory data.\n\n",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
// add default ports for hosts if none are specified with flag // add default ports for hosts if none are specified with flag
@ -138,6 +138,8 @@ var ScanCmd = &cobra.Command{
DisableProbing: disableProbing, DisableProbing: disableProbing,
Verbose: verbose, Verbose: verbose,
Debug: debug, Debug: debug,
Username: username,
Password: password,
}) })
if len(foundAssets) > 0 && debug { if len(foundAssets) > 0 && debug {

View file

@ -21,6 +21,7 @@ type RemoteAsset struct {
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
State bool `json:"state"` State bool `json:"state"`
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
ServiceType string `json:"service_type,omitempty"`
} }
// ScanParams is a collection of commom parameters passed to the CLI // ScanParams is a collection of commom parameters passed to the CLI
@ -33,6 +34,8 @@ type ScanParams struct {
DisableProbing bool DisableProbing bool
Verbose bool Verbose bool
Debug bool Debug bool
Username string
Password string
} }
// ScanForAssets() performs a net scan on a network to find available services // ScanForAssets() performs a net scan on a network to find available services
@ -45,7 +48,7 @@ type ScanParams struct {
// to be made concurrently. // to be made concurrently.
// //
// If the "disableProbing" flag is set, then the function will skip the extra // If the "disableProbing" flag is set, then the function will skip the extra
// HTTP request made to check if the response was from a Redfish service. // HTTP request made to check if the response was from a Redfish or JAWS service.
// Otherwise, not receiving a 200 OK response code from the HTTP request will // Otherwise, not receiving a 200 OK response code from the HTTP request will
// remove the service from being stored in the list of scanned results. // remove the service from being stored in the list of scanned results.
// //
@ -61,6 +64,17 @@ func ScanForAssets(params *ScanParams) []RemoteAsset {
log.Info().Any("args", params).Msg("starting scan...") log.Info().Any("args", params).Msg("starting scan...")
} }
probesToRun := []struct {
Type, Path string
}{
{Type: "Redfish", Path: "/redfish/v1/"},
{Type: "JAWS", Path: "/jaws/monitor/outlets"},
}
probeClient := &http.Client{
Timeout: time.Duration(params.Timeout) * time.Second,
}
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(params.Concurrency) wg.Add(params.Concurrency)
for i := 0; i < params.Concurrency; i++ { for i := 0; i < params.Concurrency; i++ {
@ -78,26 +92,35 @@ func ScanForAssets(params *ScanParams) []RemoteAsset {
if params.Verbose { if params.Verbose {
log.Debug().Err(err).Msgf("failed to connect to host") log.Debug().Err(err).Msgf("failed to connect to host")
} }
wg.Done() // NOTE: This was wg.Done() and return in the original, but that stops the whole worker.
return // Continuing allows the worker to process other hosts in its queue.
continue
} }
if !params.DisableProbing { if !params.DisableProbing {
assetsToAdd := []RemoteAsset{} assetsToAdd := []RemoteAsset{}
for _, foundAsset := range foundAssets { for _, foundAsset := range foundAssets {
url := fmt.Sprintf("%s:%d/redfish/v1/", foundAsset.Host, foundAsset.Port) for _, probe := range probesToRun {
res, _, err := client.MakeRequest(nil, url, http.MethodGet, nil, nil) probeURL := fmt.Sprintf("%s:%d%s", foundAsset.Host, foundAsset.Port, probe.Path)
if err != nil || res == nil { req, err := http.NewRequest("GET", probeURL, nil)
if params.Verbose { if err != nil {
log.Printf("failed to make request: %v\n", err)
}
continue continue
} else if res.StatusCode != http.StatusOK {
if params.Verbose {
log.Printf("request returned code: %v\n", res.StatusCode)
} }
continue
} else { // Add authentication for JAWS endpoints if credentials are provided
if probe.Type == "JAWS" && params.Username != "" && params.Password != "" {
req.SetBasicAuth(params.Username, params.Password)
}
res, err := probeClient.Do(req)
if err == nil && res != nil && res.StatusCode == http.StatusOK {
res.Body.Close()
foundAsset.ServiceType = probe.Type
assetsToAdd = append(assetsToAdd, foundAsset) assetsToAdd = append(assetsToAdd, foundAsset)
break // Found a valid service, no need to probe other types
}
if res != nil {
res.Body.Close()
}
} }
} }
results = append(results, assetsToAdd...) results = append(results, assetsToAdd...)