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
|
- go mod download
|
||||||
builds:
|
builds:
|
||||||
- env:
|
- env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=1
|
||||||
goos:
|
goos:
|
||||||
- darwin
|
|
||||||
- linux
|
- linux
|
||||||
- windows
|
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
|
- arm64
|
||||||
dockers:
|
dockers:
|
||||||
- image_templates:
|
- image_templates:
|
||||||
- bikeshack/{{.ProjectName}}:latest
|
- ghcr.io/openchami/{{.ProjectName}}:latest
|
||||||
- bikeshack/{{.ProjectName}}:{{ .Tag }}
|
- ghcr.io/openchami/{{.ProjectName}}:{{ .Tag }}
|
||||||
- bikeshack/{{.ProjectName}}:{{ .Major }}
|
- ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}
|
||||||
- bikeshack/{{.ProjectName}}:{{ .Major }}.{{ .Minor }}
|
- ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}.{{ .Minor }}
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--pull"
|
- "--pull"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
extra_files:
|
||||||
|
- LICENSE
|
||||||
|
- CHANGELOG.md
|
||||||
|
- README.md
|
||||||
|
- bin/magellan.sh
|
||||||
archives:
|
archives:
|
||||||
- format: tar.gz
|
- format: tar.gz
|
||||||
rlcp: true
|
rlcp: true
|
||||||
|
|
@ -34,10 +38,10 @@ archives:
|
||||||
{{- else }}{{ .Arch }}{{ end }}
|
{{- else }}{{ .Arch }}{{ end }}
|
||||||
{{- if .Arm }}v{{ .Arm }}{{ end }}
|
{{- if .Arm }}v{{ .Arm }}{{ end }}
|
||||||
files:
|
files:
|
||||||
- migrations/*
|
|
||||||
- LICENSE
|
- LICENSE
|
||||||
- CHANGELOG.md
|
- CHANGELOG.md
|
||||||
- README.md
|
- README.md
|
||||||
|
- bin/magellan.sh
|
||||||
checksum:
|
checksum:
|
||||||
name_template: 'checksums.txt'
|
name_template: 'checksums.txt'
|
||||||
snapshot:
|
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/),
|
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).
|
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]
|
## [Unreleased]
|
||||||
|
|
||||||
## [0.0.1] - 2023-09-14
|
## [0.0.1] - 2023-09-14
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
FROM cgr.dev/chainguard/wolfi-base
|
FROM cgr.dev/chainguard/wolfi-base
|
||||||
|
|
||||||
RUN apk add --no-cache tini
|
RUN apk add --no-cache tini bash
|
||||||
|
|
||||||
# nobody 65534:65534
|
# nobody 65534:65534
|
||||||
USER 65534:65534
|
USER 65534:65534
|
||||||
|
|
||||||
|
|
||||||
COPY magellan /
|
COPY magellan /magellan
|
||||||
|
COPY /bin/magellan.sh /magellan.sh
|
||||||
|
|
||||||
CMD [ "/magellan" ]
|
|
||||||
|
CMD [ "/magellan.sh" ]
|
||||||
|
|
||||||
ENTRYPOINT [ "/sbin/tini", "--" ]
|
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
|
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.
|
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
|
## Usage
|
||||||
|
|
||||||
There are three main commands to use with the tool: `scan`, `list`, and `collect`.
|
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.
|
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
|
## TODO
|
||||||
|
|
||||||
List of things left to fix, do, or ideas...
|
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
|
* [ ] Set default port automatically depending on the driver used to scan
|
||||||
* [X] Test using different `bmclib` supported drivers (mainly 'redfish')
|
* [X] Test using different `bmclib` supported drivers (mainly 'redfish')
|
||||||
* [X] Confirm loading different components into `hms-smd`
|
* [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
|
* [ ] 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
|
## 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(){
|
function build(){
|
||||||
go mod tidy && go build -C bin/magellan
|
go mod tidy && go build -C bin/magellan
|
||||||
}
|
}
|
||||||
|
|
||||||
function scan() {
|
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(){
|
function list(){
|
||||||
./magellan list
|
# ./magellan list
|
||||||
}
|
${EXE} list
|
||||||
|
|
||||||
function update() {
|
|
||||||
./magellan update --user admin --pass password --host 172.16.0.109 --component BMC --protocol HTTP --firmware-path ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function collect() {
|
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
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
magellan "github.com/bikeshack/magellan/internal"
|
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||||
"github.com/bikeshack/magellan/internal/api/smd"
|
"github.com/OpenCHAMI/magellan/internal/api/smd"
|
||||||
"github.com/bikeshack/magellan/internal/db/sqlite"
|
"github.com/OpenCHAMI/magellan/internal/db/sqlite"
|
||||||
"github.com/bikeshack/magellan/internal/log"
|
"github.com/OpenCHAMI/magellan/internal/log"
|
||||||
"github.com/cznic/mathutil"
|
"github.com/cznic/mathutil"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
@ -28,6 +28,16 @@ var collectCmd = &cobra.Command{
|
||||||
l.Log.Errorf("could not get states: %v", err)
|
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 {
|
if threads <= 0 {
|
||||||
threads = mathutil.Clamp(len(probeStates), 1, 255)
|
threads = mathutil.Clamp(len(probeStates), 1, 255)
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +50,7 @@ var collectCmd = &cobra.Command{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Threads: threads,
|
Threads: threads,
|
||||||
Verbose: verbose,
|
Verbose: verbose,
|
||||||
WithSecureTLS: withSecureTLS,
|
CaCertPath: cacertPath,
|
||||||
OutputPath: outputPath,
|
OutputPath: outputPath,
|
||||||
ForceUpdate: forceUpdate,
|
ForceUpdate: forceUpdate,
|
||||||
}
|
}
|
||||||
|
|
@ -81,6 +91,5 @@ func init() {
|
||||||
viper.BindPFlag("collect.secure-tls", collectCmd.Flags().Lookup("secure-tls"))
|
viper.BindPFlag("collect.secure-tls", collectCmd.Flags().Lookup("secure-tls"))
|
||||||
viper.BindPFlag("collect.cert-pool", collectCmd.Flags().Lookup("cert-pool"))
|
viper.BindPFlag("collect.cert-pool", collectCmd.Flags().Lookup("cert-pool"))
|
||||||
|
|
||||||
|
|
||||||
rootCmd.AddCommand(collectCmd)
|
rootCmd.AddCommand(collectCmd)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/bikeshack/magellan/internal/db/sqlite"
|
"github.com/OpenCHAMI/magellan/internal/db/sqlite"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"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 (
|
var (
|
||||||
|
accessToken string
|
||||||
timeout int
|
timeout int
|
||||||
threads int
|
threads int
|
||||||
ports []int
|
ports []int
|
||||||
hosts []string
|
hosts []string
|
||||||
protocol string
|
protocol string
|
||||||
withSecureTLS bool
|
cacertPath string
|
||||||
certPoolFile string
|
|
||||||
user string
|
user string
|
||||||
pass string
|
pass string
|
||||||
dbpath 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() {
|
func init() {
|
||||||
cobra.OnInitialize(InitializeConfig)
|
cobra.OnInitialize(InitializeConfig)
|
||||||
rootCmd.PersistentFlags().IntVar(&threads, "threads", -1, "set the number of threads")
|
rootCmd.PersistentFlags().IntVar(&threads, "threads", -1, "set the number of threads")
|
||||||
rootCmd.PersistentFlags().IntVar(&timeout, "timeout", 30, "set the timeout")
|
rootCmd.PersistentFlags().IntVar(&timeout, "timeout", 30, "set the timeout")
|
||||||
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "set the config file path")
|
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "set the config file path")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", true, "set verbose flag")
|
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")
|
rootCmd.PersistentFlags().StringVar(&dbpath, "db.path", "/tmp/magellan/magellan.db", "set the probe storage path")
|
||||||
|
|
||||||
// bind viper config flags with cobra
|
// bind viper config flags with cobra
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
magellan "github.com/bikeshack/magellan/internal"
|
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||||
"github.com/bikeshack/magellan/internal/db/sqlite"
|
"github.com/OpenCHAMI/magellan/internal/db/sqlite"
|
||||||
|
|
||||||
"github.com/cznic/mathutil"
|
"github.com/cznic/mathutil"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
magellan "github.com/bikeshack/magellan/internal"
|
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||||
"github.com/bikeshack/magellan/internal/log"
|
"github.com/OpenCHAMI/magellan/internal/log"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
@ -37,7 +37,6 @@ var updateCmd = &cobra.Command{
|
||||||
Pass: pass,
|
Pass: pass,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Port: port,
|
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(&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(&firmwareVersion, "firmware-version", "", "set the version of firmware to be installed")
|
||||||
updateCmd.Flags().StringVar(&component, "component", "", "set the component to upgrade")
|
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")
|
updateCmd.Flags().BoolVar(&status, "status", false, "get the status of the update")
|
||||||
|
|
||||||
viper.BindPFlag("bmc-host", updateCmd.Flags().Lookup("bmc-host"))
|
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
|
go 1.20
|
||||||
|
|
||||||
|
|
@ -6,9 +6,12 @@ require (
|
||||||
github.com/Cray-HPE/hms-xname v1.3.0
|
github.com/Cray-HPE/hms-xname v1.3.0
|
||||||
github.com/bmc-toolbox/bmclib/v2 v2.0.1-0.20230714152943-a1b87e2ff47f
|
github.com/bmc-toolbox/bmclib/v2 v2.0.1-0.20230714152943-a1b87e2ff47f
|
||||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548
|
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/jacobweinstock/registrar v0.4.7
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
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/mattn/go-sqlite3 v1.14.6
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/spf13/viper v1.17.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/bmc-toolbox/common v0.0.0-20230717121556-5eb9915a8a5a // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.4 // 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/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
|
@ -30,6 +34,12 @@ require (
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.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/pkg/errors v0.9.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/bikeshack/magellan/internal/util"
|
"github.com/OpenCHAMI/magellan/internal/util"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,8 @@ package smd
|
||||||
// https://github.com/alexlovelltroy/hms-smd
|
// https://github.com/alexlovelltroy/hms-smd
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/bikeshack/magellan/internal/util"
|
"github.com/OpenCHAMI/magellan/internal/util"
|
||||||
// hms "github.com/alexlovelltroy/hms-smd"
|
// hms "github.com/alexlovelltroy/hms-smd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -52,8 +51,9 @@ func AddRedfishEndpoint(data []byte, headers map[string]string) error {
|
||||||
url := makeEndpointUrl("/Inventory/RedfishEndpoints")
|
url := makeEndpointUrl("/Inventory/RedfishEndpoints")
|
||||||
res, body, err := util.MakeRequest(url, "POST", data, headers)
|
res, body, err := util.MakeRequest(url, "POST", data, headers)
|
||||||
if res != nil {
|
if res != nil {
|
||||||
if res.StatusCode != http.StatusOK {
|
statusOk := res.StatusCode >= 200 && res.StatusCode < 300
|
||||||
return fmt.Errorf("could not add redfish endpoint")
|
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))
|
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)
|
res, body, err := util.MakeRequest(url, "PUT", data, headers)
|
||||||
fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body))
|
fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body))
|
||||||
if res != nil {
|
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")
|
return fmt.Errorf("could not update redfish endpoint")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bikeshack/magellan/internal/log"
|
"github.com/OpenCHAMI/magellan/internal/log"
|
||||||
|
|
||||||
"github.com/bikeshack/magellan/internal/api/smd"
|
"github.com/OpenCHAMI/magellan/internal/api/smd"
|
||||||
"github.com/bikeshack/magellan/internal/util"
|
"github.com/OpenCHAMI/magellan/internal/util"
|
||||||
|
|
||||||
"github.com/Cray-HPE/hms-xname/xnames"
|
"github.com/Cray-HPE/hms-xname/xnames"
|
||||||
bmclib "github.com/bmc-toolbox/bmclib/v2"
|
bmclib "github.com/bmc-toolbox/bmclib/v2"
|
||||||
|
|
@ -33,7 +33,6 @@ const (
|
||||||
HTTPS_PORT = 443
|
HTTPS_PORT = 443
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// NOTE: ...params were getting too long...
|
// NOTE: ...params were getting too long...
|
||||||
type QueryParams struct {
|
type QueryParams struct {
|
||||||
Host string
|
Host string
|
||||||
|
|
@ -45,15 +44,16 @@ type QueryParams struct {
|
||||||
Threads int
|
Threads int
|
||||||
Preferred string
|
Preferred string
|
||||||
Timeout int
|
Timeout int
|
||||||
WithSecureTLS bool
|
CaCertPath string
|
||||||
CertPoolFile string
|
|
||||||
Verbose bool
|
Verbose bool
|
||||||
IpmitoolPath string
|
IpmitoolPath string
|
||||||
OutputPath string
|
OutputPath string
|
||||||
ForceUpdate bool
|
ForceUpdate bool
|
||||||
|
AccessToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(l *log.Logger, q *QueryParams) (*bmclib.Client, error) {
|
func NewClient(l *log.Logger, q *QueryParams) (*bmclib.Client, error) {
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
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
|
// only work if valid cert is provided
|
||||||
if q.WithSecureTLS && q.CertPoolFile != "" {
|
if q.CaCertPath != "" {
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
data, err := os.ReadFile(q.CertPoolFile)
|
data, err := os.ReadFile(q.CaCertPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read cert pool file: %v", err)
|
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)
|
done := make(chan struct{}, q.Threads+1)
|
||||||
chanProbeState := make(chan ScannedResult, 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
|
// collect bmc information asynchronously
|
||||||
|
var offset = 0
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(q.Threads)
|
wg.Add(q.Threads)
|
||||||
for i := 0; i < q.Threads; i++ {
|
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.Host = ps.Host
|
||||||
q.Port = ps.Port
|
q.Port = ps.Port
|
||||||
|
|
||||||
client, err := NewClient(l, q)
|
// generate custom xnames for bmcs
|
||||||
if err != nil {
|
node := xnames.Node{
|
||||||
l.Log.Errorf("could not make client: %v", err)
|
Cabinet: 1000,
|
||||||
continue
|
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 to be sent to smd
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
|
|
@ -168,15 +172,18 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err
|
||||||
var rm map[string]json.RawMessage
|
var rm map[string]json.RawMessage
|
||||||
|
|
||||||
// inventories
|
// inventories
|
||||||
inventory, err := CollectInventory(client, q)
|
// if bmclibClient != nil {
|
||||||
if err != nil {
|
// inventory, err := CollectInventory(bmclibClient, q)
|
||||||
l.Log.Errorf("could not query inventory (%v:%v): %v", q.Host, q.Port, err)
|
// 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"]
|
// json.Unmarshal(inventory, &rm)
|
||||||
|
// data["Inventory"] = rm["Inventory"]
|
||||||
|
// }
|
||||||
|
|
||||||
// chassis
|
// chassis
|
||||||
chassis, err := CollectChassis(q)
|
if gofishClient != nil {
|
||||||
|
chassis, err := CollectChassis(gofishClient, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Log.Errorf("could not query chassis: %v", err)
|
l.Log.Errorf("could not query chassis: %v", err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -184,35 +191,8 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err
|
||||||
json.Unmarshal(chassis, &rm)
|
json.Unmarshal(chassis, &rm)
|
||||||
data["Chassis"] = rm["Chassis"]
|
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
|
||||||
systems, err := CollectSystems(client, q)
|
systems, err := CollectSystems(gofishClient, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Log.Errorf("could not query systems: %v", err)
|
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)
|
json.Unmarshal(rm["Systems"], &s)
|
||||||
data["Name"] = s["Name"]
|
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 := make(map[string]string)
|
||||||
headers["Content-Type"] = "application/json"
|
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 {
|
if err != nil {
|
||||||
l.Log.Errorf("could not marshal JSON: %v", err)
|
l.Log.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if q.Verbose {
|
||||||
|
fmt.Printf("%v\n", string(body))
|
||||||
|
}
|
||||||
|
|
||||||
// write JSON data to file
|
// 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 {
|
if err != nil {
|
||||||
l.Log.Errorf("could not write data to file: %v", err)
|
l.Log.Errorf("could not write data to file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// add all endpoints to smd
|
// add all endpoints to smd
|
||||||
err = smd.AddRedfishEndpoint(b, headers)
|
err = smd.AddRedfishEndpoint(body, headers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Log.Error(err)
|
l.Log.Error(err)
|
||||||
|
|
||||||
// try updating instead
|
// try updating instead
|
||||||
if q.ForceUpdate {
|
if q.ForceUpdate {
|
||||||
err = smd.UpdateRedfishEndpoint(data["ID"].(string), b, headers)
|
err = smd.UpdateRedfishEndpoint(data["ID"].(string), body, headers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Log.Error(err)
|
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)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("Metadata: %v\n", string(b))
|
|
||||||
}
|
|
||||||
ctxCancel()
|
ctxCancel()
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
@ -340,7 +317,6 @@ func CollectInventory(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
||||||
ctxCancel()
|
ctxCancel()
|
||||||
return nil, fmt.Errorf("could not open client: %v", err)
|
return nil, fmt.Errorf("could not open client: %v", err)
|
||||||
}
|
}
|
||||||
defer client.Close(ctx)
|
|
||||||
|
|
||||||
inventory, err := client.Inventory(ctx)
|
inventory, err := client.Inventory(ctx)
|
||||||
if err != nil {
|
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)
|
return nil, fmt.Errorf("could not get inventory: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// retrieve inventory data
|
// retrieve inventory data
|
||||||
data := map[string]any{"Inventory": inventory}
|
data := map[string]any{"Inventory": inventory}
|
||||||
b, err := json.MarshalIndent(data, "", " ")
|
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)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
ctxCancel()
|
ctxCancel()
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
@ -372,7 +344,6 @@ func CollectPowerState(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
||||||
ctxCancel()
|
ctxCancel()
|
||||||
return nil, fmt.Errorf("could not open client: %v", err)
|
return nil, fmt.Errorf("could not open client: %v", err)
|
||||||
}
|
}
|
||||||
defer client.Close(ctx)
|
|
||||||
|
|
||||||
powerState, err := client.GetPowerState(ctx)
|
powerState, err := client.GetPowerState(ctx)
|
||||||
if err != nil {
|
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)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
ctxCancel()
|
ctxCancel()
|
||||||
return b, nil
|
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 nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// return b, nil
|
|
||||||
ctxCancel()
|
ctxCancel()
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
return b, nil
|
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)
|
// return nil, fmt.Errorf("could not make query: %v", err)
|
||||||
// }
|
// }
|
||||||
b, err := makeRequest(client, client.GetBiosConfiguration, q.Timeout)
|
b, err := makeRequest(client, client.GetBiosConfiguration, q.Timeout)
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CollectEthernetInterfaces(client *bmclib.Client, q *QueryParams, systemID string) ([]byte, error) {
|
func CollectEthernetInterfaces(c *gofish.APIClient, q *QueryParams, systemID string) ([]byte, error) {
|
||||||
c, err := connectGofish(q)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not connect to bmc: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
systems, err := c.Service.Systems()
|
systems, err := c.Service.Systems()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err)
|
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)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if q.Verbose {
|
|
||||||
// fmt.Printf("%v\n", string(b))
|
|
||||||
// }
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CollectChassis(q *QueryParams) ([]byte, error) {
|
func CollectChassis(c *gofish.APIClient, 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)
|
|
||||||
}
|
|
||||||
chassis, err := c.Service.Chassis()
|
chassis, err := c.Service.Chassis()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not query chassis (%v:%v): %v", q.Host, q.Port, err)
|
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)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CollectStorage(q *QueryParams) ([]byte, error) {
|
func CollectStorage(c *gofish.APIClient, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
systems, err := c.Service.StorageSystems()
|
systems, err := c.Service.StorageSystems()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err)
|
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)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CollectSystems(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
func CollectSystems(c *gofish.APIClient, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
systems, err := c.Service.Systems()
|
systems, err := c.Service.Systems()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not query systems (%v:%v): %v", q.Host, q.Port, err)
|
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
|
// query the system's ethernet interfaces
|
||||||
var temp []map[string]any
|
var temp []map[string]any
|
||||||
for _, system := range systems {
|
for _, system := range systems {
|
||||||
interfaces, err := CollectEthernetInterfaces(client, q, system.ID)
|
interfaces, err := CollectEthernetInterfaces(c, q, system.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -565,18 +498,10 @@ func CollectSystems(client *bmclib.Client, q *QueryParams) ([]byte, error) {
|
||||||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CollectRegisteries(q *QueryParams) ([]byte, error) {
|
func CollectRegisteries(c *gofish.APIClient, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
registries, err := c.Service.Registries()
|
registries, err := c.Service.Registries()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err)
|
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)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -631,17 +553,16 @@ func CollectProcessors(q *QueryParams) ([]byte, error) {
|
||||||
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
return nil, fmt.Errorf("could not marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Verbose {
|
|
||||||
fmt.Printf("%v\n", string(b))
|
|
||||||
}
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectGofish(q *QueryParams) (*gofish.APIClient, error) {
|
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)
|
c, err := gofish.Connect(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
return nil, fmt.Errorf("could not connect to redfish endpoint: %v", err)
|
return nil, fmt.Errorf("could not connect to redfish endpoint: %v", err)
|
||||||
}
|
}
|
||||||
if c != nil {
|
if c != nil {
|
||||||
|
|
@ -655,15 +576,42 @@ func connectGofish(q *QueryParams) (*gofish.APIClient, error) {
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeGofishConfig(q *QueryParams) gofish.ClientConfig {
|
func makeGofishConfig(q *QueryParams) (gofish.ClientConfig, error) {
|
||||||
url := baseRedfishUrl(q)
|
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{
|
return gofish.ClientConfig{
|
||||||
Endpoint: url,
|
Endpoint: url,
|
||||||
Username: q.User,
|
Username: q.User,
|
||||||
Password: q.Pass,
|
Password: q.Pass,
|
||||||
Insecure: !q.WithSecureTLS,
|
Insecure: q.CaCertPath == "",
|
||||||
TLSHandshakeTimeout: q.Timeout,
|
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) {
|
func makeRequest[T any](client *bmclib.Client, fn func(context.Context) (T, error), timeout int) ([]byte, error) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package sqlite
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
magellan "github.com/bikeshack/magellan/internal"
|
magellan "github.com/OpenCHAMI/magellan/internal"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"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"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bikeshack/magellan/internal/util"
|
"github.com/OpenCHAMI/magellan/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ScannedResult struct {
|
type ScannedResult struct {
|
||||||
|
|
@ -51,7 +51,6 @@ func rawConnect(host string, ports []int, timeout int, keepOpenOnly bool) []Scan
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func GenerateHosts(subnet string, subnetMask *net.IP) []string {
|
func GenerateHosts(subnet string, subnetMask *net.IP) []string {
|
||||||
if subnet == "" || subnetMask == nil {
|
if subnet == "" || subnetMask == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,14 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bikeshack/magellan/internal/log"
|
"github.com/OpenCHAMI/magellan/internal/log"
|
||||||
"github.com/bikeshack/magellan/internal/util"
|
"github.com/OpenCHAMI/magellan/internal/util"
|
||||||
bmclib "github.com/bmc-toolbox/bmclib/v2"
|
bmclib "github.com/bmc-toolbox/bmclib/v2"
|
||||||
"github.com/bmc-toolbox/bmclib/v2/constants"
|
"github.com/bmc-toolbox/bmclib/v2/constants"
|
||||||
bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors"
|
bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type UpdateParams struct {
|
type UpdateParams struct {
|
||||||
QueryParams
|
QueryParams
|
||||||
FirmwarePath string
|
FirmwarePath string
|
||||||
|
|
@ -184,8 +183,6 @@ func GetUpdateStatus(q *UpdateParams) error {
|
||||||
// return fmt.Errorf("could not read file: %v", err)
|
// return fmt.Errorf("could not read file: %v", err)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// switch q.TransferProtocol {
|
// switch q.TransferProtocol {
|
||||||
// case "HTTP":
|
// case "HTTP":
|
||||||
// default:
|
// default:
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -1,7 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bikeshack/magellan/cmd"
|
"github.com/OpenCHAMI/magellan/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue