488 lines
14 KiB
Go
488 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/joho/godotenv"
|
|
|
|
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
|
|
|
|
"github.com/UpCloudLtd/upcloud-go-api/upcloud/client"
|
|
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
|
|
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
|
|
)
|
|
|
|
func defaultSSHAccess(host string) SSHAccess {
|
|
sshaccess := SSHAccess{Host: host}
|
|
var err error
|
|
sshaccess.Port, err = strconv.Atoi(os.Getenv("SSH_PORT"))
|
|
if err != nil {
|
|
sshaccess.Port = 22
|
|
}
|
|
sshaccess.User = os.Getenv("SSH_USER")
|
|
if len(sshaccess.User) == 0 {
|
|
sshaccess.User = DFLT_SSH_USER
|
|
}
|
|
sshaccess.Password = ""
|
|
sshaccess.KeyPath = ""
|
|
return sshaccess
|
|
}
|
|
func cmdexec(command string, args []string) (string, error) {
|
|
cmd := &exec.Cmd {
|
|
Path: command,
|
|
Args: args,
|
|
// Stdout: os.Stdout,
|
|
// Stderr: os.Stdout,
|
|
}
|
|
if out, err := cmd.Output(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "%s error: %#v\n",command,err)
|
|
return "",err
|
|
} else {
|
|
return string(out), nil
|
|
}
|
|
// cmd.Start();
|
|
// cmd.Wait()
|
|
}
|
|
func run() int {
|
|
runFlags, err := ParseFlags()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
username := os.Getenv("UPCLOUD_USERNAME")
|
|
password := os.Getenv("UPCLOUD_PASSWORD")
|
|
k := os.Getenv("KUPCLAPI")
|
|
if len(username) == 0 && len(k) == 0 {
|
|
err = godotenv.Load("/etc/upcloud/.env")
|
|
if err != nil {
|
|
err = godotenv.Load("/usr/local/etc/upcloud/.env")
|
|
}
|
|
}
|
|
if runFlags.encdr == TECODER.cmd || runFlags.encdr == TECODER.abbrv {
|
|
k := os.Getenv("KUPCLAPI")
|
|
if len(k) > 0 {
|
|
kdata := strings.Split(k," ")
|
|
out, err := exec.Command(TECODER.cmd,TECODER.decoder,kdata[0]).Output()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Auth info: Username not found in ecoded: %#v\n ",err)
|
|
return 1
|
|
}
|
|
username = strings.TrimSuffix(string(out),"\n")
|
|
out, err = exec.Command(TECODER.cmd,TECODER.decoder,kdata[1]).Output()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Auth info: Password not found in ecoded: %#v\n",err)
|
|
return 1
|
|
}
|
|
password = strings.TrimSuffix(string(out),"\n")
|
|
}
|
|
} else {
|
|
username = os.Getenv("UPCLOUD_USERNAME")
|
|
password = os.Getenv("UPCLOUD_PASSWORD")
|
|
}
|
|
if len(username) == 0 {
|
|
var upcloud_env map[string]string
|
|
upcloud_env, err = godotenv.Read(os.Getenv("HOME")+ "/.config/upctl.yaml")
|
|
if len(upcloud_env["username"]) != 0 {
|
|
username = upcloud_env["username"]
|
|
password = upcloud_env["password"]
|
|
}
|
|
}
|
|
if len(username) == 0 {
|
|
fmt.Fprintln(os.Stderr, "Auth info: Username must be specified")
|
|
return 1
|
|
}
|
|
if len(password) == 0 {
|
|
fmt.Fprintln(os.Stderr, "Auth info: Password must be specified")
|
|
return 2
|
|
}
|
|
|
|
c := client.New(username, password)
|
|
s := service.New(c)
|
|
|
|
switch runFlags.command {
|
|
case "infoserver":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"info",runFlags,datacfg)
|
|
case "inventory":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"inventory",runFlags,datacfg)
|
|
case "pubhosts":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"pubhosts",runFlags,datacfg)
|
|
case "prvhosts":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"prvhosts",runFlags,datacfg)
|
|
case "deleteserver":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"delete",runFlags,datacfg)
|
|
case "startserver":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"start",runFlags,datacfg)
|
|
case "restartserver":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"restart",runFlags,datacfg)
|
|
case "stopserver":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"stop",runFlags,datacfg)
|
|
case "createserver":
|
|
datacfg,err := loadDataConfig(runFlags)
|
|
if err != nil {
|
|
return 99
|
|
}
|
|
sshkeys := loadLoginUserKeys(runFlags)
|
|
if err := createServer(s,datacfg,sshkeys); err != nil {
|
|
return 3
|
|
}
|
|
case "modifyserver":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
if runFlags.target == "" {
|
|
fmt.Fprintln(os.Stderr, "Unable to run modifyserver command: ", runFlags.command)
|
|
return 99
|
|
}
|
|
onServers(s,"modifyserver",runFlags,datacfg)
|
|
case "fixstorage":
|
|
datacfg,err := loadDataConfig(runFlags)
|
|
if err != nil {
|
|
return 99
|
|
}
|
|
if runFlags.target == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"fixstorage",runFlags,datacfg)
|
|
case "modifystorage":
|
|
storecfg,err := loadStoreConfig(runFlags.dataCfgPath)
|
|
if err != nil {
|
|
return 99
|
|
}
|
|
if err := modifyStorage(s,storecfg); err != nil {
|
|
return 3
|
|
}
|
|
case "deletestorage":
|
|
hasID(runFlags.id)
|
|
if err := deleteStorage(s, runFlags.id); err != nil {
|
|
return 2
|
|
}
|
|
case "liststorages":
|
|
datacfg,_ := loadDataConfig(runFlags)
|
|
if err := listStorages(s, datacfg, runFlags.target); err != nil {
|
|
return 2
|
|
}
|
|
case "createstorageimage":
|
|
hasID(runFlags.id)
|
|
if runFlags.target == "" {
|
|
fmt.Fprintln(os.Stderr, "Unable to run createStorageImage no title: ", runFlags.target)
|
|
return 99
|
|
}
|
|
if err := createStorageImage(s, runFlags.id,runFlags.target); err != nil {
|
|
return 2
|
|
}
|
|
case "runssh":
|
|
if len(runFlags.runCmd) == 0 {
|
|
fmt.Fprintln(os.Stderr, "Unable to run ssh command: ", runFlags.command)
|
|
return 99
|
|
}
|
|
if len(runFlags.id) == 0 {
|
|
datacfg,err := loadDataConfig(runFlags)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Unable to run ssh command: ", runFlags.command)
|
|
return 99
|
|
}
|
|
onServers(s,"runssh",runFlags,datacfg)
|
|
} else {
|
|
sshAccess := defaultSSHAccess(runFlags.id)
|
|
output, err := runSSH(runFlags, sshAccess, runFlags.runCmd)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println(string(output))
|
|
}
|
|
case "runcmd":
|
|
if len(runFlags.runCmd) == 0 {
|
|
fmt.Fprintln(os.Stderr, "Unable to run command: ", runFlags.command)
|
|
return 99
|
|
}
|
|
datacfg,err := loadDataConfig(runFlags)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Unable to run command: ", runFlags.command)
|
|
return 99
|
|
}
|
|
onServers(s,"runcmd",runFlags,datacfg)
|
|
case "infofloatip":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
ipinfo, err := getFloatIP(s,runFlags.id,datacfg)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "error getting info %s: %#v\n", datacfg.FloatIP, err)
|
|
return 99
|
|
}
|
|
if runFlags.out == "attached" {
|
|
ipData := strings.Split(ipinfo.PTRRecord,".")
|
|
ip := strings.Join(ipData[0:4],".")
|
|
fmt.Fprintf(os.Stdout, "%s\n",ip)
|
|
} else {
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.Encode(ipinfo)
|
|
}
|
|
// fmt.Fprintf(os.Stderr, "%s Located in %s %s Server: %s) \n", datacfg.FloatIP, ipinfo.PTRRecord, ipinfo.MAC, ipinfo.ServerUUID)
|
|
case "movefloatip":
|
|
datacfg := &DataConfig{}
|
|
id := runFlags.id
|
|
var data []string
|
|
if id == "" {
|
|
id = os.Getenv("UPCLOUD_MOVEFLOATIP")
|
|
}
|
|
if id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
if datacfg.FloatIP == "" {
|
|
fmt.Fprintln(os.Stderr, "Float IP not found in: ", runFlags.dataCfgPath)
|
|
return 99
|
|
}
|
|
} else {
|
|
data = strings.Split(id, SEPARATOR)
|
|
}
|
|
moveFloatIP(s,data,datacfg)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Unable to move Float IP: ", datacfg.FloatIP)
|
|
return 99
|
|
}
|
|
case "modifyip":
|
|
datacfg,err := loadDataConfig(runFlags)
|
|
if err != nil {
|
|
return 99
|
|
}
|
|
onServers(s,"modifyip",runFlags,datacfg)
|
|
case "infotags":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"infotags",runFlags,datacfg)
|
|
case "addtags":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"addtags",runFlags,datacfg)
|
|
case "deletetags":
|
|
datacfg := &DataConfig{}
|
|
if runFlags.id == "" {
|
|
datacfg,err = loadDataConfig(runFlags)
|
|
}
|
|
onServers(s,"deletetags",runFlags,datacfg)
|
|
default:
|
|
fmt.Fprintln(os.Stderr, "Unknown command: ", runFlags.command)
|
|
return 99
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func onServers(s *service.Service, tsksrvc string, runFlags RunFlags, datacfg *DataConfig) error {
|
|
uuid := runFlags.id
|
|
runCommand := runFlags.runCmd
|
|
servers, err := s.GetServers()
|
|
if err != nil || len(servers.Servers) == 0 {
|
|
fmt.Fprintf(os.Stderr, "Unable to get servers: %#v\n", err)
|
|
return err
|
|
}
|
|
inventory := map[string][]string{}
|
|
sshAccess := SSHAccess{}
|
|
var target ServerConfig
|
|
for _, server := range servers.Servers {
|
|
target_srvr := ""
|
|
role_srvr := ""
|
|
if uuid != "" {
|
|
if server.UUID == uuid || server.Hostname == uuid {
|
|
target_srvr = uuid
|
|
for _,srv := range datacfg.Servers {
|
|
if srv.Hostname == server.Hostname {
|
|
target = srv
|
|
role_srvr = srv.Cluster.Role
|
|
sshAccess = srv.SSHAccess
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if datacfg.HostPrefix != "" && !strings.Contains(server.Hostname,datacfg.HostPrefix) {
|
|
continue
|
|
}
|
|
for _,srv := range datacfg.Servers {
|
|
if srv.Hostname == server.Hostname || srv.UUID == server.UUID {
|
|
target_srvr = server.UUID
|
|
target = srv
|
|
role_srvr = srv.Cluster.Role
|
|
sshAccess = srv.SSHAccess
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if target_srvr == "" {
|
|
continue
|
|
}
|
|
switch tsksrvc {
|
|
case "delete":
|
|
err := deleteOneServer(s, server, runFlags.target)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to delete server %#v: %#v\n", server.UUID, err)
|
|
return err
|
|
}
|
|
case "stop":
|
|
err := stopOneServer(s, server)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to stop server %#v: %#v\n", server.UUID, err)
|
|
}
|
|
case "start":
|
|
err := startOneServer(s, server)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to start server %#v: %#v\n", server.UUID, err)
|
|
}
|
|
case "restart":
|
|
err := restarOneServer(s, server)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to restart server %#v: %#v\n", server.UUID, err)
|
|
}
|
|
case "info":
|
|
dataPrices,err_prices := getDataPrices()
|
|
if err_prices != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to load prices for server %#v: %#v\n", server.UUID, err_prices)
|
|
}
|
|
err := detailsOneServer(s, server, *dataPrices)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to get details for server %#v: %#v\n", server.UUID, err)
|
|
}
|
|
case "inventory":
|
|
info, err := inventoryOneServer(s, datacfg.FloatIP, server)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to make inventory from server %#v: %#v\n", server.UUID, err)
|
|
} else {
|
|
inventory[role_srvr] = append(inventory[role_srvr],info)
|
|
}
|
|
case "pubhosts":
|
|
info, err := hostsOneServer(s,"pub", datacfg.FloatIP, server, target)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to make hosts list from server %#v: %#v\n", server.UUID, err)
|
|
} else {
|
|
fmt.Printf("%s",info)
|
|
}
|
|
case "prvhosts":
|
|
info, err := hostsOneServer(s,"prv", datacfg.FloatIP, server, target)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to make hosts list from server %#v: %#v\n", server.UUID, err)
|
|
} else {
|
|
fmt.Printf("%s",info)
|
|
}
|
|
case "modifyip":
|
|
nettype := upcloud.NetworkTypePrivate
|
|
err := modifyIPOneServer(s, server, nettype, target)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to modify IP server %#v: %#v\n", server.UUID, err)
|
|
}
|
|
case "infotags":
|
|
tags, err := infoTagsFromId(s, server.UUID)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to get tags from server %#v: %#v\n", server.UUID, err)
|
|
} else {
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.Encode(tags)
|
|
}
|
|
case "addtags":
|
|
if uuid != "" {
|
|
fmt.Fprintf(os.Stderr, "No tags found, for server %#v: %#v\n", server.UUID, err)
|
|
} else {
|
|
err := addTagsToId(s, server.UUID, target.Tags)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to add tags to server %#v: %#v\n", server.UUID, err)
|
|
}
|
|
}
|
|
case "deletetags":
|
|
if uuid != "" {
|
|
fmt.Fprintf(os.Stderr, "No tags found, for server %#v: %#v\n", server.UUID, err)
|
|
} else {
|
|
err := deleteTagsFromId(s, server.UUID, target.Tags)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to delete tags from server %#v: %#v\n", server.UUID, err)
|
|
}
|
|
}
|
|
case "fixstorage":
|
|
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
|
|
UUID: server.UUID,
|
|
})
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to get details from server %#v: %#v\n", server.UUID, err)
|
|
} else {
|
|
fixStorageDevices(s, target, info, runFlags.target)
|
|
}
|
|
case "modifyserver":
|
|
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
|
|
UUID: server.UUID,
|
|
})
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to get details from server %#v: %#v\n", server.UUID, err)
|
|
} else {
|
|
modifyServer(s, target, info, runFlags.target)
|
|
}
|
|
case "runssh":
|
|
if sshAccess.Host != "" {
|
|
output, err := runSSH(runFlags, sshAccess, runCommand)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println(string(output))
|
|
} else {
|
|
fmt.Fprintf(os.Stderr, "No Host to run ssh on %s \n", server.UUID)
|
|
}
|
|
case "runcmd":
|
|
args := strings.Split(runCommand, " ")
|
|
output, err := cmdexec(args[0],args[1:])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
fmt.Fprintf(os.Stderr, "No Host to run on %s \n", server.UUID)
|
|
}
|
|
fmt.Println(string(output))
|
|
}
|
|
}
|
|
if tsksrvc == "inventory" {
|
|
for key,items := range inventory {
|
|
fmt.Printf("[%s]\n",key)
|
|
for _,item := range items {
|
|
fmt.Printf("%s\n",item)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|