mirror of
https://github.com/davidallendj/magellan.git
synced 2025-12-20 03:27:03 -07:00
Added more API documentation
This commit is contained in:
parent
2bc3c74277
commit
f7b08da064
8 changed files with 150 additions and 90 deletions
|
|
@ -95,6 +95,7 @@ func init() {
|
||||||
// set flags to only be used together
|
// set flags to only be used together
|
||||||
collectCmd.MarkFlagsRequiredTogether("username", "password")
|
collectCmd.MarkFlagsRequiredTogether("username", "password")
|
||||||
|
|
||||||
|
// bind flags to config properties
|
||||||
viper.BindPFlag("collect.driver", collectCmd.Flags().Lookup("driver"))
|
viper.BindPFlag("collect.driver", collectCmd.Flags().Lookup("driver"))
|
||||||
viper.BindPFlag("collect.host", collectCmd.Flags().Lookup("host"))
|
viper.BindPFlag("collect.host", collectCmd.Flags().Lookup("host"))
|
||||||
viper.BindPFlag("collect.port", collectCmd.Flags().Lookup("port"))
|
viper.BindPFlag("collect.port", collectCmd.Flags().Lookup("port"))
|
||||||
|
|
|
||||||
10
cmd/list.go
10
cmd/list.go
|
|
@ -12,9 +12,17 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The `list` command provides an easy way to show what was found
|
||||||
|
// 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{
|
var listCmd = &cobra.Command{
|
||||||
Use: "list",
|
Use: "list",
|
||||||
Short: "List information from scan",
|
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 "
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
probeResults, err := sqlite.GetProbeResults(cachePath)
|
probeResults, err := sqlite.GetProbeResults(cachePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ func Execute() {
|
||||||
// not able to be loaded.
|
// not able to be loaded.
|
||||||
func LoadAccessToken() (string, error) {
|
func LoadAccessToken() (string, error) {
|
||||||
// try to load token from env var
|
// try to load token from env var
|
||||||
testToken := os.Getenv("MAGELLAN_ACCESS_TOKEN")
|
testToken := os.Getenv("ACCESS_TOKEN")
|
||||||
if testToken != "" {
|
if testToken != "" {
|
||||||
return testToken, nil
|
return testToken, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,16 @@ var (
|
||||||
status bool
|
status bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The `update` command provides an interface to easily update firmware
|
||||||
|
// using Redfish. It also provides a simple way to check the status of
|
||||||
|
// an update in-progress.
|
||||||
var updateCmd = &cobra.Command{
|
var updateCmd = &cobra.Command{
|
||||||
Use: "update",
|
Use: "update",
|
||||||
Short: "Update BMC node firmware",
|
Short: "Update BMC node firmware",
|
||||||
|
Long: "Perform an firmware update using Redfish by providing a remote firmware URL and component.\n" +
|
||||||
|
"Examples:\n" +
|
||||||
|
" magellan update --host 172.16.0.108 --port 443 --username bmc_username --password bmc_password --firmware-url http://172.16.0.200:8005/firmware/bios/image.RBU --component BIOS\n" +
|
||||||
|
" magellan update --status --host 172.16.0.108 --port 443 --username bmc_username --password bmc_password",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
l := log.NewLogger(logrus.New(), logrus.DebugLevel)
|
l := log.NewLogger(logrus.New(), logrus.DebugLevel)
|
||||||
q := &magellan.UpdateParams{
|
q := &magellan.UpdateParams{
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,19 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoadConfig() will load a YAML config file at the specified path. There are some general
|
||||||
|
// considerations about how this is done with spf13/viper:
|
||||||
|
//
|
||||||
|
// 1. There are intentionally no search paths set, so config path has to be set explicitly
|
||||||
|
// 2. No data will be written to the config file from the tool
|
||||||
|
// 3. Parameters passed as CLI flags and envirnoment variables should always have
|
||||||
|
// precedence over values set in the config.
|
||||||
func LoadConfig(path string) error {
|
func LoadConfig(path string) error {
|
||||||
dir, filename, ext := util.SplitPathForViper(path)
|
dir, filename, ext := util.SplitPathForViper(path)
|
||||||
// fmt.Printf("dir: %s\nfilename: %s\nextension: %s\n", dir, filename, ext)
|
// fmt.Printf("dir: %s\nfilename: %s\nextension: %s\n", dir, filename, ext)
|
||||||
viper.AddConfigPath(dir)
|
viper.AddConfigPath(dir)
|
||||||
viper.SetConfigName(filename)
|
viper.SetConfigName(filename)
|
||||||
viper.SetConfigType(ext)
|
viper.SetConfigType(ext)
|
||||||
// ...no search paths set intentionally, so config has to be set explicitly
|
|
||||||
// ...also, the config file will not save anything
|
|
||||||
// ...and finally, parameters passed to CLI have precedence over config values
|
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,17 @@ import (
|
||||||
"github.com/pkg/browser"
|
"github.com/pkg/browser"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Login() initiates the process to retrieve an access token from an identity provider.
|
||||||
|
// This function is especially designed to work by OPAAL, but will propably be changed
|
||||||
|
// in the future to be more agnostic.
|
||||||
|
//
|
||||||
|
// The 'targetHost' and 'targetPort' parameters should point to the target host/port
|
||||||
|
// to create a temporary server to receive the access token. If an empty 'targetHost'
|
||||||
|
// or an invalid port range is passed, then neither of the parameters will be used
|
||||||
|
// and no server will be started.
|
||||||
|
//
|
||||||
|
// Returns an access token as a string if successful and nil error. Otherwise, returns
|
||||||
|
// an empty string with an error set.
|
||||||
func Login(loginUrl string, targetHost string, targetPort int) (string, error) {
|
func Login(loginUrl string, targetHost string, targetPort int) (string, error) {
|
||||||
var accessToken string
|
var accessToken string
|
||||||
|
|
||||||
|
|
|
||||||
189
internal/scan.go
189
internal/scan.go
|
|
@ -19,94 +19,31 @@ type ScannedResult struct {
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func rawConnect(host string, ports []int, timeout int, keepOpenOnly bool) []ScannedResult {
|
// ScanForAssets() performs a net scan on a network to find available services
|
||||||
results := []ScannedResult{}
|
// running. The function expects a list of hosts and ports to make requests.
|
||||||
for _, p := range ports {
|
// Note that each all ports will be used per host.
|
||||||
result := ScannedResult{
|
//
|
||||||
Host: host,
|
// This function runs in a goroutine with the "concurrency" flag setting the
|
||||||
Port: p,
|
// number of concurrent requests. Only one request is made to each BMC node
|
||||||
Protocol: "tcp",
|
// at a time, but setting a value greater than 1 with enable the requests
|
||||||
State: false,
|
// to be made concurrently.
|
||||||
Timestamp: time.Now(),
|
//
|
||||||
}
|
// If the "disableProbing" flag is set, then the function will skip the extra
|
||||||
t := time.Second * time.Duration(timeout)
|
// HTTP request made to check if the response was from a Redfish service.
|
||||||
port := fmt.Sprint(p)
|
// Otherwise, not receiving a 200 OK response code from the HTTP request will
|
||||||
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), t)
|
// remove the service from being stored in the list of scanned results.
|
||||||
if err != nil {
|
//
|
||||||
result.State = false
|
// Returns a list of scanned results to be stored in cache (but isn't doing here).
|
||||||
// fmt.Println("Connecting error:", err)
|
func ScanForAssets(hosts []string, ports []int, concurrency int, timeout int, disableProbing bool, verbose bool) []ScannedResult {
|
||||||
}
|
|
||||||
if conn != nil {
|
|
||||||
result.State = true
|
|
||||||
defer conn.Close()
|
|
||||||
// fmt.Println("Opened", net.JoinHostPort(host, port))
|
|
||||||
}
|
|
||||||
if keepOpenOnly {
|
|
||||||
if result.State {
|
|
||||||
results = append(results, result)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results = append(results, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenerateHosts(subnet string, subnetMask *net.IP) []string {
|
|
||||||
if subnet == "" || subnetMask == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert subnets from string to net.IP
|
|
||||||
subnetIp := net.ParseIP(subnet)
|
|
||||||
if subnetIp == nil {
|
|
||||||
// try parse CIDR instead
|
|
||||||
ip, network, err := net.ParseCIDR(subnet)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
subnetIp = ip
|
|
||||||
if network != nil {
|
|
||||||
t := net.IP(network.Mask)
|
|
||||||
subnetMask = &t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mask := net.IPMask(subnetMask.To4())
|
|
||||||
|
|
||||||
// if no subnet mask, use a default 24-bit mask (for now)
|
|
||||||
return generateHosts(&subnetIp, &mask)
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateHosts(ip *net.IP, mask *net.IPMask) []string {
|
|
||||||
// get all IP addresses in network
|
|
||||||
ones, _ := mask.Size()
|
|
||||||
hosts := []string{}
|
|
||||||
end := int(math.Pow(2, float64((32-ones)))) - 1
|
|
||||||
for i := 0; i < end; i++ {
|
|
||||||
// ip[3] = byte(i)
|
|
||||||
ip = util.GetNextIP(ip, 1)
|
|
||||||
if ip == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// host := fmt.Sprintf("%v.%v.%v.%v", (*ip)[0], (*ip)[1], (*ip)[2], (*ip)[3])
|
|
||||||
// fmt.Printf("host: %v\n", ip.String())
|
|
||||||
hosts = append(hosts, ip.String())
|
|
||||||
}
|
|
||||||
return hosts
|
|
||||||
}
|
|
||||||
|
|
||||||
func ScanForAssets(hosts []string, ports []int, threads int, timeout int, disableProbing bool, verbose bool) []ScannedResult {
|
|
||||||
var (
|
var (
|
||||||
results = make([]ScannedResult, 0, len(hosts))
|
results = make([]ScannedResult, 0, len(hosts))
|
||||||
done = make(chan struct{}, threads+1)
|
done = make(chan struct{}, concurrency+1)
|
||||||
chanHost = make(chan string, threads+1)
|
chanHost = make(chan string, concurrency+1)
|
||||||
)
|
)
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(threads)
|
wg.Add(concurrency)
|
||||||
for i := 0; i < threads; i++ {
|
for i := 0; i < concurrency; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
host, ok := <-chanHost
|
host, ok := <-chanHost
|
||||||
|
|
@ -161,6 +98,92 @@ func ScanForAssets(hosts []string, ports []int, threads int, timeout int, disabl
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateHosts() builds a list of hosts to scan using the "subnet"
|
||||||
|
// and "subnetMask" arguments passed. The function is capable of
|
||||||
|
// distinguishing between IP formats: a subnet with just an IP address (172.16.0.0) and
|
||||||
|
// a subnet with IP address and CIDR (172.16.0.0/24).
|
||||||
|
//
|
||||||
|
// NOTE: If a IP address is provided with CIDR, then the "subnetMask"
|
||||||
|
// parameter will be ignored. If neither is provided, then the default
|
||||||
|
// subnet mask will be used instead.
|
||||||
|
func GenerateHosts(subnet string, subnetMask *net.IP) []string {
|
||||||
|
if subnet == "" || subnetMask == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert subnets from string to net.IP
|
||||||
|
subnetIp := net.ParseIP(subnet)
|
||||||
|
if subnetIp == nil {
|
||||||
|
// try parse CIDR instead
|
||||||
|
ip, network, err := net.ParseCIDR(subnet)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
subnetIp = ip
|
||||||
|
if network != nil {
|
||||||
|
t := net.IP(network.Mask)
|
||||||
|
subnetMask = &t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mask := net.IPMask(subnetMask.To4())
|
||||||
|
|
||||||
|
// if no subnet mask, use a default 24-bit mask (for now)
|
||||||
|
return generateHosts(&subnetIp, &mask)
|
||||||
|
}
|
||||||
|
|
||||||
func GetDefaultPorts() []int {
|
func GetDefaultPorts() []int {
|
||||||
return []int{HTTPS_PORT}
|
return []int{HTTPS_PORT}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rawConnect(host string, ports []int, timeout int, keepOpenOnly bool) []ScannedResult {
|
||||||
|
results := []ScannedResult{}
|
||||||
|
for _, p := range ports {
|
||||||
|
result := ScannedResult{
|
||||||
|
Host: host,
|
||||||
|
Port: p,
|
||||||
|
Protocol: "tcp",
|
||||||
|
State: false,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
}
|
||||||
|
t := time.Second * time.Duration(timeout)
|
||||||
|
port := fmt.Sprint(p)
|
||||||
|
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), t)
|
||||||
|
if err != nil {
|
||||||
|
result.State = false
|
||||||
|
// fmt.Println("Connecting error:", err)
|
||||||
|
}
|
||||||
|
if conn != nil {
|
||||||
|
result.State = true
|
||||||
|
defer conn.Close()
|
||||||
|
// fmt.Println("Opened", net.JoinHostPort(host, port))
|
||||||
|
}
|
||||||
|
if keepOpenOnly {
|
||||||
|
if result.State {
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateHosts(ip *net.IP, mask *net.IPMask) []string {
|
||||||
|
// get all IP addresses in network
|
||||||
|
ones, _ := mask.Size()
|
||||||
|
hosts := []string{}
|
||||||
|
end := int(math.Pow(2, float64((32-ones)))) - 1
|
||||||
|
for i := 0; i < end; i++ {
|
||||||
|
// ip[3] = byte(i)
|
||||||
|
ip = util.GetNextIP(ip, 1)
|
||||||
|
if ip == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// host := fmt.Sprintf("%v.%v.%v.%v", (*ip)[0], (*ip)[1], (*ip)[2], (*ip)[3])
|
||||||
|
// fmt.Printf("host: %v\n", ip.String())
|
||||||
|
hosts = append(hosts, ip.String())
|
||||||
|
}
|
||||||
|
return hosts
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,14 @@ type UpdateParams struct {
|
||||||
TransferProtocol string
|
TransferProtocol string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Does not work since OpenBMC, whic bmclib uses underneath, does not
|
// UpdateFirmware() uses 'bmc-toolbox/bmclib' to update the firmware of a BMC node.
|
||||||
// support multipart updates. See issue: https://github.com/bmc-toolbox/bmclib/issues/341
|
// The function expects the firmware URL, firmware version, and component flags to be
|
||||||
|
// set from the CLI to perform a firmware update.
|
||||||
|
//
|
||||||
|
// NOTE: Multipart HTTP updating may not work since older verions of OpenBMC, which bmclib
|
||||||
|
// uses underneath, did not support support multipart updates. This was changed with the
|
||||||
|
// inclusion of support for MultipartHttpPushUri in OpenBMC (https://gerrit.openbmc.org/c/openbmc/bmcweb/+/32174).
|
||||||
|
// Also, related to bmclib: https://github.com/bmc-toolbox/bmclib/issues/341
|
||||||
func UpdateFirmware(client *bmclib.Client, l *log.Logger, q *UpdateParams) error {
|
func UpdateFirmware(client *bmclib.Client, l *log.Logger, q *UpdateParams) error {
|
||||||
if q.Component == "" {
|
if q.Component == "" {
|
||||||
return fmt.Errorf("component is required")
|
return fmt.Errorf("component is required")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue