chore: adding code

This commit is contained in:
Jesús Pérez Lorenzo 2021-08-28 14:45:05 +01:00
parent 1378861295
commit 0a2d1bd711
16 changed files with 2603 additions and 0 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Jesús Pérez Lorenzo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

33
Makefile Normal file
View File

@ -0,0 +1,33 @@
.PHONY: all
# all: test build run
all: build run
.PHONY: run
run:
@go run ./...
.PHONY: build
build:
@go build -o ./build/upclapi ./...
@echo "[OK] upclapi was build in ./build/upclapi !"
.PHONY: install
install:
@go install
@echo "[OK] upclapi was installed in $$GOPATH/bin/upclapi"
# .PHONY: test
# test:
# @go test -v -coverprofile=cover.out ./...
# @echo "[OK] Test and coverage file was created!"
.PHONY: show_coverage
show_coverage:
@go tool cover -html=cover.out
@echo "[OK] Coverage file opened!"

4
bin/get_hosts_pub_ips.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
[ -z "$1" ] && echo "source-cloud-home-name not found" && exit 1
. ./.env
../klouds/bin/hosts_list.sh $1 -src ../klouds/home -filter pub

23
bin/make_kenv.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
# tecoder --fencrypt _datacfg.yaml > kdata.yaml
#
ENCODER="/usr/local/bin/tecoder"
ARGSENCODER="-e"
SOURCE_ENV=".env"
TARGET_ENV=".envk"
[ ! -r "$SOURCE_ENV" ] && echo "File $SOURCE_ENV not found" && exit 1
USER=$(grep "USER" $SOURCE_ENV | cut -f2 -d"=")
[ -z "$USER" ] && echo "User is empty" && exit 1
PASSWRD=$(grep "PASSWORD" $SOURCE_ENV | cut -f2 -d"=")
[ -z "$PASSWRD" ] && echo "Password is empty" && exit 1
kuser=$($ENCODER $ARGSENCODER $USER)
[ -z "$kuser" ] && echo "Unable to encode User" && exit 1
kpasswdr=$($ENCODER $ARGSENCODER $PASSWRD)
[ -z "$kpasswdr" ] && echo "Unable to encode Password" && exit 1
echo "export KUPCLAPI=\"$kuser $kpasswdr\"" > $TARGET_ENV

22
bin/on_cloud.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
UPCLAPI_CMD="./build/upclapi"
USAGE="on_cloud.sh home-cloud-dir-name tsksrvc [ args ]
example: on_cloud.sh wuji/dvara info
"
[ -z "$1" ] || [ "$1" == "-h" ] && echo "$USAGE" && $UPCLAPI_CMD -h && exit
. ./env
CLOUD_CONFIG_PATH="$KLDS_HOME/$1/$KLDS_CONFIG"
[ ! -r "$CLOUD_CONFIG_PATH" ] && echo "$CLOUD_CONFIG_PATH not found" && exit
[ -z "$2" ] && echo "No tsksrvc defined" && echo "$USAGE" && exit 1
case "$2" in
c|create|createserver) TSKSRVC="createserver" ;;
d|delete|deleteserver) TSKSRVC="deleteserver" ;;
i|info) TSKSRVC="infoserver" ;;
*) TSKSRVC="$2"
esac
if $UPCLAPI_CMD -c "$TSKSRVC" -f "$CLOUD_CONFIG_PATH" ; then
[ "$TSKSRVC" == "createserver" ] && "$ROOT_KLDS"/bin/hosts_list.sh "$1" -src "$KLDS_HOME" -filter pub
fi

54
bin/update_storage.sh Executable file
View File

@ -0,0 +1,54 @@
#!/bin/bash
USAGE="update_storage.sh [-d] server-name new-size"
[ "$1" == "-h" ] && echo "$USAGE" && exit
[ "$1" == "-d" ] && DEBUG=$1 && shift
[ -z "$1" ] && echo "No server found" && echo "$USAGE" && exit 1
SERVER_ID=$1
[ -z "$2" ] && echo "No new size found" && echo "$USAGE" && exit 1
NEW_SIZE=$2
STOPPED="stopped"
STARTED="started"
MAX_TRIES=5
SLEEP_TIME=10
_server_state() {
[ -z "$1" ] && return
upctl server show "$1" | grep State | awk '{print $2}'
}
_is_server_started() {
[ -z "$1" ] && return
_server_state "$1" | grep started
}
_stop_server() {
[ -z "$1" ] && return
[[ "$(_server_state "$1")" =~ $STOPPED ]] && return
#[[ ! "$(_server_state "$1")" =~ $STOPPED ]] &&
upctl server stop "$1" 2>/dev/null
local n=0
echo -n "Stopping $1 "
while [[ ! "$(_server_state "$1")" =~ $STOPPED ]]
do
n=$((n+1))
echo -n ". "
sleep $SLEEP_TIME
[ "$n" -gt $MAX_TRIES ] && echo "Server $1 not $STOPPED" && exit 2
done
echo " $(_server_state "$1")"
}
STORE_UUID=$(upctl server show "$SERVER_ID" -o json | jq -r '.storage[].uuid ' | sed 's/"//')
[ -z "$STORE_UUID" ] && echo "No store found for $SERVER_ID" && exit 1
STORE_SIZE=$(upctl server show "$SERVER_ID" -o json | jq '.storage[].size ')
echo "$SERVER_ID: store $STORE_UUID from $STORE_SIZE to $NEW_SIZE"
[ "$STORE_SIZE" -gt "$NEW_SIZE" ] && echo "$NEW_SIZE is less then current $STORE_SIZE" && exit 1
_stop_server "$SERVER_ID"
[ -n "$DEBUG" ] && _server_state "$SERVER_ID"
if upctl storage modify "$STORE_UUID" --size "$NEW_SIZE" ; then
[ -n "$DEBUG" ] && _server_state "$SERVER_ID"
[[ ! "$(_server_state "$1")" =~ $STARTED ]] && upctl server start "$SERVER_ID"
else
echo "errors in modify $SERVER_ID $STORE_UUID"
fi

126
floatip.go Normal file
View File

@ -0,0 +1,126 @@
package main
import (
"fmt"
"os"
"strings"
// "encoding/json"
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
)
func getFloatIP(s *service.Service, floatIP string, datacfg *DataConfig) (*upcloud.IPAddress,error) {
ip := floatIP
if ip == "" {
ip = datacfg.FloatIP
}
ipinfo, err := s.GetIPAddressDetails(&request.GetIPAddressDetailsRequest{
Address: ip,
})
if err != nil {
fmt.Fprintf(os.Stderr, "error getting info %s: %#v\n", floatIP, err)
return nil,err
}
return ipinfo,nil
}
func moveFloatIP(s *service.Service, data []string, datacfg *DataConfig) error {
// err := s.ReleaseIPAddress(&request.ReleaseIPAddressRequest{
// IPAddress: uuid,
// })
// fmt.Fprintf(os.Stderr, "details %#v: %#v\n", uuid, err)
targetFloatIP := ""
targetHost := ""
if len(data) > 0 {
targetFloatIP = data[0]
targetHost = data[1]
} else {
targetFloatIP = datacfg.FloatIP
}
servers, err := s.GetServers()
if err != nil || len(servers.Servers) == 0 {
fmt.Fprintf(os.Stderr, "Unable to get servers: %#v\n", err)
return err
}
ipinfo, err := getFloatIP(s,targetFloatIP,datacfg)
if err != nil {
fmt.Fprintf(os.Stderr, "error getting info %s: %#v\n", targetFloatIP, err)
return err
}
// fmt.Fprintf(os.Stderr, "ipinfo: %#v\n", ipinfo)
// fmt.Printf("Retrieved %d servers\n", len(servers.Servers))
// fmt.Println("Deleting all servers")
currentFloatIP := ""
for _, server := range servers.Servers {
if datacfg.HostPrefix != "" && ! strings.Contains(server.Hostname,datacfg.HostPrefix) {
continue
}
if ipinfo.ServerUUID == server.UUID {
fmt.Fprintf(os.Stderr, "%s Located in %s %s:%s Server: %s) \n", targetFloatIP, ipinfo.PTRRecord, server.Hostname, ipinfo.MAC, ipinfo.ServerUUID)
currentFloatIP = server.Hostname
break
}
}
if currentFloatIP == "" {
fmt.Fprintf(os.Stderr, "%s Not in use in %s \n", targetFloatIP, ipinfo.PTRRecord)
}
for _, server := range servers.Servers {
if (datacfg.HostPrefix != "" && ! strings.Contains(server.Hostname,datacfg.HostPrefix)) || ipinfo.ServerUUID == server.UUID {
continue
}
var target_srv ServerConfig
if targetHost == "" {
for _,srv := range datacfg.Servers {
if server.Hostname == srv.Hostname && srv.UseFloatIP {
target_srv = srv
break
}
}
} else {
if server.Hostname == targetHost {
target_srv.Hostname = targetHost
}
}
if target_srv.Hostname == "" {
continue
}
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
UUID: server.UUID,
})
if err != nil {
continue
}
target_IP := ""
target_MAC := ""
for i := 0; i < len(info.Networking.Interfaces); i++ {
if info.Networking.Interfaces[i].Type == upcloud.NetworkTypePublic {
for _,ip := range info.Networking.Interfaces[i].IPAddresses {
if ip.Address != targetFloatIP {
target_IP = ip.Address
break
}
}
target_MAC=info.Networking.Interfaces[i].MAC
break
}
}
if target_IP != "" && target_MAC != "" {
ptr := fmt.Sprintf("%s.%s.upcloud.host", target_IP, info.Zone)
newIP,err := s.ModifyIPAddress(&request.ModifyIPAddressRequest{
IPAddress: targetFloatIP,
PTRRecord: ptr,
MAC: target_MAC,
})
if err != nil {
fmt.Fprintf(os.Stderr, "error %s: %#v\n", targetFloatIP, err)
} else {
fmt.Fprintf(os.Stderr, "%s Moved -> %s %s:%s Server: %s \n", newIP.Address, newIP.PTRRecord, target_srv.Hostname, newIP.MAC, server.UUID)
}
break
}
}
return err
}

131
inventory.go Normal file
View File

@ -0,0 +1,131 @@
package main
import (
"fmt"
// "log"
"os"
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
)
func inventoryOneServer(s *service.Service, floatIP string, server upcloud.Server) (string, error) {
ansible_data := "ansible_port=22 ansible_python_interpreter=/usr/bin/python3 ansible_user=root"
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)
return "", err
}
public_ip :=""
private_ip :=""
vpn_ip :=""
host :=""
float := upcloud.False
output := ""
for _, ip := range info.Networking.Interfaces {
for _, addr := range ip.IPAddresses {
if addr.Address == floatIP {
continue
}
switch ip.Type {
case upcloud.NetworkTypePublic:
public_ip = addr.Address
float = addr.Floating
case upcloud.NetworkTypeUtility:
private_ip = addr.Address
case upcloud.NetworkTypePrivate:
vpn_ip = addr.Address
}
}
}
if vpn_ip != "" {
host = vpn_ip
} else {
host = public_ip
}
floating := "no"
if float == upcloud.True {
floating = "yes"
}
output += fmt.Sprintf("%s ansible_host=%s hostname=%s vpn_ip=%s pub_ip=%s private_ip=%s float=%s ",
server.Hostname,
host,
server.Hostname,
vpn_ip,
public_ip,
private_ip,
floating)
output += fmt.Sprintf(" uuid=%s %s\n",info.UUID,ansible_data)
return output, nil
}
func hostsOneServer(s *service.Service, usetype string, floatIP string, server upcloud.Server, target ServerConfig) (string, error) {
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)
return "", err
}
public_ip :=""
private_ip :=""
sdn_ip :=""
vpn_ip :=""
vpn_net :=""
float := upcloud.False
output := ""
for i := 0; i < len(info.Networking.Interfaces); i++ {
for _,addr := range info.Networking.Interfaces[i].IPAddresses {
if addr.Address == floatIP {
continue
}
switch info.Networking.Interfaces[i].Type {
case upcloud.NetworkTypePublic:
public_ip = addr.Address
float = addr.Floating
case upcloud.NetworkTypeUtility:
private_ip = addr.Address
case upcloud.NetworkTypePrivate:
sdn_ip = addr.Address
}
}
}
for i := 0; i < len(target.Networks); i++ {
if target.Networks[i].Access == "vpn" {
vpn_ip = target.Networks[i].IPAddress
vpn_net = target.Networks[i].Network
}
}
floating := "no"
if float == upcloud.True {
floating = "yes"
}
suffix := ""
output += fmt.Sprintf("# %s \t %s float=%s | %s\n", server.Hostname, server.UUID, floating, server.Title)
if public_ip != "" {
if usetype == "prv" {
suffix = ".pub"
}
output += fmt.Sprintf("%s \t %s%s \t# PUB\n", public_ip, server.Hostname, suffix)
}
if sdn_ip != "" {
if usetype == "pub" {
suffix = ".prv"
} else {
suffix = ""
}
output += fmt.Sprintf("%s \t %s%s \t# SDN\n", sdn_ip,server.Hostname, suffix)
}
if private_ip != "" {
output += fmt.Sprintf("%s \t %s.pv \t# PV\n", private_ip, server.Hostname)
}
if vpn_ip != "" {
output += fmt.Sprintf("%s \t %s.vpn \t# VPN /%s\n", vpn_ip, server.Hostname, vpn_net)
}
return output,nil
}

552
main.go Normal file
View File

@ -0,0 +1,552 @@
package main
import (
"flag"
"fmt"
"path/filepath"
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
"encoding/json"
"log"
"math/rand"
"os"
"time"
"gopkg.in/yaml.v3"
// https://abhinavg.net/posts/flexible-yaml/
)
const SEPARATOR = "::"
const DATA_CFG = "datacfg.yaml"
const DFLT_COMMAND= "infoserver"
const SSH_KEY_PATH = ".sshkeys"
const MONTH_HOURS = 672
var username string
var password string
var TECODER = CoderConfig {
cmd: "tecoder",
abbrv: "tc",
decoder: "-d",
fdecoder: "--fdecrypt",
encoder: "-e",
fencoder: "--fencrypt",
}
type RunFlags struct {
command string
dataCfgPath string
id string
runCmd string
encdr string
out string
target string
}
type CoderConfig struct {
cmd string
abbrv string
decoder string
fdecoder string
encoder string
fencoder string
}
type StoreConfig struct {
Title string `yaml:"title"`
Uuid string `yaml:"uuid"`
Size int `yaml:"size"`
Servers []string `yaml:"servers"`
}
type DataStorageDevices struct {
Action string `yaml:"action"`
Title string `yaml:"title"`
Storage string `yaml:"storage"`
Size int `yaml:"size"`
FinalSize int `yaml:"finalSize"`
PartSizes string `yaml:"partSizes"`
MakeFs string `yaml:"makefs"`
Tier string `yaml:"tier"`
Source string `yaml:"source"`
// Type: string `yaml:"type"`
// Address string `json:"address,omitempty"`
}
type NetworksConfig struct {
Access string `yaml:"access"`
Family string `yaml:"family"`
Network string `yaml:"network"`
IPAddress string `yaml:"ipaddress"`
SourceIPFiltering string `yaml:"source_ip_filtering"`
// SourceIPFiltering upcloud.Boolean `yaml:"source_ip_filtering"`
}
type ClusterRole struct {
Role string `yaml:"role"`
}
type TskSrvc struct {
name string `yaml:"name"`
path string `yaml:"path"`
target string `yaml:"target"`
req string `yaml:"req"`
liveness string `yaml:"liveness"`
}
type App struct {
name string `yaml:"name"`
path string `yaml:"path"`
target string `yaml:"target"`
req string `yaml:"req"`
liveness string `yaml:"liveness"`
}
type SSHAccess struct {
Host string `yaml:"host"`
User string `yaml:"user"`
Password string `yaml:"password"`
UType string `yaml:"utype"`
KeyPath string `yaml:"keyPath"`
Port int `yaml:"port"`
}
type LoginUserConfig struct {
// CreatePassword string `yaml:"createPassword"`
// Username string `yaml:"username"`
SSHKeys []string `yaml:"sshKeys"`
}
type Cntrllrs struct {
host string `yaml:"host"`
sshaccess SSHAccess `yaml:"sshaccess"`
cldPath string `yaml:"cldPath"`
masterPath string `yaml:"masterPath"`
}
// https://abhinavg.net/posts/flexible-yaml/
type ServerConfig struct {
Hostname string `yaml:"hostname"`
Title string `yaml:"title"`
UserData string `yaml:"userData"`
Zone string `yaml:"zone"`
UUID string `yaml:"uuid"`
Host string `yaml:"host"`
Tags []string `yaml:"tags"`
Cluster ClusterRole `yaml:"cluster"`
SSHAccess SSHAccess `yaml:"sshAccess"`
LoginUser LoginUserConfig `yaml:"loginUser"`
Plan string `yaml:"plan"`
Metadata upcloud.Boolean `yaml:"metadata"`
TimeZone string `yaml:"timeZone"`
Networks []NetworksConfig `yaml:"networks"`
StorageDevices []DataStorageDevices `yaml:"storages"`
UseFloatIP bool `yaml:"useFloatIP"`
TskSrvcs []TskSrvc `yaml:"tsksrvcs"`
Apps []App `yaml:"apps"`
Clients []string `yaml:"clients"`
Rules []string `yaml:"rules"`
Backup []string `yaml:"backup"`
}
type DataConfig struct {
Servers []ServerConfig `yaml:"servers"`
FloatIP string `yaml:"floatIP"`
Group string `yaml:"group"`
GroupPath string `yaml:"group_path"`
HostPrefix string `yaml:"hostPrefix"`
MainName string `yaml:"main"`
domainName string `yaml:"domainName"`
cntrllrs []Cntrllrs `yaml:"cntrllrs"`
}
type InfoServer struct {
info upcloud.ServerDetails `json:"ServerDetails"`
price Price `json:"Price"`
// price upcloud.Price `json:"Price"`
}
/*
type CloudGroup struct {
name string `yaml:"name"`
path string `yaml:"path"`
info string `yaml:"info"`
resources ConfigResources `yaml:"resources"`
}
type KloudHome struct {
name string `yaml:"name"`
title string `yaml:"title"`
dflts string `yaml:"dflts"`
provider []ProviderName `yaml:"provider"`
netips []IpDef `yaml:"netips"`
cntrllrs []Cntrllrs `yaml:"cntrllrs"`
groups []CloudGroup `yaml:"groups"`
}
*/
// type DDataConfig struct {
// // Servers []ServerConfig `yaml:"servers"`
// floatIP string `yaml:"floatIP"`
// hostPrefix string `yaml:"hostPrefix"`
// mainName string `yaml:"main"`
// }
// Price represents a price
type Price struct {
Amount int `yam:"amount"`
Price float64 `yaml:"price"`
Hour float64 `yaml:"price_hour"`
Month float64 `yaml:"price_month"`
}
type DataPrices struct {
Prices PricesZones `yaml:"prices"`
}
type PricesZones struct {
Zone []PricesZone `yaml:"zone"`
}
type PricesZone struct {
Name string `yaml:"name"`
Firewall Price `yaml:"firewall"`
IORequestBackup Price `yaml:"io_request_backup"`
IORequestHDD Price `yaml:"io_request_hdd"`
IORequestMaxIOPS Price `yaml:"io_request_maxiops"`
IPv4Address Price `yaml:"ipv4_address"`
IPv6Address Price `yaml:"ipv6_address"`
ManagedDatabase1x1CPU2GB25GB Price `yaml:"managed_database_1x1xCPU-2GB-25GB"`
ManagedDatabase1x2CPU4GB100GB Price `yaml:"managed_database_1x2xCPU-4GB-100GB"`
ManagedDatabase1x2CPU4GB50GB Price `yaml:"managed_database_1x2xCPU-4GB-50GB"`
ManagedDatabase2x2CPU4GB100GB Price `yaml:"managed_database_2x2xCPU-4GB-100GB"`
ManagedDatabase2x2CPU4GB50GB Price `yaml:"managed_database_2x2xCPU-4GB-50GB"`
ManagedDatabase2x4CPU8GB100GB Price `yaml:"managed_database_2x4xCPU-8GB-100GB"`
ManagedDatabase2x4CPU8GB50GB Price `yaml:"managed_database_2x4xCPU-8GB-50GB"`
ManagedDatabase2x6CPU16GB100GB Price `yaml:"managed_database_2x6xCPU-16GB-100GB"`
ManagedDatabase2x6CPU16GB50GB Price `yaml:"managed_database_2x6xCPU-16GB-250GB"`
ManagedDatabase2x8CPU32GB100GB Price `yaml:"managed_database_2x8xCPU-32GB-100GB"`
ManagedDatabase2x8CPU22GB250GB Price `yaml:"managed_database_2x8xCPU-32GB-250GB"`
ManagedDatabase3x2CPU4GB100GB Price `yaml:"managed_database_3x2xCPU-4GB-100GB"`
ManagedDatabase3x2CPU4GB200GB Price `yaml:"managed_database_3x2xCPU-4GB-200GB"`
ManagedDatabase3x4CPU8GB100GB Price `yaml:"managed_database_3x4xCPU-8GB-100GB"`
ManagedDatabase3x4CPU8GB200GB Price `yaml:"managed_database_3x4xCPU-8GB-200GB"`
ManagedDatabase3x6CPU16GB200GB Price `yaml:"managed_database_3x6xCPU-16GB-200GB"`
ManagedDatabase3x6CPU16GB500GB Price `yaml:"managed_database_3x6xCPU-16GB-500GB"`
ManagedDatabase3x8CPU32GB200GB Price `yaml:"managed_database_3x8xCPU-32GB-200GB"`
ManagedDatabase3x8CPU32GB500GB Price `yaml:"managed_database_3x8xCPU-32GB-500GB"`
NetworkPrivateVLAN Price `yaml:"network_private_vlan"`
ObjectStorage1TB Price `yaml:"object_storage_1TB"`
ObjectStorage250GB Price `yaml:"object_storage_250GB"`
ObjectStorage500GB Price `yaml:"object_storage_500GB"`
PublicIPv4BandwidthIn Price `yaml:"public_ipv4_bandwidth_in"`
PublicIPv4BandwidthOut Price `yaml:"public_ipv4_bandwidth_out"`
PublicIPv6BandwidthIn Price `yaml:"public_ipv6_bandwidth_in"`
PublicIPv6BandwidthOut Price `yaml:"public_ipv6_bandwidth_out"`
ServerCore Price `yaml:"server_core"`
ServerMemory Price `yaml:"server_memory"`
ServerPlan6xCPU8GB Price `yaml:"server_plan_6xCPU-8GB"`
ServerPlan12xCPU48GB Price `yaml:"server_plan_12xCPU-48GB"`
ServerPlan16xCPU64GB Price `yaml:"server_plan_16xCPU-64GB"`
ServerPlan1xCPU1GB Price `yaml:"server_plan_1xCPU-1GB"`
ServerPlan1xCPU2GB Price `yaml:"server_plan_1xCPU-2GB"`
ServerPlan20xCPU128GB Price `yaml:"server_plan_20xCPU-128GB"`
ServerPlan20xCPU96GB Price `yaml:"server_plan_20xCPU-96GB"`
ServerPlan2xCPU4GB Price `yaml:"server_plan_2xCPU-4GB"`
ServerPlan4xCPU8GB Price `yaml:"server_plan_4xCPU-8GB"`
ServerPlan6xCPU16GB Price `yaml:"server_plan_6xCPU-16GB"`
ServerPlan8xCPU32GB Price `yaml:"server_plan_8xCPU-32GB"`
SimpleBackupDailies12xCPU48GB Price `yaml:"simple_backup_dailies_12xCPU-48GB"`
SimpleBackupDailies16xCPU64GB Price `yaml:"simple_backup_dailies_16xCPU-64GB"`
SimpleBackupDailiesx1CPU1GB Price `yaml:"simple_backup_dailies_1xCPU-1GB"`
SimpleBackupDailies1xCPU2GB Price `yaml:"simple_backup_dailies_1xCPU-2GB"`
SimpleBackupDailies20xCPU128GB Price `yaml:"simple_backup_dailies_20xCPU-128GB"`
SimpleBackupDailies20xCPU96GB Price `yaml:"simple_backup_dailies_20xCPU-96GB"`
SimpleBackupDailies2xCPU4GB Price `yaml:"simple_backup_dailies_2xCPU-4GB"`
SimpleBackupDailies4xCPU8GB Price `yaml:"simple_backup_dailies_4xCPU-8GB"`
SimpleBackupDailies6xCPU16GB Price `yaml:"simple_backup_dailies_6xCPU-16GB"`
SimpleBackupDailies8xCPU32GB Price `yaml:"simple_backup_dailies_8xCPU-32GB"`
SimpleBackupExtraDailies Price `yaml:"simple_backup_extra_dailies"`
SimpleBackupExtraMontlies Price `yaml:"simple_backup_extra_monthlies"`
SimpleBackupExtraWeeklies Price `yaml:"simple_backup_extra_weeklies"`
SimpleBackupMonthlies12xCPU48GB Price `yaml:"simple_backup_monthlies_12xCPU-48GB"`
SimpleBackupMonthlies16xCPU64GB Price `yaml:"simple_backup_monthlies_16xCPU-64GB"`
SimpleBackupMonthlies1xCPU1GB Price `yaml:"simple_backup_monthlies_1xCPU-1GB"`
SimpleBackupMonthlies1xCPU2GB Price `yaml:"simple_backup_monthlies_1xCPU-2GB"`
SimpleBackupMonthlies20xCPU128GB Price `yaml:"simple_backup_monthlies_20xCPU-128GB"`
SimpleBackupMonthlies20xCPU96GB Price `yaml:"simple_backup_monthlies_20xCPU-96GB"`
SimpleBackupMonthlies2xCPU4GB Price `yaml:"simple_backup_monthlies_2xCPU-4GB"`
SimpleBackupMonthlies4xCPU8GB Price `yaml:"simple_backup_monthlies_4xCPU-8GB"`
SimpleBackupMonthlies6xCPU16GB Price `yaml:"simple_backup_monthlies_6xCPU-16GB"`
SimpleBackupMonthlies8xCPU32GB Price `yaml:"simple_backup_monthlies_8xCPU-32GB"`
SimpleBackupWeeklies12xCPU48GB Price `yaml:"simple_backup_weeklies_12xCPU-48GB"`
SimpleBackupWeeklies16xCPU64GB Price `yaml:"simple_backup_weeklies_16xCPU-64GB"`
SimpleBackupWeeklies1xCPU1GB Price `yaml:"simple_backup_weeklies_1xCPU-1GB"`
SimpleBackupWeeklies1xCPU2GB Price `yaml:"simple_backup_weeklies_1xCPU-2GB"`
SimpleBackupWeeklies20xCPU128GB Price `yaml:"simple_backup_weeklies_20xCPU-128GB"`
SimpleBackupWeeklies20xCPU96GB Price `yaml:"simple_backup_weeklies_20xCPU-96GB"`
SimpleBackupWeeklies2xCPU4GB Price `yaml:"simple_backup_weeklies_2xCPU-4GB"`
SimpleBackupWeeklies4xCPU8GB Price `yaml:"simple_backup_weeklies_4xCPU-8GB"`
SimpleBackupWeeklies6xCPU16GB Price `yaml:"simple_backup_weeklies_6xCPU-16GB"`
SimpleBackupWeeklies8xCPU32GB Price `yaml:"simple_backup_weeklies_8xCPU-32GB"`
StorageHDD Price `yaml:"storage_hdd"`
StorageBackup Price `yaml:"storage_backup"`
StorageMaxIOPS Price `yaml:"storage_maxiops"`
StorageTemplate Price `yaml:"storage_template"`
}
func (m *InfoServer) MarshalJSON() ([]byte, error) {
// meta := `"Id":` + strconv.Itoa(m.Meta.Id)
info, err_info := json.Marshal(m.info)
if err_info != nil {
return nil, err_info
}
price, err_price := json.Marshal(m.price)
if err_price != nil {
return nil, err_price
}
// fmt.Printf("price %s \n", price)
// Stitching it all together
// return []byte(`{` + meta + `,"Contents":` + string(cont) + `}`), nil
return []byte(`{"info":` + string(info) + `,"price":` + string(price) + `}`), nil
}
//func (s *InfoServer) UnmarshalJSON(b []byte) error {
// type serverWrapper struct {
// PriceZones []PriceZone `json:"zone"`
// }
// v := struct {
// PriceZones serverWrapper `json:"prices"`
// }{}
// err := json.Unmarshal(b, &v)
// if err != nil {
// return err
// }
// s.PriceZones = v.PriceZones.PriceZones
// return nil
// }
func getDataPrices() (*DataPrices, error) {
dataPrices := &DataPrices{}
if len(os.Getenv("UPCLOUD_PRICES")) == 0 {
return dataPrices,nil
}
file, err := os.Open(os.Getenv("UPCLOUD_PRICES"))
if err != nil {
return dataPrices,err
}
defer file.Close()
// Init new YAML decode
d := yaml.NewDecoder(file)
// Start YAML decoding from file
if err := d.Decode(&dataPrices); err != nil {
return dataPrices,err
}
// fmt.Printf("Loaded prices: %#v\n", dataPrices)
return dataPrices,nil
}
// NewDataConfig returns a new decoded DataConfig struct
func NewDataConfig(runFlags RunFlags) (*DataConfig, error) {
// Load config structure from YAML
dataConfig := &DataConfig{}
// fmt.Printf("Loaded config: %#v\n", runFlags.dataCfgPath)
/*
if runFlags.encdr == TECODER.cmd || runFlags.encdr == TECODER.abbrv {
// content, err := ioutil.ReadFile(runFlags.dataCfgPath)
// if err != nil {
// log.Fatal(err)
// return nil,err
// }
//out, err := exec.Command(CODER,ARGS_DECODER,string(content)).Output()
out, err := exec.Command(TECODER.cmd,TECODER.fdecoder,runFlags.dataCfgPath).Output()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to decode %s error: %#v\n ",runFlags.dataCfgPath,err)
return nil, err
}
err = yaml.Unmarshal(out,&dataConfig)
if err != nil {
return nil, err
}
} else {
/*
ddataConfig := &DDataConfig{}
bytes, err := ioutil.ReadFile(runFlags.dataCfgPath)
if err != nil {
panic(err)
}
err = dhall.Unmarshal(bytes, &ddataConfig)
if err != nil {
panic(err)
}
fmt.Printf("Loaded config: %#v\n", ddataConfig)
*/
// Open config file
file, err := os.Open(runFlags.dataCfgPath)
if err != nil {
return nil, err
}
defer file.Close()
// Init new YAML decode
d := yaml.NewDecoder(file)
// Start YAML decoding from file
if err := d.Decode(&dataConfig); err != nil {
return nil, err
}
// fmt.Printf("Loaded config: %#v\n", dataConfig)
// }
return dataConfig, nil
}
func getAuthKeysData(path string) string {
pathAuth := fmt.Sprintf("%s/%s", path,SSH_KEY_PATH)
// fmt.Fprintf(os.Stdout, "%#v\n",pathAuth)
if _, err := os.Stat(pathAuth); err == nil {
file, err := os.Open(pathAuth)
if err != nil {
return ""
}
defer file.Close()
}
return pathAuth
}
func loadLoginUserKeys(runFlags RunFlags) request.SSHKeySlice {
dirAuth := filepath.Dir(runFlags.dataCfgPath)
keysPath := getAuthKeysData(dirAuth)
for i := 0; i < 3; i++ {
if keysPath == "" {
break
}
dirAuth = filepath.Dir(dirAuth)
keysPath = getAuthKeysData(dirAuth)
}
if keysPath == "" {
return nil
}
file, err := os.Open(keysPath)
if err != nil {
return nil
}
defer file.Close()
// fmt.Fprintf(os.Stdout, "%#v\n",keysPath)
var sshKeys request.SSHKeySlice
d := yaml.NewDecoder(file)
if err := d.Decode(&sshKeys); err != nil {
fmt.Fprintf(os.Stdout, "%#v\n",err)
return nil
}
// fmt.Fprintf(os.Stdout, "%#v\n",sshKeys)
return sshKeys
}
func init() {
rand.Seed(time.Now().Unix())
}
func main() {
if len(os.Args) > 1 || len(os.Getenv("UPCLAPI_COMMAND")) > 0 {
os.Exit(run())
} else {
fmt.Fprintln(os.Stderr, "Use --help to see options")
}
}
func hasID(id string) bool {
if len(id) == 0 {
fmt.Fprintln(os.Stderr, "D must be specified")
os.Exit(2)
}
return true
}
func loadDataConfig(runFlags RunFlags) (*DataConfig, error) {
// datacfg := &DataConfig{}
datacfg, err := NewDataConfig(runFlags)
if err != nil {
log.Fatal(err)
return datacfg, err
}
return datacfg, nil
}
// ValidateDataPath just makes sure, that the path provided is a file,
// that can be read
func ValidateDataPath(path string) error {
s, err := os.Stat(path); os.IsNotExist(err)
if err != nil {
return fmt.Errorf("'%s' config file not found", path)
}
if s.IsDir() {
return fmt.Errorf("'%s' is a directory, not a normal file", path)
}
return nil
}
// ParseFlags will create and parse the CLI flags
// and return the path to be used elsewhere
func ParseFlags() (RunFlags,error) {
// target := flag.Arg(1)
// fmt.Printf("%+v ...\n",target) // Print with Variable Name
// String that contains the configured configuration path
runFlags := RunFlags {
command: os.Getenv("UPCLAPI_COMMAND"),
dataCfgPath: os.Getenv("UPCLAPI_DATACFG"),
id: os.Getenv("UPCLAPI_ID"),
runCmd: os.Getenv("UPCLAPI_RUNCMD"),
encdr: os.Getenv("UPCLAPI_ENCODER"),
out: os.Getenv("UPCLAPI_OUT"),
target: os.Getenv("UPCLAPI_TARGET"),
}
if runFlags.command == "" {
runFlags.command = DFLT_COMMAND
}
if runFlags.dataCfgPath == "" {
runFlags.dataCfgPath = DATA_CFG
}
// var command string
// var dataCfgPath string
// var id string
// var cmd string
// var encdr string
// Set up a CLI flag called "-config" to allow users
// to supply the configuration file
commandInfo := fmt.Sprintf("command to run [\n%s\n%s\n%s\n%s\n%s\n%s\n]",
"createserver, infoserver, restartserver, startserver, stopserver, modifyserver, deleteserver,",
"infofloatip, movefloatip, modifyip,",
"infotags, addtags, deletetags (use -t keep to keep storages)",
"liststorages, fixstorage (-t size | part0 | part1 | final), deletestorage, modifystorage, createstorageimage (-t title),",
"inventory, pubhosts, prvhosts",
"runssh, runcmd")
flag.StringVar(&runFlags.command, "c", runFlags.command, commandInfo)
flag.StringVar(&runFlags.dataCfgPath, "f", runFlags.dataCfgPath, "path to data file")
flag.StringVar(&runFlags.id, "id", runFlags.id, "resource name or uuid")
flag.StringVar(&runFlags.runCmd, "cmd", runFlags.runCmd, "run [ssh] command")
flag.StringVar(&runFlags.encdr, "kdr", runFlags.encdr, "use coder ")
flag.StringVar(&runFlags.out, "o", runFlags.out, "output format ")
flag.StringVar(&runFlags.target, "t", runFlags.out, "target item for command ")
// Actually parse the flags
flag.Parse()
if len(runFlags.id) == 0 {
if _, err := os.Stat(runFlags.dataCfgPath); os.IsNotExist(err) {
msg := fmt.Errorf("'%s' config file not found", DATA_CFG)
if len(os.Getenv("UPCLAPI_DATACFG")) == 0 {
err = fmt.Errorf("%s\nUPCLAPI_DATACFG environment value not set\n",msg)
return runFlags,err
}
}
// fmt.Printf("dataCfgPath: %+v\n",dataCfgPath) // Print with Variable Name
// Validate the path first
// if err := ValidateDataPath(runFlags.dataCfgPath); err != nil {
// return runFlags,err
// }
} else {
if runFlags.command != os.Getenv("UPCLAPI_COMMAND") && runFlags.id == os.Getenv("UPCLAPI_ID") {
fmt.Fprintf(os.Stdout, "Warning: Resource ID is set in UPCLAPI_ID enviroment to: %s\n",runFlags.id)
}
}
// Return the configuration path
return runFlags,nil
}

158
networks.go Normal file
View File

@ -0,0 +1,158 @@
package main
import (
"fmt"
"os"
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
)
func getNetworkInterfaces(srv ServerConfig) []request.CreateServerInterface {
var networkInterfaces []request.CreateServerInterface
for _,net := range srv.Networks {
if net.Access == "private" {
var sourceIPFiltering upcloud.Boolean = upcloud.True
if net.SourceIPFiltering == "no" {
sourceIPFiltering = upcloud.False
}
networkInterfaces = append(networkInterfaces, request.CreateServerInterface{
IPAddresses: []request.CreateServerIPAddress{
{
Family: net.Family,
Address: net.IPAddress,
},
},
Type: net.Access,
Network: net.Network,
SourceIPFiltering: sourceIPFiltering,
})
} else {
networkInterfaces = append(networkInterfaces, request.CreateServerInterface{
IPAddresses: []request.CreateServerIPAddress{
{
Family: net.Family,
},
},
Type: net.Access,
// SourceIPFiltering: net.SourceIPFiltering,
})
}
}
return networkInterfaces
}
func modifyIPOneServer(s *service.Service, server upcloud.Server,nettype string, target ServerConfig) error {
var target_NetCfg NetworksConfig
for _,it := range target.Networks {
if it.Access == nettype { // upcloud.NetworkTypePrivate {
target_NetCfg = it
break
}
}
if target_NetCfg.IPAddress == "" {
return nil
}
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
UUID: server.UUID,
})
if err != nil {
return err
}
// target_MAC := ""
target_Index := -1
target_pos := -1
org_IPAddress := ""
for i := 0; i < len(info.Networking.Interfaces); i++ {
if info.Networking.Interfaces[i].Type == target_NetCfg.Access {
// fmt.Fprintf(os.Stderr, "Info: %#v\n",info.Networking.Interfaces[i])
target_pos=i
target_Index=info.Networking.Interfaces[i].Index
for _,ip := range info.Networking.Interfaces[i].IPAddresses {
if ip.Address == target_NetCfg.IPAddress {
fmt.Fprintf(os.Stderr, "Already interface in server: %s to config %#v\n", server.Hostname,target_NetCfg)
return nil
}
org_IPAddress = ip.Address
}
break
}
target_Index=info.Networking.Interfaces[i].Index
}
if target_pos == -1 {
target_Index +=1
}
if target_Index == -1 { // } && target_MAC != "" {
fmt.Fprintf(os.Stderr, "Unable to find interface in server: %s to config %#v\n", server.Hostname,target_NetCfg)
return nil
}
err = stopOneServer(s,server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to stop server: %#v\n", err)
return err
}
if target_pos > -1 {
err = s.DeleteNetworkInterface(&request.DeleteNetworkInterfaceRequest{
Index: target_Index,
ServerUUID: server.UUID,
})
if err != nil {
fmt.Fprintf(os.Stderr, "%s: Unable to delete interface %d server %s: %#v\n",org_IPAddress, target_Index,server.Hostname,err)
return err
}
}
var ipAddress []request.CreateNetworkInterfaceIPAddress
var r *request.CreateNetworkInterfaceRequest
switch(target_NetCfg.Access) {
case upcloud.NetworkTypeUtility:
ip_addrr := request.CreateNetworkInterfaceIPAddress{
Family: target_NetCfg.Family,
}
ipAddress = append(ipAddress, ip_addrr)
r = &request.CreateNetworkInterfaceRequest{
ServerUUID: server.UUID,
Type: upcloud.NetworkTypeUtility,
IPAddresses: ipAddress,
}
case upcloud.NetworkTypePublic:
ip_addrr := request.CreateNetworkInterfaceIPAddress{
Family: target_NetCfg.Family,
}
ipAddress = append(ipAddress, ip_addrr)
r = &request.CreateNetworkInterfaceRequest{
ServerUUID: server.UUID,
Type: upcloud.NetworkTypePublic,
IPAddresses: ipAddress,
}
case upcloud.NetworkTypePrivate:
ip_addrr := request.CreateNetworkInterfaceIPAddress{
Family: target_NetCfg.Family,
Address: target_NetCfg.IPAddress,
}
var sourceIPFiltering upcloud.Boolean = upcloud.True
if target_NetCfg.SourceIPFiltering == "no" {
sourceIPFiltering = upcloud.False
}
ipAddress = append(ipAddress, ip_addrr)
r = &request.CreateNetworkInterfaceRequest{
ServerUUID: server.UUID,
Type: upcloud.NetworkTypePrivate,
NetworkUUID: target_NetCfg.Network,
IPAddresses: ipAddress,
SourceIPFiltering: sourceIPFiltering,
}
}
newIP,err := s.CreateNetworkInterface(r)
if err != nil {
fmt.Fprintf(os.Stderr, "error %s: %#v\n", target_NetCfg.IPAddress, err)
} else {
fmt.Fprintf(os.Stderr, "IP %s installed for Private Network: %s in %s\n", target_NetCfg.IPAddress, newIP.Network, server.Hostname)
}
err = startOneServer(s,server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to start server: %#v\n", err)
return err
}
return nil
}

279
prices.go Normal file
View File

@ -0,0 +1,279 @@
package main
import (
"fmt"
"math"
// "log"
// "strings"
// "encoding/json"
// "github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
// "github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
// "gocloud.dev/server"
// "gocloud.dev/server"
// "github.com/davecgh/go-spew/spew"
)
func dataPriceResource(s *service.Service, dataprices DataPrices, zone string, target string) (*Price,error) {
resource_price := &Price{}
resource_zone := PricesZone{}
for _,price_zone := range dataprices.Prices.Zone {
if price_zone.Name == zone {
resource_zone = price_zone
break
}
}
if resource_zone.Name == zone {
switch target {
case "firewall":
resource_price = &resource_zone.Firewall
case "io_request_backup":
resource_price = &resource_zone.IORequestBackup
case "io_request_maxiops":
resource_price = &resource_zone.IORequestMaxIOPS
case "ipv4_address":
resource_price = &resource_zone.IPv4Address
case "ipv6_address":
resource_price = &resource_zone.IPv6Address
case "managed_database_1x1xCPU-2GB-25GB":
resource_price = &resource_zone.ManagedDatabase1x1CPU2GB25GB
case "managed_database_1x2xCPU-4GB-100GB":
resource_price = &resource_zone.ManagedDatabase1x2CPU4GB100GB
case "managed_database_1x2xCPU-4GB-50GB":
resource_price = &resource_zone.ManagedDatabase1x2CPU4GB50GB
case "managed_database_2x2xCPU-4GB-100GB":
resource_price = &resource_zone.ManagedDatabase2x2CPU4GB100GB
case "managed_database_2x2xCPU-4GB-50GB":
resource_price = &resource_zone.ManagedDatabase2x2CPU4GB50GB
case "managed_database_2x4xCPU-8GB-100GB":
resource_price = &resource_zone.ManagedDatabase2x4CPU8GB100GB
case "managed_database_2x4xCPU-8GB-50GB":
resource_price = &resource_zone.ManagedDatabase2x4CPU8GB50GB
case "managed_database_2x6xCPU-16GB-100GB":
resource_price = &resource_zone.ManagedDatabase2x6CPU16GB100GB
case "managed_database_2x6xCPU-16GB-250GB":
resource_price = &resource_zone.ManagedDatabase2x6CPU16GB50GB
case "managed_database_2x8xCPU-32GB-100GB":
resource_price = &resource_zone.ManagedDatabase2x8CPU32GB100GB
case "managed_database_2x8xCPU-32GB-250GB":
resource_price = &resource_zone.ManagedDatabase2x8CPU22GB250GB
case "managed_database_3x2xCPU-4GB-100GB":
resource_price = &resource_zone.ManagedDatabase3x2CPU4GB100GB
case "managed_database_3x2xCPU-4GB-200GB":
resource_price = &resource_zone.ManagedDatabase3x2CPU4GB200GB
case "managed_database_3x4xCPU-8GB-100GB":
resource_price = &resource_zone.ManagedDatabase3x4CPU8GB100GB
case "managed_database_3x4xCPU-8GB-200GB":
resource_price = &resource_zone.ManagedDatabase3x4CPU8GB200GB
case "managed_database_3x6xCPU-16GB-200GB":
resource_price = &resource_zone.ManagedDatabase3x6CPU16GB200GB
case "managed_database_3x6xCPU-16GB-500GB":
resource_price = &resource_zone.ManagedDatabase3x6CPU16GB500GB
case "managed_database_3x8xCPU-32GB-200GB":
resource_price = &resource_zone.ManagedDatabase3x8CPU32GB200GB
case "managed_database_3x8xCPU-32GB-500GB":
resource_price = &resource_zone.ManagedDatabase3x8CPU32GB500GB
case "network_private_vlan":
resource_price = &resource_zone.NetworkPrivateVLAN
case "object_storage_1TB":
resource_price = &resource_zone.ObjectStorage1TB
case "object_storage_250GB":
resource_price = &resource_zone.ObjectStorage250GB
case "object_storage_500GB":
resource_price = &resource_zone.ObjectStorage500GB
case "public_ipv4_bandwidth_in":
resource_price = &resource_zone.PublicIPv4BandwidthIn
case "public_ipv4_bandwidth_out":
resource_price = &resource_zone.PublicIPv4BandwidthOut
case "public_ipv6_bandwidth_in":
resource_price = &resource_zone.PublicIPv6BandwidthIn
case "public_ipv6_bandwidth_out":
resource_price = &resource_zone.PublicIPv6BandwidthOut
case "server_core":
resource_price = &resource_zone.ServerCore
case "server_memory":
resource_price = &resource_zone.ServerMemory
case "server_plan_6xCPU-8GB":
resource_price = &resource_zone.ServerPlan6xCPU8GB
case "server_plan_12xCPU-48GB":
resource_price = &resource_zone.ServerPlan12xCPU48GB
case "server_plan_16xCPU-64GB":
resource_price = &resource_zone.ServerPlan16xCPU64GB
case "server_plan_1xCPU-1GB":
resource_price = &resource_zone.ServerPlan1xCPU1GB
case "server_plan_1xCPU-2GB":
resource_price = &resource_zone.ServerPlan1xCPU2GB
case "server_plan_20xCPU-128GB":
resource_price = &resource_zone.ServerPlan20xCPU128GB
case "server_plan_20xCPU-96GB":
resource_price = &resource_zone.ServerPlan20xCPU96GB
case "server_plan_2xCPU-4GB":
resource_price = &resource_zone.ServerPlan2xCPU4GB
case "server_plan_4xCPU-8GB":
resource_price = &resource_zone.ServerPlan4xCPU8GB
case "server_plan_6xCPU-16GB":
resource_price = &resource_zone.ServerPlan6xCPU16GB
case "server_plan_8xCPU-32GB":
resource_price = &resource_zone.ServerPlan8xCPU32GB
case "simple_backup_dailies_12xCPU-48GB":
resource_price = &resource_zone.SimpleBackupDailies12xCPU48GB
case "simple_backup_dailies_16xCPU-64GB":
resource_price = &resource_zone.SimpleBackupDailies16xCPU64GB
case "simple_backup_dailies_1xCPU-1GB":
resource_price = &resource_zone.SimpleBackupDailiesx1CPU1GB
case "simple_backup_dailies_1xCPU-2GB":
resource_price = &resource_zone.SimpleBackupDailies1xCPU2GB
case "simple_backup_dailies_20xCPU-128GB":
resource_price = &resource_zone.SimpleBackupDailies20xCPU128GB
case "simple_backup_dailies_20xCPU-96GB":
resource_price = &resource_zone.SimpleBackupDailies20xCPU96GB
case "simple_backup_dailies_2xCPU-4GB":
resource_price = &resource_zone.SimpleBackupDailies2xCPU4GB
case "simple_backup_dailies_4xCPU-8GB":
resource_price = &resource_zone.SimpleBackupDailies4xCPU8GB
case "simple_backup_dailies_6xCPU-16GB":
resource_price = &resource_zone.SimpleBackupDailies6xCPU16GB
case "simple_backup_dailies_8xCPU-32GB":
resource_price = &resource_zone.SimpleBackupDailies8xCPU32GB
case "simple_backup_extra_dailies":
resource_price = &resource_zone.SimpleBackupExtraDailies
case "simple_backup_extra_monthlies":
resource_price = &resource_zone.SimpleBackupExtraMontlies
case "simple_backup_extra_weeklies":
resource_price = &resource_zone.SimpleBackupExtraWeeklies
case "simple_backup_monthlies_12xCPU-48GB":
resource_price = &resource_zone.SimpleBackupMonthlies12xCPU48GB
case "simple_backup_monthlies_16xCPU-64GB":
resource_price = &resource_zone.SimpleBackupMonthlies16xCPU64GB
case "simple_backup_monthlies_1xCPU-1GB":
resource_price = &resource_zone.SimpleBackupMonthlies1xCPU1GB
case "simple_backup_monthlies_1xCPU-2GB":
resource_price = &resource_zone.SimpleBackupMonthlies1xCPU2GB
case "simple_backup_monthlies_20xCPU-128GB":
resource_price = &resource_zone.SimpleBackupMonthlies20xCPU128GB
case "simple_backup_monthlies_20xCPU-96GB":
resource_price = &resource_zone.SimpleBackupMonthlies20xCPU96GB
case "simple_backup_monthlies_2xCPU-4GB":
resource_price = &resource_zone.SimpleBackupMonthlies2xCPU4GB
case "simple_backup_monthlies_4xCPU-8GB":
resource_price = &resource_zone.SimpleBackupMonthlies4xCPU8GB
case "simple_backup_monthlies_6xCPU-16GB":
resource_price = &resource_zone.SimpleBackupMonthlies6xCPU16GB
case "simple_backup_monthlies_8xCPU-32GB":
resource_price = &resource_zone.SimpleBackupMonthlies8xCPU32GB
case "simple_backup_weeklies_12xCPU-48GB":
resource_price = &resource_zone.SimpleBackupWeeklies12xCPU48GB
case "simple_backup_weeklies_16xCPU-64GB":
resource_price = &resource_zone.SimpleBackupWeeklies16xCPU64GB
case "simple_backup_weeklies_1xCPU-1GB":
resource_price = &resource_zone.SimpleBackupWeeklies1xCPU1GB
case "simple_backup_weeklies_1xCPU-2GB":
resource_price = &resource_zone.SimpleBackupWeeklies1xCPU2GB
case "simple_backup_weeklies_20xCPU-128GB":
resource_price = &resource_zone.SimpleBackupWeeklies20xCPU128GB
case "simple_backup_weeklies_20xCPU-96GB":
resource_price = &resource_zone.SimpleBackupWeeklies20xCPU96GB
case "simple_backup_weeklies_2xCPU-4GB":
resource_price = &resource_zone.SimpleBackupWeeklies2xCPU4GB
case "simple_backup_weeklies_4xCPU-8GB":
resource_price = &resource_zone.SimpleBackupWeeklies4xCPU8GB
case "simple_backup_weeklies_6xCPU-16GB":
resource_price = &resource_zone.SimpleBackupWeeklies6xCPU16GB
case "simple_backup_weeklies_8xCPU-32GB":
resource_price = &resource_zone.SimpleBackupWeeklies8xCPU32GB
case "storage_hdd":
resource_price = &resource_zone.StorageHDD
case "storage_backup":
resource_price = &resource_zone.StorageBackup
case "storage_maxiops":
resource_price = &resource_zone.StorageMaxIOPS
case "storage_template":
resource_price = &resource_zone.StorageTemplate
default:
//fmt.Fprintf(os.Stderr, "Resource price %s in %s NOT FOUND",target,zone)
return resource_price, fmt.Errorf("Resource price %s in %s NOT FOUND",target,zone)
}
resource_price.Hour = (resource_price.Price / 100)
// resource_price.Month = (resource_price.Hour * MONTH_HOURS)
resource_price.Month = math.Round(resource_price.Hour * MONTH_HOURS)
// fmt.Printf("Resource %s price in %s: %#v\n", target, zone, resource_price)
// fmt.Printf("Resource %s price in %s: %d %.4f\n", target, zone, resource_price.Amount, resource_price.Price)
}
// enc := json.NewEncoder(os.Stdout)
// enc.Encode(resource_price)
return resource_price, nil
}
/*
// UpCloud API is incomplete to make this work
// priceZones := upcloud.PriceZones{}
func upcloud_priceResource(s *service.Service, zone string, target string) (upcloud.Price,error) {
pricesZones,err:= s.GetPriceZones()
resource_price := new(upcloud.Price)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get prices zones: %#v\n", err)
return *resource_price,err
}
resource_zone := upcloud.PriceZone{}
for _,price_zone := range pricesZones.PriceZones {
if price_zone.Name == zone {
resource_zone = price_zone
break
}
}
if resource_zone.Name == zone {
switch target {
case "firewall":
resource_price = resource_zone.Firewall
case "io_request_backup":
resource_price = resource_zone.IORequestBackup
case "io_request_maxiops":
resource_price = resource_zone.IORequestMaxIOPS
case "ipv4_address":
resource_price = resource_zone.IPv4Address
case "ipv6_address":
resource_price = resource_zone.IPv6Address
case "public_ipv4_bandwidth_in":
resource_price = resource_zone.PublicIPv4BandwidthIn
case "public_ipv4_bandwidth_out":
resource_price = resource_zone.PublicIPv4BandwidthOut
case "public_ipv6_bandwidth_in":
resource_price = resource_zone.PublicIPv6BandwidthIn
case "public_ipv6_bandwidth_out":
resource_price = resource_zone.PublicIPv6BandwidthOut
case "server_core":
resource_price = resource_zone.ServerCore
case "server_memory":
resource_price = resource_zone.ServerMemory
case "server_plan_1xCPU-1GB":
resource_price = resource_zone.ServerPlan1xCPU1GB
case "server_plan_2xCPU-2GB":
resource_price = resource_zone.ServerPlan2xCPU2GB
case "server_plan_2xCPU-4GB":
resource_price.Price = 2.9761
case "server_plan_4xCPU-4GB":
resource_price = resource_zone.ServerPlan4xCPU4GB
case "server_plan_6xCPU-8GB":
resource_price = resource_zone.ServerPlan6xCPU8GB
case "storage_backup":
resource_price = resource_zone.StorageBackup
case "storage_maxiops":
resource_price = resource_zone.StorageMaxIOPS
case "storage_template":
resource_price = resource_zone.StorageTemplate
default:
//fmt.Fprintf(os.Stderr, "Resource price %s in %s NOT FOUND",target,zone)
return *resource_price, fmt.Errorf("Resource price %s in %s NOT FOUND",target,zone)
}
// fmt.Printf("Resource %s price in %s: %#v\n", target, zone, resource_price)
// fmt.Printf("Resource %s price in %s: %d %.4f\n", target, zone, resource_price.Amount, resource_price.Price)
}
// enc := json.NewEncoder(os.Stdout)
// enc.Encode(resource_price)
return *resource_price, nil
}
*/

447
run.go Normal file
View File

@ -0,0 +1,447 @@
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"strings"
"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 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 := ""
password := ""
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 {
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
}
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)
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
runComand := 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(sshAccess, runComand)
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(runComand, " ")
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
}

306
servers.go Normal file
View File

@ -0,0 +1,306 @@
package main
import (
"errors"
"fmt"
// "log"
"os"
"time"
// "strings"
"encoding/json"
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
// "gocloud.dev/server"
// "gocloud.dev/server"
// "github.com/davecgh/go-spew/spew"
)
func createServer(s *service.Service, datacfg *DataConfig,sshkeys request.SSHKeySlice) error {
fmt.Println("Creating servers ...")
//fmt.Fprintf(os.Stdout, "%#v\n",sshkeys)
//fmt.Printf("%+v\n",datacfg) // Print with Variable Name
// fmt.Printf("Created server: %#v\n", details)
servers, _ := s.GetServers()
for _,srv := range datacfg.Servers {
alreadyExists := false
for _, server := range servers.Servers {
if server.Hostname == srv.Hostname {
alreadyExists = true
break
}
}
// Networking: setNetworkInterfaces(datacfg),
title := fmt.Sprintf("%s %s", srv.Hostname, srv.Title)
if alreadyExists {
fmt.Fprintf(os.Stderr, "Already exists server: %s\n", title)
continue
}
fmt.Printf("server: %s\n", title)
var srv_sshkeys request.SSHKeySlice
if len(srv_sshkeys) == 0 && len(sshkeys) > 0 {
srv_sshkeys = sshkeys
} else {
srv_sshkeys = srv.LoginUser.SSHKeys
}
details, err := s.CreateServer(&request.CreateServerRequest{
Hostname: srv.Hostname,
Title: title, // fmt.Sprintf("example-cli-server-%04d", rand.Int31n(1000)),
Zone: srv.Zone,
Plan: srv.Plan,
Metadata: srv.Metadata,
TimeZone: srv.TimeZone,
StorageDevices: getStorageDevices(srv),
Networking: &request.CreateServerNetworking{
Interfaces: getNetworkInterfaces(srv),
},
LoginUser: &request.LoginUser{
SSHKeys: srv_sshkeys,
},
UserData: srv.UserData,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to create server: %#v\n", err)
return err
}
// spew.Println(details)
if len(details.UUID) == 0 {
fmt.Fprintf(os.Stderr, "UUID missing")
return errors.New("UUID too short")
}
details, err = s.WaitForServerState(&request.WaitForServerStateRequest{
UUID: details.UUID,
DesiredState: upcloud.ServerStateStarted,
Timeout: 1 * time.Minute,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to wait for server: %#v", err)
return err
}
fmt.Printf("Created server: %#v\n", details)
fixStorageDevices(s,srv,details,"size")
addTagsToId(s,details.UUID,srv.Tags)
}
return nil
}
func modifyServer(s *service.Service,srvrCfg ServerConfig, serverDetails *upcloud.ServerDetails, targetPlan string) error {
title := fmt.Sprintf("%s %s", serverDetails.Hostname, serverDetails.Title)
fmt.Printf("Modify server %s ...\n",title)
if serverDetails.Plan == targetPlan {
fmt.Printf("%s == %s\n",serverDetails.Plan, targetPlan)
return nil
}
if serverDetails.State != upcloud.ServerStateStopped {
err := stopOneServer(s, serverDetails.Server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to stop server %#v: %#v\n", serverDetails.UUID, err)
return err
}
}
details,err := s.WaitForServerState(&request.WaitForServerStateRequest{
UUID: serverDetails.UUID,
DesiredState: upcloud.ServerStateStopped,
Timeout: 1 * time.Minute,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to wait for server: %#v", err)
return err
}
fmt.Fprintf(os.Stderr, "Modify server after to: %s\n", targetPlan)
details, err = s.ModifyServer(&request.ModifyServerRequest{
UUID: serverDetails.UUID,
Plan: targetPlan,
})
fmt.Printf("Modify server done: %#v %#v\n", details, err)
// if err != nil {
// } else {
// fmt.Fprintf(os.Stderr, "Failed to modify server: %#v", err)
// // return err
// }
fmt.Fprintf(os.Stderr, "Start server after modify: %s\n", title)
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
UUID: serverDetails.UUID,
})
if info.State != upcloud.ServerStateStarted {
err = startOneServer(s, info.Server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to start server %#v: %#v\n", serverDetails.UUID, err)
return err
}
}
return nil
}
func waitForState(s *service.Service, server upcloud.Server, desiredState string) error {
_, err := s.WaitForServerState(&request.WaitForServerStateRequest{
UUID: server.UUID,
DesiredState: desiredState,
Timeout: 1 * time.Minute,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to wait for server to reach desired state: %#v", err)
return err
}
return nil
}
func startOneServer(s *service.Service, server upcloud.Server) error {
fmt.Printf("Starting %s (%s) ... \n", server.Hostname, server.UUID)
if server.State == upcloud.ServerStateStopped {
fmt.Printf("Server %s (%s) is stopped. Starting\n", server.Title, server.UUID)
}
_, err := s.StartServer(&request.StartServerRequest{
UUID: server.UUID,
// StopType: request.ServerStopTypeHard,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to start server: %#v\n", err)
return err
}
err = waitForState(s,server,upcloud.ServerStateStarted)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to start state server: %#v\n", err)
return err
}
fmt.Printf("Successfully started %s (%s)\n", server.Title, server.UUID)
return nil
}
func restarOneServer(s *service.Service, server upcloud.Server) error {
fmt.Printf("Restarting %s (%s) ... \n", server.Hostname, server.UUID)
_, err := s.RestartServer(&request.RestartServerRequest{
UUID: server.UUID,
StopType: request.ServerStopTypeHard,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to restart server: %#v\n", err)
return err
}
err = waitForState(s,server,upcloud.ServerStateStarted)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to restart state server: %#v\n", err)
return err
}
fmt.Printf("Successfully restarted %s (%s)\n", server.Title, server.UUID)
return nil
}
func stopOneServer(s *service.Service, server upcloud.Server) error {
fmt.Printf("Stopping %s (%s) ... \n", server.Hostname, server.UUID)
if server.State != upcloud.ServerStateStopped {
// fmt.Printf("Server %s (%s) is not stopped. Stopping\n", server.Title, server.UUID)
_, err := s.StopServer(&request.StopServerRequest{
UUID: server.UUID,
StopType: request.ServerStopTypeHard,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to stop server: %#v\n", err)
return err
}
err = waitForState(s,server,upcloud.ServerStateStopped)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to stop state server: %#v\n", err)
return err
}
fmt.Printf("Successfully stopped %s (%s)\n", server.Title, server.UUID)
}
return nil
}
func deleteOneServer(s *service.Service, server upcloud.Server, keepStorage string) error {
err := stopOneServer(s,server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to stop server: %#v\n", err)
return err
}
if keepStorage != "keep" {
fmt.Printf("Deleting %s (%s) with storage\n", server.Title, server.UUID)
err = s.DeleteServerAndStorages(&request.DeleteServerAndStoragesRequest{
UUID: server.UUID,
})
} else {
fmt.Printf("Deleting %s (%s) keep storage\n", server.Title, server.UUID)
err = s.DeleteServer(&request.DeleteServerRequest{
UUID: server.UUID,
})
}
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to delete server: %#v\n", err)
return err
}
fmt.Printf("Successfully deleted %s (%s)\n", server.Title, server.UUID)
return nil
}
func detailsOneServer(s *service.Service, server upcloud.Server, dataprices DataPrices) error {
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)
return err
}
// price,err_price:= priceResource(s,server.Zone,fmt.Sprintf("server_plan_%s",server.Plan))
price,err_price:= dataPriceResource(s,dataprices,server.Zone,fmt.Sprintf("server_plan_%s",server.Plan))
if err_price != nil {
fmt.Fprintf(os.Stderr, "Unable to get price info from server %#v: %#v\n", server.UUID, err_price)
return err
}
info_server := &InfoServer{info: *info, price: *price}
// c, err := json.Marshal(info_server)
// if err != nil {
// fmt.Println("error:", err)
// }
// os.Stdout.Write(c)
enc := json.NewEncoder(os.Stdout)
enc.Encode(info_server)
// enc.Encode(price)
/*
i,_ := json.Marshal(info)
p,_ := json.Marshal(price)
fmt.Printf("info %s \n", i)
fmt.Printf("price %s \n", p)
jsonData := []byte(`{"pric": "Value"}`)
// info_server := &v{info: fmt.Sprintf("%s",i) , price: fmt.Sprintf("%s",p)}
info_server := InfoServerPrice{info: "HOLA", price: "$$$" }
// v := struct {
// info upcloud.ServerDetails `json:"upcloud.ServerDetails"`
// price upcloud.Price `json:"upcloud.Price"`
// }{}
fmt.Printf("InfoServer %#v\n", info_server)
enc := json.NewEncoder(os.Stdout)
enc.Encode(info_server)
// b,err_un := json.Marshal(info_server) // json.Marshal(info_server)
// if err_un != nil {
// fmt.Fprintf(os.Stderr, "Unable to marshall: %#v\n", err_un)
// return err_un
// }
// os.Stdout.Write(b)
type ColorGroup struct {
ID int
Name string
Colors []string
}
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
c, err := json.Marshal(group)
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(c)
// err_en := enc.Encode(info_server)
// if err_en != nil {
// fmt.Fprintf(os.Stderr, "Unable to encode info_server: %#v\n", err_en)
// return err_en
// }
// fmt.Printf("InfoServer %#v\n", info)
*/
return nil
}

110
ssh.go Normal file
View File

@ -0,0 +1,110 @@
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
"time"
"golang.org/x/crypto/ssh"
// "github.com/mitchellh/go-homedir"
// "github.com/davecgh/go-spew/spew"
)
func publicKeyAuthFunc(kPath string) ssh.AuthMethod {
// keyPath, err := homedir.Expand(kPath)
// if err != nil {
// log.Fatal("find key's home dir failed", err)
// }
// key, err := ioutil.ReadFile(keyPath )
key, err := ioutil.ReadFile(kPath )
if err != nil {
log.Fatal("ssh key file read failed", err)
}
// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatal("ssh key signer failed", err)
}
return ssh.PublicKeys(signer)
}
func runSSH(cfg SSHAccess, cmds ...string ) ([]byte, error) {
// fmt.Fprintf(os.Stderr, "SSH: %#v\n", cfg)
fmt.Fprintf(os.Stderr, "%s - running : %s\n\n", cfg.Host, cmds)
// Create SSHP login configuration
config := &ssh.ClientConfig{
Timeout: time.Second, //ssh connection time out time is one second, if SSH validation error returns in one second
User: cfg.User,
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // This is OK, but not safe enough.
// HostKeyCallback: hostKeyCallBackFunc(h.Host),
}
if cfg.UType == "password" {
config.Auth = []ssh.AuthMethod{ssh.Password(cfg.Password)}
} else {
config.Auth = []ssh.AuthMethod{publicKeyAuthFunc(cfg.KeyPath)}
}
addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
conn, err := ssh.Dial("tcp", addr,config)
if err != nil {
log.Fatal("Failed to dial SSH client", err)
return []byte{}, err
}
session, err := conn.NewSession()
if err != nil {
log.Fatal(err)
}
defer session.Close()
modes := ssh.TerminalModes{
ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
err = session.RequestPty("xterm", 80, 40, modes)
if err != nil {
return []byte{}, err
}
in, err := session.StdinPipe()
if err != nil {
log.Fatal(err)
}
out, err := session.StdoutPipe()
if err != nil {
log.Fatal(err)
}
var output []byte
go func(in io.WriteCloser, out io.Reader, output *[]byte) {
var (
line string
r = bufio.NewReader(out)
)
for {
b, err := r.ReadByte()
if err != nil {
break
}
*output = append(*output, b)
if b == byte('\n') {
line = ""
continue
}
line += string(b)
if strings.HasPrefix(line, "[sudo] password for ") && strings.HasSuffix(line, ": ") {
_, err = in.Write([]byte(cfg.Password+ "\n"))
if err != nil {
break
}
}
}
}(in, out, &output)
cmd := strings.Join(cmds, "; ")
_, err = session.Output(cmd)
if err != nil {
return []byte{}, err
}
return output, nil
}

281
storage.go Normal file
View File

@ -0,0 +1,281 @@
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"
"time"
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
"gopkg.in/yaml.v3"
)
func NewStoreConfig(dataPath string) (*StoreConfig, error) {
// Create config structure
storeConfig := &StoreConfig{}
// Open config file
file, err := os.Open(dataPath)
if err != nil {
return nil, err
}
defer file.Close()
// Init new YAML decode
d := yaml.NewDecoder(file)
// Start YAML decoding from file
if err := d.Decode(&storeConfig); err != nil {
return nil, err
}
return storeConfig, nil
}
func loadStoreConfig(dataCfgPath string) (*StoreConfig, error) {
// datacfg := &DataConfig{}
storecfg, err := NewStoreConfig(dataCfgPath)
if err != nil {
log.Fatal(err)
return storecfg, err
}
return storecfg, nil
}
func getStorageDevices(srv ServerConfig) []request.CreateServerStorageDevice {
var storageDevices []request.CreateServerStorageDevice
for _,storage := range srv.StorageDevices {
storageDevices = append(storageDevices,request.CreateServerStorageDevice{
Action : storage.Action,
Title : storage.Title,
Storage : storage.Storage,
Size : storage.Size,
Tier : storage.Tier,
})
}
return storageDevices
}
func deleteStorage(s *service.Service, uuid string) error {
fmt.Println("Getting storage")
storages, err := s.GetStorages(&request.GetStoragesRequest{
Access: upcloud.StorageAccessPrivate,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get storages: %#v\n", err)
return err
}
fmt.Printf("Retrieved %d storages\n", len(storages.Storages))
if len(storages.Storages) > 0 {
for _, storage := range storages.Storages {
if storage.UUID == uuid {
fmt.Printf("Deleting storage %s", storage.UUID)
err := errors.New("Dummy")
for i := 0; err != nil && i < 5; i++ {
fmt.Printf("%d: Deleting %s (%s)\n", i, storage.Title, storage.UUID)
err = s.DeleteStorage(&request.DeleteStorageRequest{
UUID: storage.UUID,
})
}
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to delete storage: %#v (%s)\n", err, err.Error())
return err
}
fmt.Printf("Successfully deleted %s (%s)\n", storage.Title, storage.UUID)
}
}
}
return nil
}
func modifyStorage(s *service.Service, storecfg *StoreConfig) error {
if len(storecfg.Uuid) == 0 {
fmt.Printf("Unable to get storages uuid from data file \n")
return nil
}
for _, item := range storecfg.Servers {
s.StopServer(&request.StopServerRequest{
UUID: item,
StopType: request.ServerStopTypeHard,
})
}
fmt.Println("Getting storage")
storages, err := s.GetStorages(&request.GetStoragesRequest{
Access: upcloud.StorageAccessPrivate,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get storages: %#v\n", err)
return err
}
fmt.Printf("Retrieved %d storages\n", len(storages.Storages))
if len(storages.Storages) > 0 {
for _, storage := range storages.Storages {
if storage.UUID == storecfg.Uuid {
fmt.Printf("Modify storage %s (%s)\n",storage.Title, storage.UUID)
err := errors.New("Dummy")
for i := 0; err != nil && i < 5; i++ {
fmt.Printf("%d: Modify %s (%s)\n", i, storage.Title, storage.UUID)
_, err = s.ModifyStorage(&request.ModifyStorageRequest{
UUID: storecfg.Uuid,
// Title: datacfg.Title,
Size: storecfg.Size,
// BackupRule *upcloud.BackupRule `json:"backup_rule,omitempty"`
})
}
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to modify storage: %#v (%s)\n", err, err.Error())
// return err
}
fmt.Printf("Successfully modified %s (%s)\n", storage.Title, storage.UUID)
}
}
}
for _, item := range storecfg.Servers {
s.StartServer(&request.StartServerRequest{
UUID: item,
// StopType: request.ServerStopTypeHard,
})
}
return nil
}
func fixStorageDevices(s *service.Service,srvrCfg ServerConfig, serverDetails *upcloud.ServerDetails, targetSize string) {
if len(serverDetails.StorageDevices) == 0 {
return
}
err := errors.New("Dummy")
// fmt.Fprintf(os.Stderr, "%#v: %#v\n", serverDetails.StorageDevices, srvrCfg)
for i, storage := range serverDetails.StorageDevices {
store_size := 0
switch targetSize {
case "size":
store_size = srvrCfg.StorageDevices[i].Size
case "part0":
s := strings.Split(srvrCfg.StorageDevices[i].PartSizes, ",")
store_size, err = strconv.Atoi(s[0])
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get partsize 0 to create image storage for %s: %#v (%s)\n", storage.Title, err, err.Error())
return
}
case "part1":
s := strings.Split(srvrCfg.StorageDevices[i].PartSizes, ",")
store_size, err = strconv.Atoi(s[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get partsize 1 to create image storage for %s: %#v (%s)\n", storage.Title, err, err.Error())
return
}
case "final":
store_size = srvrCfg.StorageDevices[i].FinalSize
}
if store_size == 0 {
fmt.Printf("No targetSize found [size|final] for %s (%s) %dGB == %dGB\n",storage.Title, storage.UUID, storage.Size, store_size)
continue
}
if store_size== storage.Size {
fmt.Printf("%s (%s) %dGB == %dGB\n",storage.Title, storage.UUID, storage.Size, store_size)
continue
}
fmt.Printf("FixStorage %s (%s) %dGB to %dGB\n",storage.Title, storage.UUID, storage.Size, store_size)
if serverDetails.Server.State != upcloud.ServerStateStopped {
err = stopOneServer(s, serverDetails.Server)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to stop server %#v: %#v\n", serverDetails.Server.UUID, err)
continue
}
}
_, err = s.ModifyStorage(&request.ModifyStorageRequest{
UUID: storage.UUID,
// Title: srvrCfg.StorageDevices[i].Title,
Size: store_size,
// BackupRule *upcloud.BackupRule `json:"backup_rule,omitempty"`
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to modify storage: %#v (%s)\n", err, err.Error())
} else {
fmt.Printf("Successfully modified %s (%s) to %dGB\n", storage.Title, storage.UUID, store_size)
}
}
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
UUID: serverDetails.UUID,
})
if info.Server.State != upcloud.ServerStateStarted {
startOneServer(s, serverDetails.Server)
}
}
func createStorageImage(s *service.Service, target_uuid string, title string) error {
info,err := s.TemplatizeStorage(&request.TemplatizeStorageRequest{
UUID: target_uuid,
Title: strings.Trim(title, "\""),
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to create image storage: %#v (%s)\n", err, err.Error())
} else {
fmt.Printf("Successfully created %s (%s)\n", title, target_uuid)
}
s.WaitForStorageState(&request.WaitForStorageStateRequest{
UUID: target_uuid,
DesiredState: "online",
Timeout: 1 * time.Minute,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to wait for storage to reach desired online state: %#v", err)
return err
}
enc := json.NewEncoder(os.Stdout)
enc.Encode(info)
return nil
}
func listStorages(s *service.Service, dataCfg *DataConfig, target string) error {
storages, err := s.GetStorages(&request.GetStoragesRequest{
Access: upcloud.StorageAccessPrivate,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get storages: %#v\n", err)
return err
}
servers, _ := s.GetServers()
fmt.Printf("Scanning over %d storages and %d servers \n", len(storages.Storages), len(servers.Servers))
if len(storages.Storages) > 0 {
for _, storage := range storages.Storages {
storage_out := fmt.Sprintf("title: \"%s\", id: %s, size: %d, plan: %s, state: %s, zone: %s, type: %s",
storage.Title, storage.UUID, storage.Size, storage.PartOfPlan, storage.State, storage.Zone, storage.Type)
is_attached := false
for _, server := range servers.Servers {
isInGroup := false
for _, srv := range dataCfg.Servers {
if server.Hostname == srv.Hostname {
isInGroup = true
break
}
}
info,err := s.GetServerDetails(&request.GetServerDetailsRequest{
UUID: server.UUID,
})
if err == nil {
for _, srv_storage := range info.StorageDevices {
if srv_storage.UUID == storage.UUID {
is_attached=true
if isInGroup {
fmt.Printf("%s, attached: \"%s\", id: %s\n", storage_out, server.Title, server.UUID)
}
}
}
}
}
if ! is_attached {
fmt.Printf("%s, attached: none\n", storage_out)
}
if target != "" && storage.UUID == target {
break
}
}
}
return nil
}

56
tags.go Normal file
View File

@ -0,0 +1,56 @@
package main
import (
"fmt"
"os"
"github.com/UpCloudLtd/upcloud-go-api/upcloud"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
)
func infoTagsFromId(s *service.Service, id string) (upcloud.TagServerSlice,error) {
var upTags upcloud.TagServerSlice
if id == "" {
fmt.Fprintf(os.Stderr, "Resouce not found to get tags\n")
return upTags,nil
}
info, err := s.GetServerDetails(&request.GetServerDetailsRequest{
UUID: id,
})
for _, tag := range info.Tags {
upTags = append(upTags, tag)
}
return upTags, err
}
func addTagsToId(s *service.Service, id string, tags []string) error {
if id == "" || len(tags) == 0 {
fmt.Fprintf(os.Stderr, "Resouce not found to add tags to (%s)\n",id)
return nil
}
var upTags upcloud.TagServerSlice
for i := 0; i < len(tags); i++ {
upTags = append(upTags,tags[i])
}
_, err := s.TagServer(&request.TagServerRequest{
UUID: id,
Tags: upTags,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to tag server %s: %#v", id, err)
return err
}
return nil
}
func deleteTagsFromId(s *service.Service, id string, tags []string) error {
if id == "" || len(tags) == 0 {
fmt.Fprintf(os.Stderr, "Resouce not found to get tags (%s)\n",id)
return nil
}
_, err := s.UntagServer(&request.UntagServerRequest{
UUID: id,
Tags: tags,
})
// fmt.Printf("Delete Tag form server: %#v\n", msg)
return err
}