Skip to content

Commit

Permalink
Merge pull request #2 from Golgautier/add-debug-mode
Browse files Browse the repository at this point in the history
Add-on of debug mode and --old-pc flag
  • Loading branch information
Golgautier authored Sep 4, 2024
2 parents 5a1c46c + 84522bd commit a5eccf4
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 34 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/dependabot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2

on:
push:
tags:
- "v*"

updates:
# Enable version updates for Go modules
- package-ecosystem: "gomod"
Expand Down
Empty file.
Empty file.
39 changes: 26 additions & 13 deletions functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

func ProgUsage() bool {
fmt.Println("Usage : nvgm [--server <PC Name>] [--user <Username>] [--help] [--usage] [--secure-mode]")
fmt.Println("Usage : nvgm [--server <PC Name>] [--user <Username>] [--help] [--usage] [--secure-mode] [--debug-mode] [--old-pc]")
os.Exit(1)
return true
}
Expand All @@ -26,8 +26,14 @@ func GetPrismInfo() {
help := flag.Bool("help", false, "Request usage")
usage := flag.Bool("usage", false, "Request usage")
secure := flag.Bool("secure-mode", false, "Request usage")
debug := flag.Bool("debug-mode", false, "Request usage")
compatibility := flag.Bool("old-pc", false, "Request usage")
flag.Parse()

if *help || *usage {
ProgUsage()
}

// Affect or request server value
if *PC == string("") {
fmt.Printf("Please enter Prism Central IP or FQDN : ")
Expand All @@ -36,10 +42,6 @@ func GetPrismInfo() {
MyPrism.PC = *PC
}

if *help || *usage {
ProgUsage()
}

// Affect or request user value
if *User == string("") {
fmt.Printf("Please enter Prism User : ")
Expand All @@ -65,6 +67,17 @@ func GetPrismInfo() {
ActivateSSLCheck(false)
}

if *debug {
MyPrism.ActivateDebug("./debug.log")
}

if *compatibility {
MyPrism.Compatibility = true
fmt.Println("Compatibility mode activated (for 2023.2 < PC < 2024.1)")
} else {
MyPrism.Compatibility = false
}

}

// =========== ActivateSSLCheck ===========
Expand All @@ -73,11 +86,11 @@ func ActivateSSLCheck(value bool) {

}

// =========== CheckErr ===========
// This function is will handle errors
func CheckErr(context string, err error) {
if err != nil {
fmt.Println("ERROR", context, " : ", err.Error())
os.Exit(2)
}
}
// // =========== CheckErr ===========
// // This function is will handle errors
// func CheckErr(context string, err error) {
// if err != nil {
// fmt.Println("ERROR", context, " : ", err.Error())
// os.Exit(2)
// }
// }
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ require (
github.com/mattn/go-runewidth v0.0.2 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/sys v0.2.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzC
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
Expand Down
85 changes: 70 additions & 15 deletions ntnx_api_call/ntnx_api_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
"net/http"
"strings"
"time"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

// Emoji symbols from http://www.unicode.org/emoji/charts/emoji-list.html
Expand All @@ -21,22 +24,30 @@ var symbols = map[string]string{
}

type Ntnx_endpoint struct {
PC string
PE string
Mode string
User string
Password string
Cert string
Chain string
Key string
PC string
PE string
Mode string
User string
Password string
Cert string
Chain string
Key string
DebugMode bool
DebugLogger *zap.SugaredLogger
RetryMode bool
RetryNumber int
Compatibility bool
}

const const_max_retry int = 3

// =========== CheckErr ===========
// This function is will handle errors
func CheckErr(context string, err error) {
func CheckErr(context string, err error, debugmode bool, debuglogger *zap.SugaredLogger) {
if err != nil {
if debugmode {
debuglogger.Debugf("API Call Error : %s\n", err.Error())
}
log.Fatal(symbols["FAIL"], context, err.Error())
}
}
Expand Down Expand Up @@ -70,6 +81,32 @@ func (e Ntnx_endpoint) WaitForTask(task string) (bool, string, string) {

}

// =========== ActivateDebug ===========
// Create file to store all API calls for debug purpose
func (e *Ntnx_endpoint) ActivateDebug(filename string) {
myconf := zap.NewDevelopmentEncoderConfig()
myconf.TimeKey = "timestamp"
myconf.EncodeTime = zapcore.ISO8601TimeEncoder
myconf.CallerKey = ""

config := zap.Config{
Level: zap.NewAtomicLevelAt(zapcore.DebugLevel),
Development: true,
Encoding: "console",
EncoderConfig: myconf,
OutputPaths: []string{filename},
ErrorOutputPaths: []string{filename},
}

logger, err := config.Build()
if err != nil {
panic(err)
}
e.DebugMode = true
e.DebugLogger = logger.Sugar()
defer logger.Sync()
}

// =========== CallAPIJSON ===========
// Do a call API and unmarshall the result
func (e Ntnx_endpoint) CallAPIJSON(target string, method string, url string, payload string, retour interface{}) {
Expand Down Expand Up @@ -102,8 +139,15 @@ func (e Ntnx_endpoint) CallAPIJSON(target string, method string, url string, pay
}

// Create new request

// Log it
if e.DebugMode {
e.DebugLogger.Debugf("New API Call : %s %s %s\n", ReqMethod, long_url, bytes.NewBuffer(jsonStr))
}

// Execute it
req, err := http.NewRequest(ReqMethod, long_url, bytes.NewBuffer(jsonStr))
CheckErr("Unable to prepare API Call", err)
CheckErr("Unable to prepare API Call", err, e.DebugMode, e.DebugLogger)

// Define default headers
req.Header.Add("Accept", "application/json")
Expand All @@ -116,7 +160,7 @@ func (e Ntnx_endpoint) CallAPIJSON(target string, method string, url string, pay
} else if e.Mode == "cert" {

_, err := tls.X509KeyPair([]byte(e.Cert), []byte(e.Key))
CheckErr("Unable to load certs", err)
CheckErr("Unable to load certs", err, e.DebugMode, e.DebugLogger)

} else {
log.Fatalln("FAIL", "Mode "+e.Mode+" unknown for Nutanix API call")
Expand All @@ -127,19 +171,30 @@ func (e Ntnx_endpoint) CallAPIJSON(target string, method string, url string, pay

// If an error occurs, we retry
for retry := 1; retry < const_max_retry && err != nil; retry++ {
if e.DebugMode {
e.DebugLogger.Debugf("Try %d...\n", retry)
}
resp, err = client.Do(req)
}
CheckErr("API Call failed", err)
CheckErr("API Call failed", err, e.DebugMode, e.DebugLogger)

// Log error is > 299
if int(resp.StatusCode) > 299 {
log.Fatal(symbols["FAIL"], " API Call failed : ", resp.Status)

// Put it in debug file if requested
if e.DebugMode {
e.DebugLogger.Debugf("API Call Error : %s\n%s\n", resp.Status, resp.Request)
}

// Display it and exit
log.Fatal(symbols["FAIL"], " API Call failed :", resp.Status)
}

defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
CheckErr("Unable to read API answer body", err)
CheckErr("Unable to read API answer body", err, e.DebugMode, e.DebugLogger)

// Transform json answer to map
err = json.Unmarshal(bodyBytes, &retour)
CheckErr("Unable to get json answer from API Call.", err)
CheckErr("Unable to get json answer from API Call.", err, e.DebugMode, e.DebugLogger)
}
12 changes: 7 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ just launch `nvgm` and answer to all questions.

With parameters :
Launch `nvgm` with several parameters :
- `-help` or `-usage`: Display usage message
- `-secure-mode`: Secure mode, to force https with valid certificate
- `-server <Server name>` : Specify Prism Central name or address
- `-user <User name>` : Specify user to use (must have admin rights)
- `--help` or `-usage`: Display usage message
- `--secure-mode`: Secure mode, to force https with valid certificate
- `--server <Server name>` : Specify Prism Central name or address
- `--user <User name>` : Specify user to use (must have admin rights)
- `--debug-mode` : create debug.log fiel with all API calls done to PC
- `--old-pc` : if you have PC before 2024.1, please use this flag

Password will be requested interactively

Example :
```
./nvgm -server pc.ntnx.fr -user admin
./nvgm -server pc.ntnx.fr --user admin
```

## Usage ##
Expand Down
6 changes: 5 additions & 1 deletion vg.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ func GetVGList() {
} `json:"data"`
}

MyPrism.CallAPIJSON("PC", "GET", "/api/storage/v4.0.a2/config/storage-containers", "", &tmp1)
if MyPrism.Compatibility {
MyPrism.CallAPIJSON("PC", "GET", "/api/storage/v4.0.a2/config/storage-containers", "", &tmp1)
} else {
MyPrism.CallAPIJSON("PC", "GET", "/api/clustermgmt/v4.0.b2/config/storage-containers", "", &tmp1)
}

// Parse all SC
for tmp := range tmp1.Data {
Expand Down

0 comments on commit a5eccf4

Please sign in to comment.