Skip to content

Instantly share code, notes, and snippets.

@stgraber

stgraber/main.go

Created Jun 14, 2021
Embed
What would you like to do?
lxd-evacuate
package main
import (
"fmt"
"math/rand"
"os"
"os/user"
"path"
"sort"
"time"
"github.com/lxc/lxd/lxc/config"
"github.com/lxc/lxd/shared/api"
)
func main() {
rand.Seed(time.Now().Unix())
err := run()
if err != nil {
fmt.Printf("error: %s\n", err)
os.Exit(1)
}
}
func run() error {
srcHost := ""
if len(os.Args) > 2 {
srcHost = os.Args[2]
}
// Figure out config path.
var configDir string
if os.Getenv("LXD_CONF") != "" {
configDir = os.Getenv("LXD_CONF")
} else if os.Getenv("HOME") != "" {
configDir = path.Join(os.Getenv("HOME"), ".config", "lxc")
} else {
user, err := user.Current()
if err != nil {
return err
}
configDir = path.Join(user.HomeDir, ".config", "lxc")
}
// Get the remote.
conf, err := config.LoadConfig(os.ExpandEnv(path.Join(configDir, "config.yml")))
if err != nil {
return err
}
// Connect to LXD.
c, err := conf.GetInstanceServer(os.Args[1])
if err != nil {
return err
}
// List the cluster members.
members, err := c.GetClusterMemberNames()
if err != nil {
return err
}
randomMember := func() string {
again:
target := members[rand.Intn(len(members))]
if target == srcHost {
goto again
}
return target
}
// List all projects.
projects, err := c.GetProjectNames()
if err != nil {
return err
}
// Functions.
actionInstance := func(proj string, inst api.Instance, action string) error {
req := api.InstanceStatePut{
Action: action,
}
if action == "stop" && inst.Status == "Stopped" {
return nil
}
if action == "start" && inst.Status == "Running" {
return nil
}
op, err := c.UseProject(proj).UpdateInstanceState(inst.Name, req, "")
if err != nil {
return err
}
err = op.Wait()
if err != nil {
return err
}
return nil
}
moveInstance := func(proj string, inst api.Instance, target string) error {
// Stop the instance.
err := actionInstance(proj, inst, "stop")
if err != nil {
return err
}
inst.Status = "Stopped"
// Refresh the config
newInst, etag, err := c.UseProject(proj).GetInstance(inst.Name)
if err != nil {
return err
}
inst = *newInst
// Update evacuate variable.
originalLocation := inst.ExpandedConfig["user.evacuate.origin"]
if target != originalLocation {
inst.Config["user.evacuate.origin"] = inst.Location
} else {
delete(inst.Config, "user.evacuate.origin")
}
// Update the config.
op, err := c.UseProject(proj).UpdateInstance(inst.Name, inst.Writable(), etag)
if err != nil {
return err
}
err = op.Wait()
if err != nil {
return err
}
// Move the instance.
req := api.InstancePost{Name: inst.Name, Migration: true}
op, err = c.UseTarget(target).UseProject(proj).MigrateInstance(inst.Name, req)
if err != nil {
return err
}
err = op.Wait()
if err != nil {
return err
}
// Start the instance.
err = actionInstance(proj, inst, "start")
if err != nil {
return err
}
inst.Status = "Running"
return nil
}
// Go through the projects.
count := map[string]int64{}
for _, proj := range projects {
fmt.Printf("- %s\n", proj)
// Go through the instances.
instances, err := c.UseProject(proj).GetInstances(api.InstanceTypeAny)
if err != nil {
return err
}
for _, inst := range instances {
name := fmt.Sprintf(" - %s [%s]", inst.Name, inst.Location)
if inst.ExpandedConfig["user.evacuate"] == "false" {
name = fmt.Sprintf("%s[*]", name)
}
if inst.Status == "Stopped" {
name = fmt.Sprintf("%s[S]", name)
}
count[inst.Location]++
if inst.Location != srcHost {
originalLocation, ok := inst.ExpandedConfig["user.evacuate.origin"]
if ok && originalLocation != inst.Location && originalLocation != srcHost {
err := moveInstance(proj, inst, originalLocation)
if err != nil {
return err
}
fmt.Printf("%s => moved back to %s\n", name, originalLocation)
} else {
fmt.Printf("%s\n", name)
}
} else {
if inst.ExpandedConfig["user.evacuate"] == "false" {
err := actionInstance(proj, inst, "stop")
if err != nil {
return err
}
inst.Status = "Stopped"
fmt.Printf("%s => stopped\n", name)
} else {
target := randomMember()
err := moveInstance(proj, inst, target)
if err != nil {
return err
}
fmt.Printf("%s => moved to %s\n", name, target)
}
}
}
}
keys := []string{}
for k, _ := range count {
keys = append(keys, k)
}
sort.Strings(keys)
fmt.Printf("\nInstance count:\n")
for _, k := range keys {
v := count[k]
fmt.Printf(" - %s: %d\n", k, v)
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment