Skip to content

Commit bc37d47

Browse files
committed
Code refactoring
1 parent 46b33c1 commit bc37d47

File tree

10 files changed

+336
-267
lines changed

10 files changed

+336
-267
lines changed

cmd/boot.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/Charlie-Root/smcli/host"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var BootCmd = &cobra.Command{
11+
Use: "boot [cd|pxe]",
12+
Short: "Set boot order",
13+
Args: cobra.ExactArgs(2),
14+
Run: func(cmd *cobra.Command, args []string) {
15+
action := args[0]
16+
hostName := args[1]
17+
18+
host.SelectHost(hostName)
19+
host.GetRedfishPath()
20+
21+
switch action {
22+
case "cd":
23+
host.ServerSetBootOnceCD()
24+
case "pxe":
25+
host.ServerSetBootOncePXE()
26+
default:
27+
fmt.Println("Unknown boot command:", action)
28+
}
29+
},
30+
}

cmd/media.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/Charlie-Root/smcli/host"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var MediaCmd = &cobra.Command{
11+
Use: "media [insert|eject|status]",
12+
Short: "Manage virtual media",
13+
Args: cobra.ExactArgs(2),
14+
Run: func(cmd *cobra.Command, args []string) {
15+
action := args[0]
16+
hostName := args[1]
17+
18+
host.SelectHost(hostName)
19+
host.GetRedfishPath()
20+
21+
switch action {
22+
case "insert":
23+
host.ServerVirtualMediaInsert()
24+
case "eject":
25+
host.ServerVirtualMediaEject()
26+
case "status":
27+
host.ServerVirtualMediaStatus()
28+
default:
29+
fmt.Println("Unknown media command:", action)
30+
}
31+
},
32+
}

cmd/power.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/Charlie-Root/smcli/host"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var PowerCmd = &cobra.Command{
11+
Use: "power [on|off|restart]",
12+
Short: "Manage power",
13+
Args: cobra.ExactArgs(2),
14+
Run: func(cmd *cobra.Command, args []string) {
15+
action := args[0]
16+
hostName := args[1]
17+
18+
host.SelectHost(hostName)
19+
host.GetRedfishPath()
20+
21+
switch action {
22+
case "on":
23+
host.ServerPowerOn()
24+
case "off":
25+
host.ServerPowerOff()
26+
case "restart":
27+
host.ServerRestart()
28+
default:
29+
fmt.Println("Unknown power command:", action)
30+
}
31+
},
32+
}

cmd/root.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
var Verbose bool
8+
9+
var RootCmd = &cobra.Command{
10+
Use: "sm-cli",
11+
}

config/config.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package config
2+
3+
import (
4+
"log"
5+
"os"
6+
7+
"gopkg.in/yaml.v2"
8+
)
9+
10+
type Host struct {
11+
Name string `yaml:"name"`
12+
BMCAddress string `yaml:"bmc_address"`
13+
UsernamePassword string `yaml:"username_password"`
14+
ISOImage string `yaml:"iso_image"`
15+
}
16+
17+
type Config struct {
18+
Hosts []Host `yaml:"hosts"`
19+
}
20+
21+
var ConfigData Config
22+
23+
func LoadConfig(filename string) {
24+
file, err := os.Open(filename)
25+
if err != nil {
26+
log.Fatalf("Error opening config file: %v", err)
27+
}
28+
defer file.Close()
29+
30+
decoder := yaml.NewDecoder(file)
31+
err = decoder.Decode(&ConfigData)
32+
if err != nil {
33+
log.Fatalf("Error decoding config file: %v", err)
34+
}
35+
}

host/host.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package host
2+
3+
import (
4+
"log"
5+
6+
"github.com/Charlie-Root/smcli/config"
7+
)
8+
9+
var (
10+
CurrentHost config.Host
11+
RedfishPath string
12+
verbose bool
13+
)
14+
15+
func SetVerbose(v bool) {
16+
verbose = v
17+
}
18+
19+
func SelectHost(name string) {
20+
for _, host := range config.ConfigData.Hosts {
21+
if host.Name == name {
22+
CurrentHost = host
23+
return
24+
}
25+
}
26+
log.Fatalf("Host with name %s not found", name)
27+
}

host/redfish.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package host
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
)
8+
9+
func GetRedfishPath() {
10+
url := fmt.Sprintf("https://%s/redfish/v1/Systems", CurrentHost.BMCAddress)
11+
respBody := MakeRequest("GET", url, nil)
12+
13+
var data map[string]interface{}
14+
if err := json.Unmarshal(respBody, &data); err != nil {
15+
log.Fatal(err)
16+
}
17+
18+
if members, ok := data["Members"].([]interface{}); ok && len(members) > 0 {
19+
if member, ok := members[0].(map[string]interface{}); ok {
20+
RedfishPath = member["@odata.id"].(string)
21+
}
22+
}
23+
}

host/request.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package host
2+
3+
import (
4+
"bytes"
5+
"crypto/tls"
6+
"encoding/json"
7+
"fmt"
8+
"io/ioutil"
9+
"log"
10+
"net/http"
11+
"strings"
12+
)
13+
14+
func MakeRequest(method, url string, body []byte) []byte {
15+
tr := &http.Transport{
16+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
17+
}
18+
client := &http.Client{Transport: tr}
19+
20+
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
21+
if err != nil {
22+
log.Fatal(err)
23+
}
24+
authParts := strings.SplitN(CurrentHost.UsernamePassword, ":", 2)
25+
req.SetBasicAuth(authParts[0], authParts[1])
26+
req.Header.Set("Content-Type", "application/json")
27+
req.Header.Set("Accept", "application/json")
28+
29+
resp, err := client.Do(req)
30+
if err != nil {
31+
log.Fatal(err)
32+
}
33+
defer resp.Body.Close()
34+
35+
respBody, err := ioutil.ReadAll(resp.Body)
36+
if err != nil {
37+
log.Fatal(err)
38+
}
39+
40+
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
41+
var success struct {
42+
Message string `json:"message"`
43+
}
44+
if err := json.Unmarshal(respBody, &success); err == nil {
45+
if verbose {
46+
fmt.Println("Success:", success.Message)
47+
}
48+
} else {
49+
if verbose {
50+
fmt.Println("Success:", string(respBody))
51+
}
52+
}
53+
} else {
54+
var errorResp struct {
55+
Error struct {
56+
Code string `json:"code"`
57+
Message string `json:"message"`
58+
MessageExtended []struct {
59+
MessageId string `json:"MessageId"`
60+
Severity string `json:"Severity"`
61+
Resolution string `json:"Resolution"`
62+
Message string `json:"Message"`
63+
MessageArgs []string `json:"MessageArgs"`
64+
RelatedProperties []string `json:"RelatedProperties"`
65+
} `json:"@Message.ExtendedInfo"`
66+
} `json:"error"`
67+
}
68+
if err := json.Unmarshal(respBody, &errorResp); err == nil {
69+
fmt.Printf("Error: %s - %s\n", errorResp.Error.Code, errorResp.Error.Message)
70+
for _, info := range errorResp.Error.MessageExtended {
71+
fmt.Printf(" %s: %s\n", info.Severity, info.Message)
72+
if info.Resolution != "" {
73+
fmt.Printf(" Resolution: %s\n", info.Resolution)
74+
}
75+
}
76+
} else {
77+
fmt.Printf("Error: %s\n", resp.Status)
78+
fmt.Println(string(respBody))
79+
}
80+
}
81+
82+
return respBody
83+
}

host/server.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package host
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
func ServerPowerOff() {
8+
url := fmt.Sprintf("https://%s%s/Actions/ComputerSystem.Reset", CurrentHost.BMCAddress, RedfishPath)
9+
body := []byte(`{"ResetType": "ForceOff"}`)
10+
MakeRequest("POST", url, body)
11+
}
12+
13+
func ServerPowerOn() {
14+
url := fmt.Sprintf("https://%s%s/Actions/ComputerSystem.Reset", CurrentHost.BMCAddress, RedfishPath)
15+
body := []byte(`{"ResetType": "On"}`)
16+
MakeRequest("POST", url, body)
17+
}
18+
19+
func ServerRestart() {
20+
url := fmt.Sprintf("https://%s%s/Actions/ComputerSystem.Reset", CurrentHost.BMCAddress, RedfishPath)
21+
body := []byte(`{"ResetType": "ForceRestart"}`)
22+
MakeRequest("POST", url, body)
23+
}
24+
25+
func ServerVirtualMediaInsert() {
26+
url := fmt.Sprintf("https://%s/redfish/v1/Managers/1/VirtualMedia/CD1/Actions/VirtualMedia.InsertMedia", CurrentHost.BMCAddress)
27+
body := []byte(fmt.Sprintf(`{"Image": "%s"}`, CurrentHost.ISOImage))
28+
MakeRequest("POST", url, body)
29+
}
30+
31+
func ServerVirtualMediaEject() {
32+
url := fmt.Sprintf("https://%s/redfish/v1/Managers/1/VirtualMedia/CD1/Actions/VirtualMedia.EjectMedia", CurrentHost.BMCAddress)
33+
body := []byte(`{}`)
34+
MakeRequest("POST", url, body)
35+
}
36+
37+
func ServerVirtualMediaStatus() {
38+
url := fmt.Sprintf("https://%s/redfish/v1/Managers/1/VirtualMedia/1", CurrentHost.BMCAddress)
39+
MakeRequest("GET", url, nil)
40+
}
41+
42+
func ServerSetBootOnceCD() {
43+
url := fmt.Sprintf("https://%s%s", CurrentHost.BMCAddress, RedfishPath)
44+
body := []byte(`{"Boot":{ "BootSourceOverrideEnabled": "Once", "BootSourceOverrideTarget": "Cd", "BootSourceOverrideMode": "UEFI"}}`)
45+
MakeRequest("PATCH", url, body)
46+
}
47+
48+
func ServerSetBootOncePXE() {
49+
url := fmt.Sprintf("https://%s%s", CurrentHost.BMCAddress, RedfishPath)
50+
body := []byte(`{"Boot":{ "BootSourceOverrideEnabled": "Once", "BootSourceOverrideTarget": "Pxe", "BootSourceOverrideMode": "UEFI"}}`)
51+
MakeRequest("PATCH", url, body)
52+
}

0 commit comments

Comments
 (0)