diff --git a/add.go b/add.go index 6813aa8..d4bd08a 100644 --- a/add.go +++ b/add.go @@ -22,11 +22,6 @@ func (x *Hypervisor) SetMemoryGB(gb int) { x.Memory = int64(gb * 1024 * 1024 * 1024) } -func (x *Hypervisor) GetMemoryPrintable() string { - i := x.Memory / (1024 * 1024 * 1024) - return fmt.Sprintf("%d GB", i) -} - func (all *Droplets) oldFindDroplet(name string) *Droplet { for _, d := range all.Droplets { if d.Hostname == name { @@ -111,7 +106,7 @@ func (c *NewCluster) AddDropletLocal(name string, hypername string) *Droplet { d := &Droplet{ Hostname: name, } - d.LocalOnly = hypername + d.LocalOnly = "yes on: " + hypername d.Current = new(Current) d.Current.Hypervisor = hypername d.StartState = DropletState_OFF @@ -121,32 +116,6 @@ func (c *NewCluster) AddDropletLocal(name string, hypername string) *Droplet { return d } -// This isn't for the marketing department -// so this isn't going to use 'MiB' and 'GiB' -func HumanFormatBytes(b int64) string { - if b < 2000 { - return fmt.Sprintf("%d B", b) - } - - kb := int(b / 1024) - if kb < 2000 { - return fmt.Sprintf("%d KB", kb) - } - - mb := int(b / (1024 * 1024)) - if mb < 2000 { - return fmt.Sprintf("%d MB", mb) - } - - gb := int(b / (1024 * 1024 * 1024)) - if gb < 2000 { - return fmt.Sprintf("%d GB", gb) - } - - tb := int(b / (1024 * 1024 * 1024 * 1024)) - return fmt.Sprintf("%d TB", tb) -} - func (c *NewCluster) BlankFields() { loop := c.DropletsAll() // get the list of droplets for loop.Scan() { diff --git a/change.go b/change.go index dc77b0c..04c5b20 100644 --- a/change.go +++ b/change.go @@ -49,6 +49,14 @@ func convertToString(x any) string { return fmt.Sprintf("%d", x.(int)) case uint: return fmt.Sprintf("%d", x.(uint)) + case *DropletState: + var s *DropletState + s = x.(*DropletState) + return s.String() + case DropletState: + var s DropletState + s = x.(DropletState) + return s.String() case bool: if x.(bool) { return "true" @@ -128,6 +136,34 @@ func (d *Droplet) SetCpus(b int64) { log.Info("Set the number of cpus for the droplet", b) } +// update the droplet memory +func (d *Droplet) SetState(newState DropletState) { + if d.Current == nil { + d.Current = new(Current) + } + if d.Current.State == newState { + // nothing has changed + return + } + switch newState { + case DropletState_ON: + d.Current.OnSince = timestamppb.New(time.Now()) + d.Current.OffSince = nil + case DropletState_OFF: + d.Current.OffSince = timestamppb.New(time.Now()) + d.Current.OnSince = nil + default: + // zero on OnSince to indicate something hickup'd? + // not sure if this should be done here. probably trust qemu dom0 instead + // but I can't do that right now so for now this will work + d.Current.OnSince = timestamppb.New(time.Now()) + d.Current.OffSince = timestamppb.New(time.Now()) + } + d.Current.State = newState + d.NewChangeEvent("STATE", d.Current.State, newState) + log.Info("Droplet", d.Hostname, "changed state from", d.Current.State, "to", newState) +} + // records an event that the droplet changed state (aka turned on, turned off, etc) func (c *NewCluster) ChangeDropletState(d *Droplet, newState DropletState) error { if c == nil { diff --git a/droplet.proto b/droplet.proto index f52fa7f..dd1b30a 100644 --- a/droplet.proto +++ b/droplet.proto @@ -44,6 +44,8 @@ message Current { string full_xml = 4; // the full libvirt xml to import google.protobuf.Timestamp last_poll = 5; // the last time we heard anything from this droplet string image_url = 6; // url to the image + google.protobuf.Timestamp off_since = 7; // when the droplet was turned off + google.protobuf.Timestamp on_since = 8; // when the droplet was turned on } message Archive { diff --git a/human.go b/human.go new file mode 100644 index 0000000..c6a2fbc --- /dev/null +++ b/human.go @@ -0,0 +1,157 @@ +package virtbuf + +// mostly just functions related to making STDOUT +// more readable by us humans + +// also function shortcuts the do limited formatting (haha, who remembers COBOL?) +// so reporting tables of the status of what droplets and hypervisors +// are in text columns and rows that can be easily read in a terminal + +import ( + "fmt" + "strings" + "time" +) + +func oldGetDurationStamp(t time.Time) string { + // Get the current time + currentTime := time.Now() + + // Calculate the duration between t current time + duration := currentTime.Sub(t) + + return FormatDuration(duration) +} + +// This isn't for the marketing department +// so this isn't going to use 'MiB' and 'GiB' +func HumanFormatBytes(b int64) string { + if b < 2000 { + return fmt.Sprintf("%d B", b) + } + + kb := int(b / 1024) + if kb < 2000 { + return fmt.Sprintf("%d KB", kb) + } + + mb := int(b / (1024 * 1024)) + if mb < 2000 { + return fmt.Sprintf("%d MB", mb) + } + + gb := int(b / (1024 * 1024 * 1024)) + if gb < 2000 { + return fmt.Sprintf("%d GB", gb) + } + + tb := int(b / (1024 * 1024 * 1024 * 1024)) + return fmt.Sprintf("%d TB", tb) +} + +func FormatDuration(d time.Duration) string { + result := "" + + // check if it's more than a year + years := int(d.Hours()) / (24 * 365) + if years > 0 { + result += fmt.Sprintf("%dy", years) + return result + } + + // check if it's more than a day + days := int(d.Hours()) / 24 + if days > 0 { + result += fmt.Sprintf("%dd", days) + return result + } + + // check if it's more than an hour + hours := int(d.Hours()) % 24 + if hours > 0 { + result += fmt.Sprintf("%dh", hours) + return result + } + + // check if it's more than a minute + minutes := int(d.Minutes()) % 60 + if minutes > 0 { + result += fmt.Sprintf("%dm", minutes) + return result + } + + // check if it's more than a second + seconds := int(d.Seconds()) % 60 + if seconds > 0 { + result += fmt.Sprintf("%ds", seconds) + return result + } + + // report in milliseconds + ms := int(d.Milliseconds()) + if ms > 100 { + // todo: print .3s, etc ? + return fmt.Sprintf("%1.2fs", seconds/1000) + } + if ms > 0 { + result += fmt.Sprintf("%dms", ms) + } + + // totally not necessary but wth + var t time.Duration + t = time.Duration(ms) * time.Millisecond + nanos := d - t + result += fmt.Sprintf("%dnanos", nanos) + return result +} + + +func (d *Droplet) SprintHeader() string { + header := fmt.Sprintf("%-3.3s %-9.9s %-20.20s", d.Current.State, d.Current.Hypervisor, d.Hostname) + + if d.Current == nil { + d.Current = new(Current) + } + + switch d.Current.State { + case DropletState_ON: + dur := time.Since(d.Current.OnSince.AsTime()) // time since 'ON' + header += " (on :" + FormatDuration(dur) + ")" + case DropletState_OFF: + // everything is as it should be with this vm + dur := time.Since(d.Current.OffSince.AsTime()) // time since 'OFF' + header += " (off:" + FormatDuration(dur) + ")" + default: + dur := time.Since(d.Current.OffSince.AsTime()) // use 'OFF' here? + header += " (???:" + FormatDuration(dur) + ")" + } + return header +} + +func (d *Droplet) SprintDumpHeader() string { + var macs []string + for _, n := range d.Networks { + macs = append(macs, n.Mac) + } + + // this line in golang could replace 80 lines of COBOL + header := fmt.Sprintf("%-4.4s%20s %-8s", d.Current.State, strings.Join(macs, " "), d.Current.Hypervisor) + + if d.Current == nil { + d.Current = new(Current) + } + + switch d.Current.State { + case DropletState_ON: + dur := time.Since(d.Current.OnSince.AsTime()) // time since 'ON' + header += " (on :" + FormatDuration(dur) + ")" + case DropletState_OFF: + // everything is as it should be with this vm + dur := time.Since(d.Current.OffSince.AsTime()) // time since 'OFF' + header += " (off:" + FormatDuration(dur) + ")" + default: + dur := time.Since(d.Current.OffSince.AsTime()) // use 'OFF' here? + header += " (???:" + FormatDuration(dur) + ")" + } + return header +} diff --git a/time.go b/time.go deleted file mode 100644 index 28f4098..0000000 --- a/time.go +++ /dev/null @@ -1,64 +0,0 @@ -package virtbuf - -import ( - "fmt" - "time" -) - -func FormatDuration(d time.Duration) string { - result := "" - - // check if it's more than a year - years := int(d.Hours()) / (24 * 365) - if years > 0 { - result += fmt.Sprintf("%dy ", years) - return result - } - - // check if it's more than a day - days := int(d.Hours()) / 24 - if days > 0 { - result += fmt.Sprintf("%dd ", days) - return result - } - - // check if it's more than an hour - hours := int(d.Hours()) % 24 - if hours > 0 { - result += fmt.Sprintf("%dh ", hours) - return result - } - - // check if it's more than a minute - minutes := int(d.Minutes()) % 60 - if minutes > 0 { - result += fmt.Sprintf("%dm ", minutes) - return result - } - - // check if it's more than a second - seconds := int(d.Seconds()) % 60 - if seconds > 0 { - result += fmt.Sprintf("%ds", seconds) - return result - } - - // report in milliseconds - ms := int(d.Milliseconds()) - if ms > 100 { - // todo: print .3s, etc ? - return fmt.Sprintf("%1.2fs", seconds/1000) - } - result += fmt.Sprintf("%dms", ms) - return result -} - -func GetDurationStamp(t time.Time) string { - // Get the current time - currentTime := time.Now() - - // Calculate the duration between t current time - duration := currentTime.Sub(t) - - return FormatDuration(duration) -}