mirror of
https://github.com/davidallendj/magellan.git
synced 2025-12-20 03:27:03 -07:00
Merge branch 'main' into config-file
This commit is contained in:
commit
2b421e8af5
24 changed files with 618 additions and 298 deletions
4
.devcontainer/.env
Normal file
4
.devcontainer/.env
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_DB=postgres
|
||||
POSTGRES_HOSTNAME=localhost
|
||||
13
.devcontainer/Dockerfile
Normal file
13
.devcontainer/Dockerfile
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
FROM mcr.microsoft.com/devcontainers/go:1-1.20-bullseye
|
||||
|
||||
# [Optional] Uncomment this section to install additional OS packages.
|
||||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||
|
||||
# [Optional] Uncomment the next lines to use go get to install anything else you need
|
||||
# USER vscode
|
||||
# RUN go get -x <your-dependency-or-tool>
|
||||
# USER root
|
||||
|
||||
# [Optional] Uncomment this line to install global node packages.
|
||||
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
|
||||
28
.devcontainer/devcontainer.json
Normal file
28
.devcontainer/devcontainer.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/go-postgres
|
||||
{
|
||||
"name": "Go & PostgreSQL",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||
"features": {
|
||||
"ghcr.io/marcozac/devcontainer-features/goreleaser:1": {},
|
||||
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
|
||||
|
||||
}
|
||||
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
// "features": {},
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [5432],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "go version",
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
38
.devcontainer/docker-compose.yml
Normal file
38
.devcontainer/docker-compose.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
version: '3.8'
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
env_file:
|
||||
# Ensure that the variables in .env match the same variables in devcontainer.json
|
||||
- .env
|
||||
|
||||
volumes:
|
||||
- ../..:/workspaces:cached
|
||||
|
||||
# Overrides default command so things don't shut down after the process ends.
|
||||
command: sleep infinity
|
||||
|
||||
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
|
||||
network_mode: service:db
|
||||
|
||||
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
|
||||
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
||||
|
||||
db:
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
env_file:
|
||||
# Ensure that the variables in .env match the same variables in devcontainer.json
|
||||
- .env
|
||||
|
||||
|
||||
# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
|
||||
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
||||
|
|
@ -3,25 +3,29 @@ before:
|
|||
- go mod download
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
- CGO_ENABLED=1
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
dockers:
|
||||
- image_templates:
|
||||
- bikeshack/{{.ProjectName}}:latest
|
||||
- bikeshack/{{.ProjectName}}:{{ .Tag }}
|
||||
- bikeshack/{{.ProjectName}}:{{ .Major }}
|
||||
- bikeshack/{{.ProjectName}}:{{ .Major }}.{{ .Minor }}
|
||||
- ghcr.io/openchami/{{.ProjectName}}:latest
|
||||
- ghcr.io/openchami/{{.ProjectName}}:{{ .Tag }}
|
||||
- ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}
|
||||
- ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}.{{ .Minor }}
|
||||
build_flag_templates:
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CHANGELOG.md
|
||||
- README.md
|
||||
- bin/magellan.sh
|
||||
archives:
|
||||
- format: tar.gz
|
||||
rlcp: true
|
||||
|
|
@ -34,10 +38,10 @@ archives:
|
|||
{{- else }}{{ .Arch }}{{ end }}
|
||||
{{- if .Arm }}v{{ .Arm }}{{ end }}
|
||||
files:
|
||||
- migrations/*
|
||||
- LICENSE
|
||||
- CHANGELOG.md
|
||||
- README.md
|
||||
- bin/magellan.sh
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
|
|
|
|||
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
|
||||
## [0.0.5] - 2023-11-02
|
||||
|
||||
### Added
|
||||
|
||||
* Ability to update firmware
|
||||
* Refactored connection handling for faster scanning
|
||||
* Updated to refelct home at github.com/OpenCHAMI
|
||||
* Updated to reflect ghcr.io as container home
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.0.1] - 2023-09-14
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
FROM cgr.dev/chainguard/wolfi-base
|
||||
|
||||
RUN apk add --no-cache tini
|
||||
RUN apk add --no-cache tini bash
|
||||
|
||||
# nobody 65534:65534
|
||||
USER 65534:65534
|
||||
|
||||
|
||||
COPY magellan /
|
||||
COPY magellan /magellan
|
||||
COPY /bin/magellan.sh /magellan.sh
|
||||
|
||||
CMD [ "/magellan" ]
|
||||
|
||||
CMD [ "/magellan.sh" ]
|
||||
|
||||
ENTRYPOINT [ "/sbin/tini", "--" ]
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -41,6 +41,9 @@ go mod tidy && go build
|
|||
This should find and download all of the required dependencies. Although other
|
||||
versions of Go may work, the project has only been tested with v1.20.
|
||||
|
||||
To build the Docker container, run `docker build -t magellan:latest .` in the
|
||||
project's directory.
|
||||
|
||||
## Usage
|
||||
|
||||
There are three main commands to use with the tool: `scan`, `list`, and `collect`.
|
||||
|
|
@ -81,6 +84,13 @@ to find the `hms-smd` API.
|
|||
|
||||
Note: If the `db.path` flag is not set, `magellan` will use /tmp/magellan.db by default.
|
||||
|
||||
Both the `scan` and `collect` commands can be ran via Docker after pulling the image:
|
||||
|
||||
```bash
|
||||
docker pull bikeshack/magellan:latest
|
||||
docker run bikeshack/magellan:latest /magellan.sh --scan "--subnet 172.16.0.0 --port 443 --timeout 3" --collect "--user admin --pass password --host http://vm01 --port 27779"
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
List of things left to fix, do, or ideas...
|
||||
|
|
@ -89,9 +99,9 @@ List of things left to fix, do, or ideas...
|
|||
* [ ] Set default port automatically depending on the driver used to scan
|
||||
* [X] Test using different `bmclib` supported drivers (mainly 'redfish')
|
||||
* [X] Confirm loading different components into `hms-smd`
|
||||
* [ ] Add ability to set subnet mask for scanning
|
||||
* [X] Add ability to set subnet mask for scanning
|
||||
* [ ] Add unit tests for `scan`, `list`, and `collect` commands
|
||||
* [ ] Clean up, remove unused, and tidy code
|
||||
* [X] Clean up, remove unused, and tidy code
|
||||
|
||||
## Copyright
|
||||
|
||||
|
|
|
|||
119
bin/magellan.sh
Normal file → Executable file
119
bin/magellan.sh
Normal file → Executable file
|
|
@ -1,21 +1,126 @@
|
|||
#!/bin/bash
|
||||
|
||||
EXE=./magellan
|
||||
SUBNETS=""
|
||||
PORTS=""
|
||||
USER=""
|
||||
PASS=""
|
||||
SMD_HOST=""
|
||||
SMD_PORT=""
|
||||
THREADS="1"
|
||||
TIMEOUT="30"
|
||||
ARGS=""
|
||||
FORCE_UPDATE=false
|
||||
SCAN_PARAMS=""
|
||||
COLLECT_PARAMS=""
|
||||
|
||||
|
||||
function build(){
|
||||
go mod tidy && go build -C bin/magellan
|
||||
}
|
||||
|
||||
function scan() {
|
||||
./magellan scan --subnet 172.16.0.0 --port 443
|
||||
# ./magellan scan --subnet 172.16.0.0 --port 443
|
||||
${EXE} scan ${SCAN_PARAMS}
|
||||
# --subnet ${SUBNETS} \
|
||||
# --port ${PORTS} \
|
||||
# --timeout ${TIMEOUT} \
|
||||
# --threads ${THREADS}
|
||||
}
|
||||
|
||||
function list(){
|
||||
./magellan list
|
||||
}
|
||||
|
||||
function update() {
|
||||
./magellan update --user admin --pass password --host 172.16.0.109 --component BMC --protocol HTTP --firmware-path ""
|
||||
# ./magellan list
|
||||
${EXE} list
|
||||
}
|
||||
|
||||
function collect() {
|
||||
./magellan collect --user admin --pass password
|
||||
# ./magellan collect --user admin --pass password
|
||||
${EXE} collect ${COLLECT_PARAMS}
|
||||
# --user ${USER} \
|
||||
# --pass ${PASS} \
|
||||
# --timeout ${TIMEOUT} \
|
||||
# --threads ${THREADS} \
|
||||
# --host ${SMD_HOST} \
|
||||
# --port ${SMD_PORT} \
|
||||
# --force-update ${FORCE_UPDATE}
|
||||
}
|
||||
|
||||
|
||||
# parse incoming arguments to set variables
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--scan)
|
||||
SCAN_PARAMS="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--collect)
|
||||
COLLECT_PARAMS="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--subnet)
|
||||
SUBNETS="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
-p|--port)
|
||||
PORTS="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--user)
|
||||
USER="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--pass|--password)
|
||||
PASS="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--smd-host)
|
||||
SMD_HOST="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--smd-port)
|
||||
SMD_PORT="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--timeout)
|
||||
TIMEOUT="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--threads)
|
||||
THREADS="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-*|--*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
ARGS+=("$1") # save positional arg
|
||||
shift # past argument
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
|
||||
|
||||
if [[ -n $1 ]]; then
|
||||
echo "Last line of file specified as non-opt/last argument:"
|
||||
tail -1 "$1"
|
||||
fi
|
||||
|
||||
scan
|
||||
collect
|
||||
|
||||
# run with docker
|
||||
# docker run magellan:latest magellan.sh \
|
||||
# --scan "--subnet 127.16.0.0 --port 443" \
|
||||
# --collect "--user admin --pass password --timeout 300 --threads 1 --smd-host host --smd-port port"
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
magellan "github.com/bikeshack/magellan/internal"
|
||||
"github.com/bikeshack/magellan/internal/api/smd"
|
||||
"github.com/bikeshack/magellan/internal/db/sqlite"
|
||||
"github.com/bikeshack/magellan/internal/log"
|
||||
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||
"github.com/OpenCHAMI/magellan/internal/api/smd"
|
||||
"github.com/OpenCHAMI/magellan/internal/db/sqlite"
|
||||
"github.com/OpenCHAMI/magellan/internal/log"
|
||||
"github.com/cznic/mathutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
|
@ -28,6 +28,16 @@ var collectCmd = &cobra.Command{
|
|||
l.Log.Errorf("could not get states: %v", err)
|
||||
}
|
||||
|
||||
// try to load access token either from env var, file, or config if var not set
|
||||
if accessToken == "" {
|
||||
var err error
|
||||
accessToken, err = LoadAccessToken()
|
||||
if err != nil {
|
||||
l.Log.Errorf("failed to load access token: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if threads <= 0 {
|
||||
threads = mathutil.Clamp(len(probeStates), 1, 255)
|
||||
}
|
||||
|
|
@ -40,7 +50,7 @@ var collectCmd = &cobra.Command{
|
|||
Timeout: timeout,
|
||||
Threads: threads,
|
||||
Verbose: verbose,
|
||||
WithSecureTLS: withSecureTLS,
|
||||
CaCertPath: cacertPath,
|
||||
OutputPath: outputPath,
|
||||
ForceUpdate: forceUpdate,
|
||||
}
|
||||
|
|
@ -81,6 +91,5 @@ func init() {
|
|||
viper.BindPFlag("collect.secure-tls", collectCmd.Flags().Lookup("secure-tls"))
|
||||
viper.BindPFlag("collect.cert-pool", collectCmd.Flags().Lookup("cert-pool"))
|
||||
|
||||
|
||||
rootCmd.AddCommand(collectCmd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bikeshack/magellan/internal/db/sqlite"
|
||||
"github.com/OpenCHAMI/magellan/internal/db/sqlite"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
|
|
|||
84
cmd/login.go
Normal file
84
cmd/login.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||
"github.com/OpenCHAMI/magellan/internal/log"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
loginUrl string
|
||||
targetHost string
|
||||
targetPort int
|
||||
tokenPath string
|
||||
forceLogin bool
|
||||
noBrowser bool
|
||||
)
|
||||
|
||||
var loginCmd = &cobra.Command{
|
||||
Use: "login",
|
||||
Short: "Log in with identity provider for access token",
|
||||
Long: "",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// make application logger
|
||||
l := log.NewLogger(logrus.New(), logrus.DebugLevel)
|
||||
|
||||
// check if we have a valid JWT before starting login
|
||||
if !forceLogin {
|
||||
// try getting the access token from env var
|
||||
testToken, err := LoadAccessToken()
|
||||
if err != nil {
|
||||
l.Log.Errorf("failed to load access token: %v", err)
|
||||
}
|
||||
|
||||
// parse into jwt.Token to validate
|
||||
token, err := jwt.Parse([]byte(testToken))
|
||||
if err != nil {
|
||||
fmt.Printf("failed to parse access token contents: %v\n", err)
|
||||
return
|
||||
}
|
||||
// check if the token is invalid and we need a new one
|
||||
err = jwt.Validate(token)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to validate access token...fetching a new one")
|
||||
} else {
|
||||
fmt.Printf("found a valid token...skipping login (use the '-f/--force' flag to login anyway)")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// start the login flow
|
||||
var err error
|
||||
accessToken, err = magellan.Login(loginUrl, targetHost, targetPort)
|
||||
if errors.Is(err, http.ErrServerClosed) {
|
||||
fmt.Printf("\n=========================================\nServer closed.\n=========================================\n\n")
|
||||
} else if err != nil {
|
||||
fmt.Printf("failed to start server: %v\n", err)
|
||||
}
|
||||
|
||||
// if we got a new token successfully, save it to the token path
|
||||
if accessToken != "" && tokenPath != "" {
|
||||
err := os.WriteFile(tokenPath, []byte(accessToken), os.ModePerm)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to write access token to file: %v\n", err)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
loginCmd.Flags().StringVar(&loginUrl, "url", "http://127.0.0.1:3333/login", "set the login URL")
|
||||
loginCmd.Flags().StringVar(&targetHost, "target-host", "127.0.0.1", "set the target host to return the access code")
|
||||
loginCmd.Flags().IntVar(&targetPort, "target-port", 5000, "set the target host to return the access code")
|
||||
loginCmd.Flags().BoolVarP(&forceLogin, "force", "f", false, "start the login process even with a valid token")
|
||||
loginCmd.Flags().StringVar(&tokenPath, "token-path", ".ochami-token", "set the path the load/save the access token")
|
||||
loginCmd.Flags().BoolVar(&noBrowser, "no-browser", false, "prevent the default browser from being opened automatically")
|
||||
rootCmd.AddCommand(loginCmd)
|
||||
}
|
||||
22
cmd/root.go
22
cmd/root.go
|
|
@ -12,13 +12,13 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
accessToken string
|
||||
timeout int
|
||||
threads int
|
||||
ports []int
|
||||
hosts []string
|
||||
protocol string
|
||||
withSecureTLS bool
|
||||
certPoolFile string
|
||||
cacertPath string
|
||||
user string
|
||||
pass string
|
||||
dbpath string
|
||||
|
|
@ -54,12 +54,30 @@ func Execute() {
|
|||
}
|
||||
}
|
||||
|
||||
func LoadAccessToken() (string, error) {
|
||||
// try to load token from env var
|
||||
testToken := os.Getenv("OCHAMI_ACCESS_TOKEN")
|
||||
if testToken != "" {
|
||||
return testToken, nil
|
||||
}
|
||||
|
||||
// try reading access token from a file
|
||||
b, err := os.ReadFile(tokenPath)
|
||||
if err == nil {
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// TODO: try to load token from config
|
||||
return "", fmt.Errorf("could not load from environment variable or file")
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(InitializeConfig)
|
||||
rootCmd.PersistentFlags().IntVar(&threads, "threads", -1, "set the number of threads")
|
||||
rootCmd.PersistentFlags().IntVar(&timeout, "timeout", 30, "set the timeout")
|
||||
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "set the config file path")
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", true, "set verbose flag")
|
||||
rootCmd.PersistentFlags().StringVar(&accessToken, "access-token", "", "set the access token")
|
||||
rootCmd.PersistentFlags().StringVar(&dbpath, "db.path", "/tmp/magellan/magellan.db", "set the probe storage path")
|
||||
|
||||
// bind viper config flags with cobra
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
|
||||
magellan "github.com/bikeshack/magellan/internal"
|
||||
"github.com/bikeshack/magellan/internal/db/sqlite"
|
||||
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||
"github.com/OpenCHAMI/magellan/internal/db/sqlite"
|
||||
|
||||
"github.com/cznic/mathutil"
|
||||
"github.com/spf13/cobra"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
magellan "github.com/bikeshack/magellan/internal"
|
||||
"github.com/bikeshack/magellan/internal/log"
|
||||
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||
"github.com/OpenCHAMI/magellan/internal/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
|
@ -37,7 +37,6 @@ var updateCmd = &cobra.Command{
|
|||
Pass: pass,
|
||||
Timeout: timeout,
|
||||
Port: port,
|
||||
WithSecureTLS: withSecureTLS,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +76,6 @@ func init() {
|
|||
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")
|
||||
updateCmd.Flags().BoolVar(&withSecureTLS, "secure-tls", false, "enable secure TLS")
|
||||
updateCmd.Flags().BoolVar(&status, "status", false, "get the status of the update")
|
||||
|
||||
viper.BindPFlag("bmc-host", updateCmd.Flags().Lookup("bmc-host"))
|
||||
|
|
|
|||
12
go.mod
12
go.mod
|
|
@ -1,4 +1,4 @@
|
|||
module github.com/bikeshack/magellan
|
||||
module github.com/OpenCHAMI/magellan
|
||||
|
||||
go 1.20
|
||||
|
||||
|
|
@ -6,9 +6,12 @@ require (
|
|||
github.com/Cray-HPE/hms-xname v1.3.0
|
||||
github.com/bmc-toolbox/bmclib/v2 v2.0.1-0.20230714152943-a1b87e2ff47f
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/jacobweinstock/registrar v0.4.7
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/lestrrat-go/jwx v1.2.29
|
||||
github.com/mattn/go-sqlite3 v1.14.6
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/viper v1.17.0
|
||||
|
|
@ -22,6 +25,7 @@ require (
|
|||
github.com/bmc-toolbox/common v0.0.0-20230717121556-5eb9915a8a5a // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
|
|
@ -30,6 +34,12 @@ require (
|
|||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/bikeshack/magellan/internal/util"
|
||||
"github.com/OpenCHAMI/magellan/internal/util"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ package smd
|
|||
// https://github.com/alexlovelltroy/hms-smd
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/bikeshack/magellan/internal/util"
|
||||
"github.com/OpenCHAMI/magellan/internal/util"
|
||||
// hms "github.com/alexlovelltroy/hms-smd"
|
||||
)
|
||||
|
||||
|
|
@ -52,8 +51,9 @@ func AddRedfishEndpoint(data []byte, headers map[string]string) error {
|
|||
url := makeEndpointUrl("/Inventory/RedfishEndpoints")
|
||||
res, body, err := util.MakeRequest(url, "POST", data, headers)
|
||||
if res != nil {
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("could not add redfish endpoint")
|
||||
statusOk := res.StatusCode >= 200 && res.StatusCode < 300
|
||||
if !statusOk {
|
||||
return fmt.Errorf("returned status code %d when adding endpoint", res.StatusCode)
|
||||
}
|
||||
fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body))
|
||||
}
|
||||
|
|
@ -69,7 +69,8 @@ func UpdateRedfishEndpoint(xname string, data []byte, headers map[string]string)
|
|||
res, body, err := util.MakeRequest(url, "PUT", data, headers)
|
||||
fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body))
|
||||
if res != nil {
|
||||
if res.StatusCode != http.StatusOK {
|
||||
statusOk := res.StatusCode >= 200 && res.StatusCode < 300
|
||||
if !statusOk {
|
||||
return fmt.Errorf("could not update redfish endpoint")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bikeshack/magellan/internal/log"
|
||||
"github.com/OpenCHAMI/magellan/internal/log"
|
||||
|
||||
"github.com/bikeshack/magellan/internal/api/smd"
|
||||
"github.com/bikeshack/magellan/internal/util"
|
||||
"github.com/OpenCHAMI/magellan/internal/api/smd"
|
||||
"github.com/OpenCHAMI/magellan/internal/util"
|
||||
|
||||
"github.com/Cray-HPE/hms-xname/xnames"
|
||||
bmclib "github.com/bmc-toolbox/bmclib/v2"
|
||||
|
|
@ -33,7 +33,6 @@ const (
|
|||
HTTPS_PORT = 443
|
||||
)
|
||||
|
||||
|
||||
// NOTE: ...params were getting too long...
|
||||
type QueryParams struct {
|
||||
Host string
|
||||
|
|
@ -45,15 +44,16 @@ type QueryParams struct {
|
|||
Threads int
|
||||
Preferred string
|
||||
Timeout int
|
||||
WithSecureTLS bool
|
||||
CertPoolFile string
|
||||
CaCertPath string
|
||||
Verbose bool
|
||||
IpmitoolPath string
|
||||
OutputPath string
|
||||
ForceUpdate bool
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
func NewClient(l *log.Logger, q *QueryParams) (*bmclib.Client, error) {
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
|
@ -75,9 +75,9 @@ func NewClient(l *log.Logger, q *QueryParams) (*bmclib.Client, error) {
|
|||
}
|
||||
|
||||
// only work if valid cert is provided
|
||||
if q.WithSecureTLS && q.CertPoolFile != "" {
|
||||
if q.CaCertPath != "" {
|
||||
pool := x509.NewCertPool()
|
||||
data, err := os.ReadFile(q.CertPoolFile)
|
||||
data, err := os.ReadFile(q.CaCertPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read cert pool file: %v", err)
|
||||
}
|
||||
|
|
@ -122,15 +122,8 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err
|
|||
done := make(chan struct{}, q.Threads+1)
|
||||
chanProbeState := make(chan ScannedResult, q.Threads+1)
|
||||
|
||||
// generate custom xnames for bmcs
|
||||
node := xnames.Node{
|
||||
Cabinet: 1000,
|
||||
Chassis: 1,
|
||||
ComputeModule: 7,
|
||||
NodeBMC: -1,
|
||||
}
|
||||
|
||||
// collect bmc information asynchronously
|
||||
var offset = 0
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(q.Threads)
|
||||
for i := 0; i < q.Threads; i++ {
|
||||
|
|
@ -144,13 +137,24 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err
|
|||
q.Host = ps.Host
|
||||
q.Port = ps.Port
|
||||
|
||||
client, err := NewClient(l, q)
|
||||
if err != nil {
|
||||
l.Log.Errorf("could not make client: %v", err)
|
||||
continue
|
||||
// generate custom xnames for bmcs
|
||||
node := xnames.Node{
|
||||
Cabinet: 1000,
|
||||
Chassis: 1,
|
||||
ComputeModule: 7,
|
||||
NodeBMC: offset,
|
||||
}
|
||||
offset += 1
|
||||
|
||||
node.NodeBMC += 1
|
||||
// bmclibClient, err := NewClient(l, q)
|
||||
// if err != nil {
|
||||
// l.Log.Errorf("could not make client: %v", err)
|
||||
// }
|
||||
|
||||
gofishClient, err := connectGofish(q)
|
||||
if err != nil {
|
||||
l.Log.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err)
|
||||
}
|
||||
|
||||
// data to be sent to smd
|
||||
data := map[string]any{
|
||||
|
|
@ -168,15 +172,18 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err
|
|||
var rm map[string]json.RawMessage
|
||||
|
||||
// inventories
|
||||
inventory, err := CollectInventory(client, q)
|
||||
if err != nil {
|
||||
l.Log.Errorf("could not query inventory (%v:%v): %v", q.Host, q.Port, err)
|
||||
}
|
||||
json.Unmarshal(inventory, &rm)
|
||||
data["Inventory"] = rm["Inventory"]
|
||||
// if bmclibClient != nil {
|
||||
// inventory, err := CollectInventory(bmclibClient, q)
|
||||
// if err != nil {
|
||||
// l.Log.Errorf("could not query inventory (%v:%v): %v", q.Host, q.Port, err)
|
||||
// }
|
||||
// json.Unmarshal(inventory, &rm)
|
||||
// data["Inventory"] = rm["Inventory"]
|
||||
// }
|
||||
|
||||
// chassis
|
||||
chassis, err := CollectChassis(q)
|
||||
if gofishClient != nil {
|
||||
chassis, err := CollectChassis(gofishClient, q)
|
||||
if err != nil {
|
||||
l.Log.Errorf("could not query chassis: %v", err)
|
||||
continue
|
||||
|
|
@ -184,35 +191,8 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err
|
|||
json.Unmarshal(chassis, &rm)
|
||||
data["Chassis"] = rm["Chassis"]
|
||||
|
||||
// ethernet interfaces
|
||||
// interfaces, err := QueryEthernetInterfaces(client, q)
|
||||
// if err != nil {
|
||||
// l.Log.Errorf("could not query ethernet interfaces: %v", err)
|
||||
// continue
|
||||
// }
|
||||
// json.Unmarshal(interfaces, &rm)
|
||||
// data["Interfaces"] = rm["Interfaces"]
|
||||
|
||||
// storage
|
||||
// storage, err := QueryStorage(q)
|
||||
// if err != nil {
|
||||
// l.Log.Errorf("could not query storage: %v", err)
|
||||
// continue
|
||||
// }
|
||||
// json.Unmarshal(storage, &rm)
|
||||
// data["Storage"] = rm["Storage"]
|
||||
|
||||
// get specific processor info
|
||||
// procs, err := QueryProcessors(q)
|
||||
// if err != nil {
|
||||
// l.Log.Errorf("could not query processors: %v", err)
|
||||
// }
|
||||
// var p map[string]interface{}
|
||||
// json.Unmarshal(procs, &p)
|
||||
// data["Processors"] = rm["Processors"]
|
||||
|
||||
// systems
|
||||
systems, err := CollectSystems(client, q)
|
||||
systems, err := CollectSystems(gofishClient, q)
|
||||
if err != nil {
|
||||
l.Log.Errorf("could not query systems: %v", err)
|
||||
}
|
||||
|
|
@ -225,39 +205,39 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err
|
|||
json.Unmarshal(rm["Systems"], &s)
|
||||
data["Name"] = s["Name"]
|
||||
}
|
||||
|
||||
// data["Type"] = rm[""]
|
||||
|
||||
// registries
|
||||
// registries, err := QueryRegisteries(q)
|
||||
// if err != nil {
|
||||
// l.Log.Errorf("could not query registries: %v", err)
|
||||
// }
|
||||
// json.Unmarshal(registries, &rm)
|
||||
// data["Registries"] = rm["Registries"]
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
b, err := json.MarshalIndent(data, "", " ")
|
||||
// use access token in authorization header if we have it
|
||||
if q.AccessToken != "" {
|
||||
headers["Authorization"] = "Bearer " + q.AccessToken
|
||||
}
|
||||
|
||||
body, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
l.Log.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(body))
|
||||
}
|
||||
|
||||
// write JSON data to file
|
||||
err = os.WriteFile(path.Clean(outputPath + "/" + q.Host + ".json"), b, os.ModePerm)
|
||||
err = os.WriteFile(path.Clean(outputPath+"/"+q.Host+".json"), body, os.ModePerm)
|
||||
if err != nil {
|
||||
l.Log.Errorf("could not write data to file: %v", err)
|
||||
}
|
||||
|
||||
// add all endpoints to smd
|
||||
err = smd.AddRedfishEndpoint(b, headers)
|
||||
err = smd.AddRedfishEndpoint(body, headers)
|
||||
if err != nil {
|
||||
l.Log.Error(err)
|
||||
|
||||
// try updating instead
|
||||
if q.ForceUpdate {
|
||||
err = smd.UpdateRedfishEndpoint(data["ID"].(string), b, headers)
|
||||
err = smd.UpdateRedfishEndpoint(data["ID"].(string), body, headers)
|
||||
if err != nil {
|
||||
l.Log.Error(err)
|
||||
}
|
||||
|
|
@ -324,9 +304,6 @@ func CollectMetadata(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("Metadata: %v\n", string(b))
|
||||
}
|
||||
ctxCancel()
|
||||
return b, nil
|
||||
}
|
||||
|
|
@ -340,7 +317,6 @@ func CollectInventory(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
ctxCancel()
|
||||
return nil, fmt.Errorf("could not open client: %v", err)
|
||||
}
|
||||
defer client.Close(ctx)
|
||||
|
||||
inventory, err := client.Inventory(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -348,7 +324,6 @@ func CollectInventory(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not get inventory: %v", err)
|
||||
}
|
||||
|
||||
|
||||
// retrieve inventory data
|
||||
data := map[string]any{"Inventory": inventory}
|
||||
b, err := json.MarshalIndent(data, "", " ")
|
||||
|
|
@ -357,9 +332,6 @@ func CollectInventory(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
ctxCancel()
|
||||
return b, nil
|
||||
}
|
||||
|
|
@ -372,7 +344,6 @@ func CollectPowerState(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
ctxCancel()
|
||||
return nil, fmt.Errorf("could not open client: %v", err)
|
||||
}
|
||||
defer client.Close(ctx)
|
||||
|
||||
powerState, err := client.GetPowerState(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -388,9 +359,6 @@ func CollectPowerState(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
ctxCancel()
|
||||
return b, nil
|
||||
|
||||
|
|
@ -422,11 +390,7 @@ func CollectUsers(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
// return b, nil
|
||||
ctxCancel()
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
|
@ -436,18 +400,10 @@ func CollectBios(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
// return nil, fmt.Errorf("could not make query: %v", err)
|
||||
// }
|
||||
b, err := makeRequest(client, client.GetBiosConfiguration, q.Timeout)
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
func CollectEthernetInterfaces(client *bmclib.Client, q *QueryParams, systemID string) ([]byte, error) {
|
||||
c, err := connectGofish(q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not connect to bmc: %v", err)
|
||||
}
|
||||
|
||||
func CollectEthernetInterfaces(c *gofish.APIClient, q *QueryParams, systemID string) ([]byte, error) {
|
||||
systems, err := c.Service.Systems()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err)
|
||||
|
|
@ -472,17 +428,10 @@ func CollectEthernetInterfaces(client *bmclib.Client, q *QueryParams, systemID s
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
// if q.Verbose {
|
||||
// fmt.Printf("%v\n", string(b))
|
||||
// }
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func CollectChassis(q *QueryParams) ([]byte, error) {
|
||||
c, err := connectGofish(q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err)
|
||||
}
|
||||
func CollectChassis(c *gofish.APIClient, q *QueryParams) ([]byte, error) {
|
||||
chassis, err := c.Service.Chassis()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not query chassis (%v:%v): %v", q.Host, q.Port, err)
|
||||
|
|
@ -494,18 +443,10 @@ func CollectChassis(q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func CollectStorage(q *QueryParams) ([]byte, error) {
|
||||
c, err := connectGofish(q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err)
|
||||
}
|
||||
|
||||
func CollectStorage(c *gofish.APIClient, q *QueryParams) ([]byte, error) {
|
||||
systems, err := c.Service.StorageSystems()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err)
|
||||
|
|
@ -527,18 +468,10 @@ func CollectStorage(q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func CollectSystems(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
||||
c, err := connectGofish(q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err)
|
||||
}
|
||||
|
||||
func CollectSystems(c *gofish.APIClient, q *QueryParams) ([]byte, error) {
|
||||
systems, err := c.Service.Systems()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not query systems (%v:%v): %v", q.Host, q.Port, err)
|
||||
|
|
@ -547,7 +480,7 @@ func CollectSystems(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
// query the system's ethernet interfaces
|
||||
var temp []map[string]any
|
||||
for _, system := range systems {
|
||||
interfaces, err := CollectEthernetInterfaces(client, q, system.ID)
|
||||
interfaces, err := CollectEthernetInterfaces(c, q, system.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
|
@ -565,18 +498,10 @@ func CollectSystems(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func CollectRegisteries(q *QueryParams) ([]byte, error) {
|
||||
c, err := connectGofish(q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err)
|
||||
}
|
||||
|
||||
func CollectRegisteries(c *gofish.APIClient, q *QueryParams) ([]byte, error) {
|
||||
registries, err := c.Service.Registries()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err)
|
||||
|
|
@ -588,9 +513,6 @@ func CollectRegisteries(q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
|
@ -631,17 +553,16 @@ func CollectProcessors(q *QueryParams) ([]byte, error) {
|
|||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
if q.Verbose {
|
||||
fmt.Printf("%v\n", string(b))
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func connectGofish(q *QueryParams) (*gofish.APIClient, error) {
|
||||
config := makeGofishConfig(q)
|
||||
config, err := makeGofishConfig(q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to make gofish config: %v", err)
|
||||
}
|
||||
c, err := gofish.Connect(config)
|
||||
if err != nil {
|
||||
|
||||
return nil, fmt.Errorf("could not connect to redfish endpoint: %v", err)
|
||||
}
|
||||
if c != nil {
|
||||
|
|
@ -655,15 +576,42 @@ func connectGofish(q *QueryParams) (*gofish.APIClient, error) {
|
|||
return c, err
|
||||
}
|
||||
|
||||
func makeGofishConfig(q *QueryParams) gofish.ClientConfig {
|
||||
url := baseRedfishUrl(q)
|
||||
func makeGofishConfig(q *QueryParams) (gofish.ClientConfig, error) {
|
||||
var (
|
||||
client = &http.Client{}
|
||||
url = baseRedfishUrl(q)
|
||||
config = gofish.ClientConfig{
|
||||
Endpoint: url,
|
||||
Username: q.User,
|
||||
Password: q.Pass,
|
||||
Insecure: q.CaCertPath == "",
|
||||
TLSHandshakeTimeout: q.Timeout,
|
||||
HTTPClient: client,
|
||||
// MaxConcurrentRequests: int64(q.Threads), // NOTE: this was added in latest gofish
|
||||
}
|
||||
)
|
||||
if q.CaCertPath != "" {
|
||||
cacert, err := os.ReadFile(q.CaCertPath)
|
||||
if err != nil {
|
||||
return config, fmt.Errorf("failed to read CA cert file: %v", err)
|
||||
}
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AppendCertsFromPEM(cacert)
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
}
|
||||
return gofish.ClientConfig{
|
||||
Endpoint: url,
|
||||
Username: q.User,
|
||||
Password: q.Pass,
|
||||
Insecure: !q.WithSecureTLS,
|
||||
Insecure: q.CaCertPath == "",
|
||||
TLSHandshakeTimeout: q.Timeout,
|
||||
}
|
||||
HTTPClient: client,
|
||||
// MaxConcurrentRequests: int64(q.Threads), // NOTE: this was added in latest gofish
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeRequest[T any](client *bmclib.Client, fn func(context.Context) (T, error), timeout int) ([]byte, error) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package sqlite
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
magellan "github.com/bikeshack/magellan/internal"
|
||||
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
|
|
|||
41
internal/login.go
Normal file
41
internal/login.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package magellan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/pkg/browser"
|
||||
)
|
||||
|
||||
func Login(loginUrl string, targetHost string, targetPort int) (string, error) {
|
||||
var accessToken string
|
||||
|
||||
// check and make sure the login URL isn't empty
|
||||
if loginUrl == "" {
|
||||
return "", fmt.Errorf("no login URL provided")
|
||||
}
|
||||
|
||||
// if a target host and port are provided, then add to URL
|
||||
if targetHost != "" && targetPort > 0 && targetPort < 65536 {
|
||||
loginUrl += fmt.Sprintf("?target=http://%s:%d", targetHost, targetPort)
|
||||
}
|
||||
|
||||
// open browser with the specified URL
|
||||
err := browser.OpenURL(loginUrl)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to open browser: %v", err)
|
||||
}
|
||||
|
||||
// start a temporary server to listen for token
|
||||
s := http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", targetHost, targetPort),
|
||||
}
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// try and extract access token from headers
|
||||
accessToken = r.Header.Get("access_token")
|
||||
s.Close()
|
||||
})
|
||||
return accessToken, s.ListenAndServe()
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bikeshack/magellan/internal/util"
|
||||
"github.com/OpenCHAMI/magellan/internal/util"
|
||||
)
|
||||
|
||||
type ScannedResult struct {
|
||||
|
|
@ -51,7 +51,6 @@ func rawConnect(host string, ports []int, timeout int, keepOpenOnly bool) []Scan
|
|||
return results
|
||||
}
|
||||
|
||||
|
||||
func GenerateHosts(subnet string, subnetMask *net.IP) []string {
|
||||
if subnet == "" || subnetMask == nil {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -10,15 +10,14 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bikeshack/magellan/internal/log"
|
||||
"github.com/bikeshack/magellan/internal/util"
|
||||
"github.com/OpenCHAMI/magellan/internal/log"
|
||||
"github.com/OpenCHAMI/magellan/internal/util"
|
||||
bmclib "github.com/bmc-toolbox/bmclib/v2"
|
||||
"github.com/bmc-toolbox/bmclib/v2/constants"
|
||||
bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
type UpdateParams struct {
|
||||
QueryParams
|
||||
FirmwarePath string
|
||||
|
|
@ -184,8 +183,6 @@ func GetUpdateStatus(q *UpdateParams) error {
|
|||
// return fmt.Errorf("could not read file: %v", err)
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// switch q.TransferProtocol {
|
||||
// case "HTTP":
|
||||
// default:
|
||||
|
|
|
|||
2
main.go
2
main.go
|
|
@ -1,7 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/bikeshack/magellan/cmd"
|
||||
"github.com/OpenCHAMI/magellan/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue