From 1c77ec7e63355cab48564a9fb49f34f55b5f0b15 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Tue, 15 Oct 2024 11:02:34 -0500 Subject: [PATCH] start from the command line works Signed-off-by: Jeff Carr --- Makefile | 3 ++- configfiles.go | 7 +++++-- event.go | 46 ++++++++++++++++++++++++++++++++++++++++++---- http.go | 26 ++++++++++++++++++++++++++ poll.go | 4 +++- structs.go | 14 ++++++++------ 6 files changed, 86 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 3443eda..ad8f8b2 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,8 @@ all: ./virtigo --hosts farm01 farm02 farm03 start: - curl --silent http://localhost:8080/start?start=jcarr + curl --silent http://localhost:8080/start?start=rdate.wit.com + @#curl --silent http://localhost:8080/start?start=jcarr @# ./virtigo --start jcarr curl-uptime: diff --git a/configfiles.go b/configfiles.go index b23cb0d..1bdf153 100644 --- a/configfiles.go +++ b/configfiles.go @@ -32,13 +32,16 @@ func readDropletFile(filename string) { // this is a new unknown droplet (not in the config file) d = new(DropletT) d.Hostname = name - if len(fields) < 2 || fields[1] != "ON" { + if len(fields) > 1 && fields[1] != "ON" { d.State = "OFF" } else { d.State = "ON" } + if len(fields) >= 3 { + d.hyperPreferred = fields[2] + } me.droplets = append(me.droplets, d) - log.Log(EVENT, "NEW CONFIG DROPLET", d.Hostname, d.State) + log.Log(EVENT, "NEW CONFIG DROPLET", d.Hostname, d.State, d.hyperPreferred) } else { log.Info("not sure what to do here. duplicate droplet", name, "in config file") } diff --git a/event.go b/event.go index ee4bef0..2956b0e 100644 --- a/event.go +++ b/event.go @@ -23,8 +23,46 @@ func (h *HyperT) RestartDaemon() { me.killcount += 1 } -func (h *HyperT) Start(d *DropletT) { - url := "http://" + h.Hostname + ":2520/start?" + d.Hostname - s := shell.Wget(url) - log.Info("EVENT start droplet", url, s) +// checks if the cluster is ready and stable +func clusterReady() bool { + last := time.Since(me.unstable) + if last > 133*time.Second { + // the cluster has not been stable for 133 seconds + log.Warn("clusterReady() is stable for 133s") + return true + } + log.Warn("clusterReady() is unstable for", shell.FormatDuration(last)) + return false +} + +func (d *DropletT) dropletReady() bool { + if d.CurrentState == "ON" { + log.Warn("EVENT start droplet is already ON") + return false + } + if d.starts > 2 { + log.Warn("EVENT start droplet has already been started", d.starts, "times") + return false + } + return true +} + +func (h *HyperT) Start(d *DropletT) { + if ! clusterReady() { + return + } + if ! d.dropletReady() { + return + } + + url := "http://" + h.Hostname + ":2520/start?start=" + d.Hostname + s := shell.Wget(url) + log.Warn("EVENT start droplet url:", url) + log.Warn("EVENT start droplet response:", s) + + // increment the counter for a start attempt working + d.starts += 1 + + // mark the cluster as unstable so droplet starts can be throttled + me.unstable = time.Now() } diff --git a/http.go b/http.go index 5f5297a..e998203 100644 --- a/http.go +++ b/http.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "math/rand" "net/http" "strings" "time" @@ -98,10 +99,35 @@ func okHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "can't start unknown droplet", start) return } + + // make the list of hypervisors that are active and can start new droplets + var pool []*HyperT for _, h := range me.hypers { fmt.Fprintln(w, "could start droplet on", start, "on", h.Hostname, h.Active) + if d.hyperPreferred == h.Hostname { + // the config file says this droplet should run on this hypervisor + h.Start(d) + return + } + if h.Active != true { + continue + } + pool = append(pool, h) } + // left here as an example of how to actually do random numbers + // it's complete mathematical chaos. Randomness is simple when + // human interaction occurs -- which is exactly what happens most + // of the time. most random shit is bullshit. all you really need + // is exactly this to make sure the random functions work as they + // should. Probably, just use this everywhere in all cases. --jcarr + rand.Seed(time.Now().UnixNano()) + a := 0 + b := len(pool) + n := a + rand.Intn(b-a) + fmt.Fprintln(w, "pool has", len(pool), "members", "rand =", n) + h := pool[n] + h.Start(d) return } diff --git a/poll.go b/poll.go index 1497509..bc3ea49 100644 --- a/poll.go +++ b/poll.go @@ -87,6 +87,7 @@ func clusterHealthy() (bool, string) { var failed int var missing int var unknown int + var unknownList []string for _, d := range me.droplets { total += 1 @@ -97,6 +98,7 @@ func clusterHealthy() (bool, string) { if d.CurrentState == "" { // log.Info("SKIP. hostname has not been polled yet", d.Hostname, d.hname) unknown += 1 + unknownList = append(unknownList, d.Hostname) continue } if d.CurrentState != "ON" { @@ -129,7 +131,7 @@ func clusterHealthy() (bool, string) { summary += fmt.Sprintf("missing = %d ", missing) } if unknown > 0 { - summary += fmt.Sprintf("unknown = %d ", unknown) + summary += fmt.Sprintf("unknown = %d ", unknown, unknownList) } if failed > 0 { summary += fmt.Sprintf("failed = %d ", failed) diff --git a/structs.go b/structs.go index ca77a7c..80f493b 100644 --- a/structs.go +++ b/structs.go @@ -37,10 +37,12 @@ type HyperT struct { // the stuff that is needed for a hypervisor type DropletT struct { - Hostname string // the name of the virtual machine. should be unique (probably enforce this forever) - State string // what the state of the droplet is SUPPOSED TO BE - CurrentState string // what the state of the droplet is ACTUALLY IS - hname string // the hypervisor it's currently running on - h *HyperT // the hypervisor it's currently running on - lastpoll time.Time // the last time the droplet was seen running + Hostname string // the name of the virtual machine. should be unique (probably enforce this forever) + State string // what the state of the droplet is SUPPOSED TO BE + CurrentState string // what the state of the droplet is ACTUALLY IS + hyperPreferred string // the hypervisor to prefer to run the droplet on + hname string // the hypervisor it's currently running on + h *HyperT // the hypervisor it's currently running on + lastpoll time.Time // the last time the droplet was seen running + starts int // how many times a start event has been attempted }