mirror of
https://github.com/davidallendj/magellan.git
synced 2025-12-20 03:27:03 -07:00
refactor: improvements to CLI and update pkg
This commit is contained in:
parent
b7761c2cbf
commit
92b05a81c7
8 changed files with 86 additions and 87 deletions
|
|
@ -23,17 +23,19 @@ import (
|
|||
// This command should be ran after the `scan` to find available hosts
|
||||
// on a subnet.
|
||||
var CollectCmd = &cobra.Command{
|
||||
Use: "collect",
|
||||
Use: "collect",
|
||||
Example: ` // basic collect after scan without making a follow-up request
|
||||
magellan collect --cache ./assets.db --cacert ochami.pem -o ./logs -t 30
|
||||
|
||||
// set username and password for all nodes and make request to specified host
|
||||
magellan collect --host https://smd.openchami.cluster -u $bmc_username -p $bmc_password
|
||||
|
||||
// run a collect using secrets manager with fallback username and password
|
||||
export MASTER_KEY=$(magellan secrets generatekey)
|
||||
magellan secrets store $node_creds_json -f nodes.json
|
||||
magellan collect --host https://smd.openchami.cluster -u $fallback_bmc_username -p $fallback_bmc_password`,
|
||||
Short: "Collect system information by interrogating BMC node",
|
||||
Long: "Send request(s) to a collection of hosts running Redfish services found stored from the 'scan' in cache.\n" +
|
||||
"See the 'scan' command on how to perform a scan.\n\n" +
|
||||
"Examples:\n" +
|
||||
" magellan collect --cache ./assets.db --output ./logs --timeout 30 --cacert cecert.pem\n" +
|
||||
" magellan collect --host smd.example.com --port 27779 --username $username --password $password\n\n" +
|
||||
// example using `collect`
|
||||
" export MASTER_KEY=$(magellan secrets generatekey)\n" +
|
||||
" magellan secrets store $node_creds_json -f nodes.json" +
|
||||
" magellan collect --host openchami.cluster --username $username --password $password \\\n",
|
||||
Long: "Send request(s) to a collection of hosts running Redfish services found stored from the 'scan' in cache.\nSee the 'scan' command on how to perform a scan.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// get probe states stored in db from scan
|
||||
scannedResults, err := sqlite.GetScannedAssets(cachePath)
|
||||
|
|
@ -114,8 +116,6 @@ func init() {
|
|||
|
||||
// bind flags to config properties
|
||||
checkBindFlagError(viper.BindPFlag("collect.host", CollectCmd.Flags().Lookup("host")))
|
||||
checkBindFlagError(viper.BindPFlag("collect.username", CollectCmd.Flags().Lookup("username")))
|
||||
checkBindFlagError(viper.BindPFlag("collect.password", CollectCmd.Flags().Lookup("password")))
|
||||
checkBindFlagError(viper.BindPFlag("collect.scheme", CollectCmd.Flags().Lookup("scheme")))
|
||||
checkBindFlagError(viper.BindPFlag("collect.protocol", CollectCmd.Flags().Lookup("protocol")))
|
||||
checkBindFlagError(viper.BindPFlag("collect.output", CollectCmd.Flags().Lookup("output")))
|
||||
|
|
|
|||
12
cmd/crawl.go
12
cmd/crawl.go
|
|
@ -17,13 +17,11 @@ import (
|
|||
// specfic inventory detail. This command only expects host names and does
|
||||
// not require a scan to be performed beforehand.
|
||||
var CrawlCmd = &cobra.Command{
|
||||
Use: "crawl [uri]",
|
||||
Use: "crawl [uri]",
|
||||
Example: ` magellan crawl https://bmc.example.com
|
||||
magellan crawl https://bmc.example.com -i -u username -p password`,
|
||||
Short: "Crawl a single BMC for inventory information",
|
||||
Long: "Crawl a single BMC for inventory information with URI. This command does NOT scan subnets nor store scan information\n" +
|
||||
"in cache after completion. To do so, use the 'collect' command instead\n\n" +
|
||||
"Examples:\n" +
|
||||
" magellan crawl https://bmc.example.com\n" +
|
||||
" magellan crawl https://bmc.example.com -i -u username -p password",
|
||||
Long: "Crawl a single BMC for inventory information with URI.\n\n NOTE: This command does not scan subnets, store scan information in cache, nor make a request to a specified host. It is used only to retrieve inventory data directly. Otherwise, use 'scan' and 'collect' instead.",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
// Validate that the only argument is a valid URI
|
||||
var err error
|
||||
|
|
@ -82,8 +80,6 @@ func init() {
|
|||
CrawlCmd.Flags().BoolVarP(&insecure, "insecure", "i", false, "Ignore SSL errors")
|
||||
CrawlCmd.Flags().StringVarP(&secretsFile, "file", "f", "nodes.json", "set the secrets file with BMC credentials")
|
||||
|
||||
checkBindFlagError(viper.BindPFlag("crawl.username", CrawlCmd.Flags().Lookup("username")))
|
||||
checkBindFlagError(viper.BindPFlag("crawl.password", CrawlCmd.Flags().Lookup("password")))
|
||||
checkBindFlagError(viper.BindPFlag("crawl.insecure", CrawlCmd.Flags().Lookup("insecure")))
|
||||
|
||||
rootCmd.AddCommand(CrawlCmd)
|
||||
|
|
|
|||
11
cmd/list.go
11
cmd/list.go
|
|
@ -20,14 +20,15 @@ var (
|
|||
// and stored in a cache database from a scan. The data that's stored
|
||||
// is what is consumed by the `collect` command with the --cache flag.
|
||||
var ListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Use: "list",
|
||||
Example: ` magellan list
|
||||
magellan list --cache ./assets.db
|
||||
magellan list --cache-info
|
||||
`,
|
||||
Args: cobra.ExactArgs(0),
|
||||
Short: "List information stored in cache from a scan",
|
||||
Long: "Prints all of the host and associated data found from performing a scan.\n" +
|
||||
"See the 'scan' command on how to perform a scan.\n\n" +
|
||||
"Examples:\n" +
|
||||
" magellan list\n" +
|
||||
" magellan list --cache ./assets.db",
|
||||
"See the 'scan' command on how to perform a scan.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// check if we just want to show cache-related info and exit
|
||||
if showCache {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ var (
|
|||
var rootCmd = &cobra.Command{
|
||||
Use: "magellan",
|
||||
Short: "Redfish-based BMC discovery tool",
|
||||
Long: "",
|
||||
Long: "Redfish-based BMC discovery tool with dynamic discovery features.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
err := cmd.Help()
|
||||
|
|
@ -75,8 +75,8 @@ func Execute() {
|
|||
func init() {
|
||||
currentUser, _ = user.Current()
|
||||
cobra.OnInitialize(InitializeConfig)
|
||||
rootCmd.PersistentFlags().IntVar(&concurrency, "concurrency", -1, "Set the number of concurrent processes")
|
||||
rootCmd.PersistentFlags().IntVar(&timeout, "timeout", 5, "Set the timeout for requests")
|
||||
rootCmd.PersistentFlags().IntVarP(&concurrency, "concurrency", "j", -1, "Set the number of concurrent processes")
|
||||
rootCmd.PersistentFlags().IntVarP(&timeout, "timeout", "t", 5, "Set the timeout for requests")
|
||||
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "Set the config file path")
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Set to enable/disable verbose output")
|
||||
rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "Set to enable/disable debug messages")
|
||||
|
|
@ -94,7 +94,7 @@ func init() {
|
|||
|
||||
func checkBindFlagError(err error) {
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to bind flag")
|
||||
log.Error().Err(err).Msg("failed to bind cobra/viper flag")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
40
cmd/scan.go
40
cmd/scan.go
|
|
@ -33,7 +33,28 @@ var (
|
|||
// See the `ScanForAssets()` function in 'internal/scan.go' for details
|
||||
// related to the implementation.
|
||||
var ScanCmd = &cobra.Command{
|
||||
Use: "scan urls...",
|
||||
Use: "scan urls...",
|
||||
Example: `
|
||||
// assumes host https://10.0.0.101:443
|
||||
magellan scan 10.0.0.101
|
||||
|
||||
// assumes subnet using HTTPS and port 443 except for specified host
|
||||
magellan scan http://10.0.0.101:80 https://user:password@10.0.0.102:443 http://172.16.0.105:8080 --subnet 172.16.0.0/24
|
||||
|
||||
// assumes hosts http://10.0.0.101:8080 and http://10.0.0.102:8080
|
||||
magellan scan 10.0.0.101 10.0.0.102 https://172.16.0.10:443 --port 8080 --protocol tcp
|
||||
|
||||
// assumes subnet using default unspecified subnet-masks
|
||||
magellan scan --subnet 10.0.0.0
|
||||
|
||||
// assumes subnet using HTTPS and port 443 with specified CIDR
|
||||
magellan scan --subnet 10.0.0.0/16
|
||||
|
||||
// assumes subnet using HTTP and port 5000 similar to 192.168.0.0/16
|
||||
magellan scan --subnet 192.168.0.0 --protocol tcp --scheme https --port 5000 --subnet-mask 255.255.0.0
|
||||
|
||||
// assumes subnet without CIDR has a subnet-mask of 255.255.0.0
|
||||
magellan scan --subnet 10.0.0.0/24 --subnet 172.16.0.0 --subnet-mask 255.255.0.0 --cache ./assets.db`,
|
||||
Short: "Scan to discover BMC nodes on a network",
|
||||
Long: "Perform a net scan by attempting to connect to each host and port specified and getting a response.\n" +
|
||||
"Each host is passed *with a full URL* including the protocol and port. Additional subnets can be added\n" +
|
||||
|
|
@ -46,22 +67,7 @@ var ScanCmd = &cobra.Command{
|
|||
"'--protocol' flag.\n\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" +
|
||||
"for determining which hosts to collect inventory data.\n\n" +
|
||||
"Examples:\n" +
|
||||
// assumes host https://10.0.0.101:443
|
||||
" magellan scan 10.0.0.101\n" +
|
||||
// assumes subnet using HTTPS and port 443 except for specified host
|
||||
" magellan scan http://10.0.0.101:80 https://user:password@10.0.0.102:443 http://172.16.0.105:8080 --subnet 172.16.0.0/24\n" +
|
||||
// assumes hosts http://10.0.0.101:8080 and http://10.0.0.102:8080
|
||||
" magellan scan 10.0.0.101 10.0.0.102 https://172.16.0.10:443 --port 8080 --protocol tcp\n" +
|
||||
// assumes subnet using default unspecified subnet-masks
|
||||
" magellan scan --subnet 10.0.0.0\n" +
|
||||
// assumes subnet using HTTPS and port 443 with specified CIDR
|
||||
" magellan scan --subnet 10.0.0.0/16\n" +
|
||||
// assumes subnet using HTTP and port 5000 similar to 192.168.0.0/16
|
||||
" magellan scan --subnet 192.168.0.0 --protocol tcp --scheme https --port 5000 --subnet-mask 255.255.0.0\n" +
|
||||
// assumes subnet without CIDR has a subnet-mask of 255.255.0.0
|
||||
" magellan scan --subnet 10.0.0.0/24 --subnet 172.16.0.0 --subnet-mask 255.255.0.0 --cache ./assets.db\n",
|
||||
"for determining which hosts to collect inventory data.\n\n",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// add default ports for hosts if none are specified with flag
|
||||
if len(ports) == 0 {
|
||||
|
|
|
|||
|
|
@ -20,17 +20,21 @@ var (
|
|||
)
|
||||
|
||||
var secretsCmd = &cobra.Command{
|
||||
Use: "secrets",
|
||||
Use: "secrets",
|
||||
Example: `
|
||||
// generate new key and set environment variable
|
||||
export MASTER_KEY=$(magellan secrets generatekey)
|
||||
|
||||
// store specific BMC node creds for collect and crawl in default secrets store (--file/-f flag not set)
|
||||
magellan secrets store $bmc_host $bmc_creds
|
||||
|
||||
// retrieve creds from secrets store
|
||||
magellan secrets retrieve $bmc_host -f nodes.json
|
||||
|
||||
// list creds from specific secrets
|
||||
magellan secrets list -f nodes.json`,
|
||||
Short: "Manage credentials for BMC nodes",
|
||||
Long: "Manage credentials for BMC nodes to for querying information through redfish. This requires generating a key and setting the 'MASTER_KEY' environment variable for the secrets store.\n" +
|
||||
"Examples:\n\n" +
|
||||
" export MASTER_KEY=$(magellan secrets generatekey)\n" +
|
||||
// store specific BMC node creds for `collect` and `crawl` in default secrets store (`--file/-f`` flag not set)
|
||||
" magellan secrets store $bmc_host $bmc_creds" +
|
||||
// retrieve creds from secrets store
|
||||
" magellan secrets retrieve $bmc_host -f nodes.json" +
|
||||
// list creds from specific secrets
|
||||
" magellan secrets list -f nodes.json",
|
||||
Long: "Manage credentials for BMC nodes to for querying information through redfish. This requires generating a key and setting the 'MASTER_KEY' environment variable for the secrets store.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// show command help and exit
|
||||
if len(args) < 1 {
|
||||
|
|
@ -60,7 +64,7 @@ var secretsStoreCmd = &cobra.Command{
|
|||
Short: "Stores the given string value under secretID.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
secretID = args[0]
|
||||
secretID string = args[0]
|
||||
secretValue string
|
||||
store secrets.SecretStore
|
||||
inputFileBytes []byte
|
||||
|
|
@ -163,7 +167,7 @@ var secretsStoreCmd = &cobra.Command{
|
|||
|
||||
func isValidCredsJSON(val string) bool {
|
||||
var (
|
||||
valid = !json.Valid([]byte(val))
|
||||
valid bool = !json.Valid([]byte(val))
|
||||
creds map[string]string
|
||||
err error
|
||||
)
|
||||
|
|
@ -219,8 +223,8 @@ var secretsListCmd = &cobra.Command{
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
for key := range secrets {
|
||||
fmt.Printf("%s\n", key)
|
||||
for key, value := range secrets {
|
||||
fmt.Printf("%s: %s\n", key, value)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
var (
|
||||
host string
|
||||
firmwareUrl string
|
||||
firmwareUri string
|
||||
firmwareVersion string
|
||||
component string
|
||||
transferProtocol string
|
||||
|
|
@ -24,12 +24,16 @@ var (
|
|||
// using Redfish. It also provides a simple way to check the status of
|
||||
// an update in-progress.
|
||||
var updateCmd = &cobra.Command{
|
||||
Use: "update hosts...",
|
||||
Use: "update hosts...",
|
||||
Example: ` // perform an firmware update
|
||||
magellan update 172.16.0.108:443 -i -u $bmc_username -p $bmc_password \
|
||||
--firmware-url http://172.16.0.200:8005/firmware/bios/image.RBU \
|
||||
--component BIOS
|
||||
|
||||
// check update status
|
||||
magellan update 172.16.0.108:443 -i -u $bmc_username -p $bmc_password --status`,
|
||||
Short: "Update BMC node firmware",
|
||||
Long: "Perform an firmware update using Redfish by providing a remote firmware URL and component.\n\n" +
|
||||
"Examples:\n" +
|
||||
" magellan update 172.16.0.108:443 --insecure --username bmc_username --password bmc_password --firmware-url http://172.16.0.200:8005/firmware/bios/image.RBU --component BIOS\n" +
|
||||
" magellan update 172.16.0.108:443 --insecure --status --username bmc_username --password bmc_password",
|
||||
Long: "Perform an firmware update using Redfish by providing a remote firmware URL and component.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// check that we have at least one host
|
||||
if len(args) <= 0 {
|
||||
|
|
@ -41,9 +45,7 @@ var updateCmd = &cobra.Command{
|
|||
for _, arg := range args {
|
||||
if showStatus {
|
||||
err := magellan.GetUpdateStatus(&magellan.UpdateParams{
|
||||
FirmwarePath: firmwareUrl,
|
||||
FirmwareVersion: firmwareVersion,
|
||||
Component: component,
|
||||
FirmwareURI: firmwareUri,
|
||||
TransferProtocol: transferProtocol,
|
||||
Insecure: Insecure,
|
||||
CollectParams: magellan.CollectParams{
|
||||
|
|
@ -61,9 +63,7 @@ var updateCmd = &cobra.Command{
|
|||
|
||||
// initiate a remote update
|
||||
err := magellan.UpdateFirmwareRemote(&magellan.UpdateParams{
|
||||
FirmwarePath: firmwareUrl,
|
||||
FirmwareVersion: firmwareVersion,
|
||||
Component: component,
|
||||
FirmwareURI: firmwareUri,
|
||||
TransferProtocol: strings.ToUpper(transferProtocol),
|
||||
Insecure: Insecure,
|
||||
CollectParams: magellan.CollectParams{
|
||||
|
|
@ -81,21 +81,15 @@ var updateCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
updateCmd.Flags().StringVar(&username, "username", "", "Set the BMC user")
|
||||
updateCmd.Flags().StringVar(&password, "password", "", "Set the BMC password")
|
||||
updateCmd.Flags().StringVarP(&username, "username", "u", "", "Set the BMC user")
|
||||
updateCmd.Flags().StringVarP(&password, "password", "p", "", "Set the BMC password")
|
||||
updateCmd.Flags().StringVar(&transferProtocol, "scheme", "https", "Set the transfer protocol")
|
||||
updateCmd.Flags().StringVar(&firmwareUrl, "firmware-url", "", "Set the path to the firmware")
|
||||
updateCmd.Flags().StringVar(&firmwareVersion, "firmware-version", "", "Set the version of firmware to be installed")
|
||||
updateCmd.Flags().StringVar(&component, "component", "", "Set the component to upgrade (BMC|BIOS)")
|
||||
updateCmd.Flags().StringVar(&firmwareUri, "firmware-uri", "", "Set the URI to retrieve the firmware")
|
||||
updateCmd.Flags().BoolVar(&showStatus, "status", false, "Get the status of the update")
|
||||
updateCmd.Flags().BoolVar(&Insecure, "insecure", false, "Allow insecure connections to the server")
|
||||
updateCmd.Flags().BoolVarP(&Insecure, "insecure", "i", false, "Allow insecure connections to the server")
|
||||
|
||||
checkBindFlagError(viper.BindPFlag("update.username", updateCmd.Flags().Lookup("username")))
|
||||
checkBindFlagError(viper.BindPFlag("update.password", updateCmd.Flags().Lookup("password")))
|
||||
checkBindFlagError(viper.BindPFlag("update.scheme", updateCmd.Flags().Lookup("scheme")))
|
||||
checkBindFlagError(viper.BindPFlag("update.firmware-url", updateCmd.Flags().Lookup("firmware-url")))
|
||||
checkBindFlagError(viper.BindPFlag("update.firmware-version", updateCmd.Flags().Lookup("firmware-version")))
|
||||
checkBindFlagError(viper.BindPFlag("update.component", updateCmd.Flags().Lookup("component")))
|
||||
checkBindFlagError(viper.BindPFlag("update.firmware-uri", updateCmd.Flags().Lookup("firmware-uri")))
|
||||
checkBindFlagError(viper.BindPFlag("update.status", updateCmd.Flags().Lookup("status")))
|
||||
checkBindFlagError(viper.BindPFlag("update.insecure", updateCmd.Flags().Lookup("insecure")))
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ import (
|
|||
|
||||
type UpdateParams struct {
|
||||
CollectParams
|
||||
FirmwarePath string
|
||||
FirmwareVersion string
|
||||
Component string
|
||||
FirmwareURI string
|
||||
TransferProtocol string
|
||||
Insecure bool
|
||||
}
|
||||
|
|
@ -51,7 +49,7 @@ func UpdateFirmwareRemote(q *UpdateParams) error {
|
|||
|
||||
// Build the update request payload
|
||||
req := redfish.SimpleUpdateParameters{
|
||||
ImageURI: q.FirmwarePath,
|
||||
ImageURI: q.FirmwareURI,
|
||||
TransferProtocol: redfish.TransferProtocolType(q.TransferProtocol),
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue