diff --git a/add.go b/add.go index e1c0298..1543341 100644 --- a/add.go +++ b/add.go @@ -36,8 +36,10 @@ func (all *Droplets) oldFindDroplet(name string) *Droplet { return nil } -func (c *Cluster) FindDropletByName(name string) *Droplet { - for _, d := range c.Droplets { +func (c *NewCluster) FindDropletByName(name string) *Droplet { + loop := c.DropletsAll() // get the list of droplets + for loop.Scan() { + d := loop.Droplet() if d.Hostname == name { return d } @@ -45,8 +47,8 @@ func (c *Cluster) FindDropletByName(name string) *Droplet { return nil } -func (c *Cluster) FindHypervisorByName(name string) *Hypervisor { - for _, h := range c.Hypervisors { +func (c *NewCluster) FindHypervisorByName(name string) *Hypervisor { + for _, h := range c.h.Hypervisors { if h.Hostname == name { return h } @@ -54,7 +56,7 @@ func (c *Cluster) FindHypervisorByName(name string) *Hypervisor { return nil } -func (c *Cluster) AddHypervisor(hostname string, cpus int, mem int) *Hypervisor { +func (c *NewCluster) AddHypervisor(hostname string, cpus int, mem int) *Hypervisor { h := c.FindHypervisorByName(hostname) if h != nil { return h @@ -71,11 +73,11 @@ func (c *Cluster) AddHypervisor(hostname string, cpus int, mem int) *Hypervisor h.Cpus = 1 } h.SetMemoryGB(mem * 32) - c.Hypervisors = append(c.Hypervisors, h) + c.h.Hypervisors = append(c.h.Hypervisors, h) return h } -func (c *Cluster) AddDroplet(uuid string, hostname string, cpus int, mem int) *Droplet { +func (c *NewCluster) AddDroplet(uuid string, hostname string, cpus int, mem int) *Droplet { d := c.FindDropletByName(hostname) if d != nil { return d @@ -91,7 +93,7 @@ func (c *Cluster) AddDroplet(uuid string, hostname string, cpus int, mem int) *D d.Cpus = 1 } d.Memory = SetGB(mem * 32) - c.Droplets = append(c.Droplets, d) + c.d.Droplets = append(c.d.Droplets, d) return d } @@ -121,8 +123,10 @@ func HumanFormatBytes(b int64) string { return fmt.Sprintf("%d TB", tb) } -func (c *Cluster) BlankFields() { - for _, d := range c.Droplets { +func (c *NewCluster) BlankFields() { + loop := c.DropletsAll() // get the list of droplets + for loop.Scan() { + d := loop.Droplet() d.Current = nil } } @@ -131,7 +135,7 @@ func (epb *Events) AppendEvent(e *Event) { epb.Events = append(epb.Events, e) } -func (c *Cluster) ClusterStable() (bool, string) { +func (c *NewCluster) ClusterStable() (bool, string) { last := time.Since(c.Unstable.AsTime()) if last > c.UnstableTimeout.AsDuration() { // the cluster has not been stable for 133 seconds @@ -143,7 +147,7 @@ func (c *Cluster) ClusterStable() (bool, string) { } // check the cluster and droplet to make sure it's ready to start -func (c *Cluster) DropletReady(d *Droplet) (bool, string) { +func (c *NewCluster) DropletReady(d *Droplet) (bool, string) { if c == nil { return false, "cluster == nil" } diff --git a/change.go b/change.go index 25cd57b..dc77b0c 100644 --- a/change.go +++ b/change.go @@ -94,8 +94,6 @@ func NewAddEvent(a any, fname string, newval any) *Event { var d *Droplet d = a.(*Droplet) e.Droplet = d.Hostname - case *Cluster: - e.Droplet = "Cluster" case nil: e.Droplet = "" default: @@ -131,7 +129,7 @@ func (d *Droplet) SetCpus(b int64) { } // records an event that the droplet changed state (aka turned on, turned off, etc) -func (c *Cluster) ChangeDropletState(d *Droplet, newState DropletState) error { +func (c *NewCluster) ChangeDropletState(d *Droplet, newState DropletState) error { if c == nil { return errors.New("cluster is nil") } @@ -153,12 +151,12 @@ func (c *Cluster) ChangeDropletState(d *Droplet, newState DropletState) error { now := time.Now() e.Start = timestamppb.New(now) - c.E.Events = append(c.E.Events, e) + c.e.Events = append(c.e.Events, e) return nil } // records an event that the droplet migrated to another hypervisor -func (c *Cluster) DropletMoved(d *Droplet, newh *Hypervisor) error { +func (c *NewCluster) DropletMoved(d *Droplet, newh *Hypervisor) error { if c == nil { return errors.New("cluster is nil") } @@ -185,7 +183,7 @@ func (c *Cluster) DropletMoved(d *Droplet, newh *Hypervisor) error { now := time.Now() e.Start = timestamppb.New(now) - c.E.Events = append(c.E.Events, e) + c.e.Events = append(c.e.Events, e) // update the droplet record d.Current.Hypervisor = newh.Hostname diff --git a/cluster.proto b/cluster.proto index 8658597..9569465 100644 --- a/cluster.proto +++ b/cluster.proto @@ -7,7 +7,7 @@ import "event.proto"; import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp import "google/protobuf/duration.proto"; // Import the well-known type for Timestamp -message Cluster { +message OldCluster { int64 id = 1; repeated string dirs = 2; @@ -15,7 +15,7 @@ message Cluster { repeated Hypervisor hypervisors = 4; // repeated Event events = 5; - Droplets d = 6; + // Droplets d = 6; Hypervisors h = 7; Events e = 8; google.protobuf.Timestamp unstable = 9; // the last time we heard anything from this droplet diff --git a/config.go b/config.go index 7973da9..8fc9d27 100644 --- a/config.go +++ b/config.go @@ -16,7 +16,7 @@ import ( // writes out the cluster information it seperate files // to make it humanly possible to hand edit things as needed -func (c *Cluster) ConfigSave() error { +func (c *NewCluster) ConfigSave() error { // try to backup the current cluster config files if err := backupConfig(); err != nil { return err @@ -25,9 +25,8 @@ func (c *Cluster) ConfigSave() error { // make a new droplets struct var d *Droplets d = new(Droplets) - d.Droplets = c.Droplets // copy all the records over to the new struct - for _, drop := range c.Droplets { + for _, drop := range c.d.Droplets { d.Droplets = append(d.Droplets, drop) } // delete all the Current data so it's not put in the config file @@ -43,83 +42,42 @@ func (c *Cluster) ConfigSave() error { return err } - var h *Hypervisors - h = new(Hypervisors) - h.Hypervisors = c.Hypervisors - if err := ConfigWriteJSON(h, "hypervisors.json"); err != nil { + if err := ConfigWriteJSON(c.h, "hypervisors.json"); err != nil { fmt.Println("hypervisors.json write failed") return err } - if err := ConfigWriteTEXT(h, "hypervisors.text"); err != nil { + if err := ConfigWriteTEXT(c.h, "hypervisors.text"); err != nil { fmt.Println("hypervisors.json write failed") return err } - if err := ConfigWriteJSON(c.E, "events.json"); err != nil { + if err := ConfigWriteJSON(c.e, "events.json"); err != nil { fmt.Println("events.json write failed") return err } - if err := ConfigWriteTEXT(c.E, "events.text"); err != nil { + if err := ConfigWriteTEXT(c.e, "events.text"); err != nil { fmt.Println("events.json write failed") return err } - - if err := ConfigWriteTEXT(c, "cluster.full.text"); err != nil { - fmt.Println("Cluster.json write failed") - return err - } - - var newc Cluster - newc.Dirs = c.Dirs - newc.Droplets = nil - newc.Hypervisors = nil - newc.E = nil - if err := ConfigWriteTEXT(&newc, "cluster.text"); err != nil { - fmt.Println("cluster.json write failed") - return err - } - return nil } -func (c *Cluster) ConfigLoad() error { +func (c *NewCluster) ConfigLoad() error { if c == nil { return errors.New("It's not safe to run ConfigLoad() on a nil cluster") } - // load the cluster config file - if data, err := loadFile("cluster.text"); err == nil { - if err = prototext.Unmarshal(data, c); err != nil { - fmt.Println("broken cluster.text config file") - fmt.Println(err) - return errors.New("cluster.text file is broken") - } - } else { - return err - } - - var d *Droplets - d = new(Droplets) - // load the droplet config file if data, err := loadFile("droplets.json"); err == nil { - if err = protojson.Unmarshal(data, d); err != nil { + if err = protojson.Unmarshal(data, c.d); err != nil { fmt.Println("broken droplets.json config file") return err } } else { return err } - // copy them over. is this needed? does the memory free otherwise? - // also set initial values - for _, drop := range d.Droplets { - c.Droplets = append(c.Droplets, drop) - } - var h *Hypervisors - h = new(Hypervisors) - // load the hypervisors config file if data, err := loadFile("hypervisors.json"); err == nil { - if err = protojson.Unmarshal(data, h); err != nil { + if err = protojson.Unmarshal(data, c.h); err != nil { fmt.Println("broken hypervisors.json config file") return err } @@ -127,26 +85,15 @@ func (c *Cluster) ConfigLoad() error { fmt.Println("ERROR HERE IN Hypervisors") return err } - // copy them over. is this needed? does the memory free otherwise? - for _, a := range h.Hypervisors { - c.Hypervisors = append(c.Hypervisors, a) - } - var e *Events - e = new(Events) - - if c.E == nil { + if c.e == nil { // this seems to panic on nil. something is wrong about doing this // does it not stay allocated after this function ends? - c.E = new(Events) + c.e = new(Events) } - if err := e.loadEvents(); err != nil { + if err := c.e.loadEvents(); err != nil { return err } - // copy them over. is this needed? does the memory free otherwise? - for _, e := range e.Events { - c.E.Events = append(c.E.Events, e) - } return nil } diff --git a/droplet.proto b/droplet.proto index adf1299..f52fa7f 100644 --- a/droplet.proto +++ b/droplet.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package virtbuf; +import "google/protobuf/duration.proto"; // Import the well-known type for Timestamp import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp message Droplets { @@ -27,7 +28,10 @@ message Droplet { string local_only = 14; // this is only defined locally on the hypervisor string custom_xml = 15; // if needed, - DropletArchive archive = 16; // what the state of the droplet is SUPPOSED TO BE ('on' or 'off') + Archive archive = 16; // what the state of the droplet is SUPPOSED TO BE ('on' or 'off') + + google.protobuf.Timestamp unstable = 39; // the last time we heard anything from this droplet + google.protobuf.Duration unstable_timeout = 40; // the last time we heard anything from this droplet } // volatile data. the current settings and values of things. @@ -58,8 +62,8 @@ enum DropletState { } enum DropletArchive { - ARCHIVE_DUP = 0; - ARCHIVE_USER = 1; + DUP = 0; + USER = 1; } message Network { diff --git a/example/configfile b/example/configfile deleted file mode 100755 index 81377e3..0000000 Binary files a/example/configfile and /dev/null differ diff --git a/example/main.go b/example/main.go index d54803d..8327628 100644 --- a/example/main.go +++ b/example/main.go @@ -1,15 +1,11 @@ package main import ( - "bufio" - "bytes" "fmt" "io/ioutil" "log" "os" - "google.golang.org/protobuf/proto" - pb "go.wit.com/lib/protobuf/virtbuf" ) @@ -20,38 +16,43 @@ import ( func main() { TestWriteCluster() - in, err := ioutil.ReadFile("/tmp/testing4.protobuf") + _, err := ioutil.ReadFile("/tmp/testing4.protobuf") if err != nil { log.Fatalln("Error reading file:", err) } - var aCluster pb.Cluster - if err := proto.Unmarshal(in, &aCluster); err != nil { - log.Fatalln("Failed to parse droplet:", err) - } + var c *pb.NewCluster + c = new(pb.NewCluster) - log.Println(aCluster.String()) + // log.Println(aCluster.String()) // show the droplets to STDOUT - for _, d := range aCluster.Droplets { + loop := c.DropletsAll() // get the list of droplets + for loop.Scan() { + d := loop.Droplet() fmt.Println("\tdroplet =", d.Hostname, "preffered host:", d.PreferredHypervisor) } - // show the hypervisors to STDOUT - for _, h := range aCluster.Hypervisors { - fmt.Println("\thypervisor =", h.Hostname, h.GetMemoryPrintable()) - } + /* + // show the hypervisors to STDOUT + for _, h := range aCluster.Hypervisors { + fmt.Println("\thypervisor =", h.Hostname, h.GetMemoryPrintable()) + } + */ - json := aCluster.FormatJSON() - fmt.Println(json) + /* + json := aCluster.FormatJSON() + fmt.Println(json) - data, _ := aCluster.MarshalJSON() - fmt.Println(string(data)) + data, _ := aCluster.MarshalJSON() + fmt.Println(string(data)) - text := aCluster.FormatTEXT() - fmt.Println(text) + text := aCluster.FormatTEXT() + fmt.Println(text) + */ } -func marshalWriteToFile(myWriter *bufio.Writer, c *pb.Cluster) { +/* +func marshalWriteToFile(myWriter *bufio.Writer, c *pb.NewCluster) { buf, err := proto.Marshal(c) if err != nil { log.Fatal("marshaling error: ", err) @@ -65,22 +66,20 @@ func marshalWriteToFile(myWriter *bufio.Writer, c *pb.Cluster) { myWriter.Flush() log.Println("bufio.Write() tmp2, err = ", tmp2, err) } +*/ func TestWriteCluster() { - buf := new(bytes.Buffer) - c := pb.CreateSampleCluster(7) + os.Setenv("VIRTIGO_HOME", "/tmp/virtigo/") - got := buf.String() - log.Println(got) - - newfile, _ := os.Create("/tmp/testing4.protobuf") - myWriter := bufio.NewWriter(newfile) - marshalWriteToFile(myWriter, c) - + if err := c.ConfigSave(); err != nil { + fmt.Println("configsave error", err) + os.Exit(-1) + } // marshalUnmarshal() } +/* func marshalUnmarshal() { test := pb.CreateSampleCluster(7) data, err := proto.Marshal(test) @@ -96,3 +95,4 @@ func marshalUnmarshal() { log.Println("proto.Marshal() and proto.Unmarshal() worked") } } +*/ diff --git a/helpers.go b/helpers.go index 6a8fe69..72d4008 100644 --- a/helpers.go +++ b/helpers.go @@ -10,10 +10,6 @@ import ( ) // human readable JSON -func (c *Cluster) FormatJSON() string { - return protojson.Format(c) -} - func (d *Droplets) FormatJSON() string { return protojson.Format(d) } @@ -33,10 +29,6 @@ func (h *Hypervisors) FormatJSON() string { // apparently this isn't supposed to be used? // https://protobuf.dev/reference/go/faq/#unstable-text // this is a shame because this is much nicer output than JSON Format() -func (c *Cluster) FormatTEXT() string { - return prototext.Format(c) -} - func (d *Droplets) FormatTEXT() string { return prototext.Format(d) } @@ -46,10 +38,6 @@ func (e *Events) FormatTEXT() string { } // marshal -func (c *Cluster) MarshalJSON() ([]byte, error) { - return protojson.Marshal(c) -} - func (d *Droplets) MarshalJSON() ([]byte, error) { return protojson.Marshal(d) } @@ -63,10 +51,6 @@ func (e *Events) MarshalJSON() ([]byte, error) { } // unmarshal -func (c *Cluster) UnmarshalJSON(data []byte) error { - return protojson.Unmarshal(data, c) -} - func (d *Droplets) UnmarshalJSON(data []byte) error { return protojson.Unmarshal(data, d) } diff --git a/newCluster.go b/newCluster.go new file mode 100644 index 0000000..245638d --- /dev/null +++ b/newCluster.go @@ -0,0 +1,19 @@ +package virtbuf + +import ( + sync "sync" + + durationpb "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +type NewCluster struct { + sync.RWMutex + + Dirs []string + d *Droplets + h *Hypervisors + e *Events + Unstable *timestamppb.Timestamp + UnstableTimeout *durationpb.Duration +} diff --git a/sampleData.go b/sampleData.go index 0085822..dedd7c0 100644 --- a/sampleData.go +++ b/sampleData.go @@ -67,9 +67,12 @@ func CreateSampleEvents(total int) *Events { return e } -func CreateSampleCluster(total int) *Cluster { - var c *Cluster - c = new(Cluster) +func CreateSampleCluster(total int) *NewCluster { + var c *NewCluster + c = new(NewCluster) + c.d = new(Droplets) + c.h = new(Hypervisors) + c.e = new(Events) for i := 0; i < total; i++ { hostname := fmt.Sprintf("bmath%d.wit.com", i) @@ -80,7 +83,7 @@ func CreateSampleCluster(total int) *Cluster { d.Memory = SetGB(256) } - c.Droplets = append(c.Droplets, d) + c.d.Droplets = append(c.d.Droplets, d) } for i := 0; i < 3; i++ { @@ -88,7 +91,7 @@ func CreateSampleCluster(total int) *Cluster { h := CreateSampleHypervisor(hostname, i+1) h.Comment = fmt.Sprintf("Sample hypervisor %d", i) - c.Hypervisors = append(c.Hypervisors, h) + c.h.Hypervisors = append(c.h.Hypervisors, h) } return c diff --git a/scanIterator.go b/scanIterator.go new file mode 100644 index 0000000..3bbaeef --- /dev/null +++ b/scanIterator.go @@ -0,0 +1,106 @@ +package virtbuf + +import ( + "fmt" + "os" + + "go.wit.com/log" +) + +type DropletIterator struct { + droplets []*Droplet + index int +} + +// NewDropletIterator initializes a new iterator. +func NewDropletIterator(droplets []*Droplet) *DropletIterator { + return &DropletIterator{droplets: droplets} +} + +// Scan moves to the next element and returns false if there are no more droplets. +func (it *DropletIterator) Scan() bool { + if it.index >= len(it.droplets) { + return false + } + it.index++ + return true +} + +// Droplet returns the current droplet. +func (it *DropletIterator) Droplet() *Droplet { + if it.droplets[it.index-1] == nil { + for i, d := range it.droplets { + fmt.Println("i =", i, d) + } + fmt.Println("len =", len(it.droplets)) + fmt.Println("droplet == nil", it.index, it.index-1) + os.Exit(-1) + } + return it.droplets[it.index-1] +} + +// Use Scan() in a loop, similar to a while loop +// +// for iterator.Scan() { +// d := iterator.Droplet() +// fmt.Println("Droplet UUID:", d.Uuid) +// } +func (c *NewCluster) GetDropletIterator() *DropletIterator { + dropletPointers := c.SelectDropletPointers() + + iterator := NewDropletIterator(dropletPointers) + + return iterator +} + +func (c *NewCluster) DropletsAll() *DropletIterator { + dropletPointers := c.SelectDropletAll() + + iterator := NewDropletIterator(dropletPointers) + + return iterator +} + +// SelectDropletPointers safely returns a slice of pointers to Droplet records. +func (c *NewCluster) SelectDropletAll() []*Droplet { + c.RLock() + defer c.RUnlock() + + // Create a new slice to hold pointers to each Droplet + // dropletPointers := make([]*Droplet, len(c.E.Droplets)) + var dropletPointers []*Droplet + if c.d == nil { + log.Info("SelectDropletsAll() c.d == nil") + os.Exit(-1) + } + for _, d := range c.d.Droplets { + if d == nil { + continue + } + if d.Archive != nil { + continue + } + dropletPointers = append(dropletPointers, d) // Copy pointers for safe iteration + } + + return dropletPointers +} + +// SelectDropletPointers safely returns a slice of pointers to Droplet records. +func (c *NewCluster) SelectDropletPointers() []*Droplet { + c.RLock() + defer c.RUnlock() + + // Create a new slice to hold pointers to each Droplet + // dropletPointers := make([]*Droplet, len(c.E.Droplets)) + dropletPointers := make([]*Droplet, 1) + if c.d == nil { + log.Info("c.d == nil") + os.Exit(-1) + } + for _, d := range c.d.Droplets { + dropletPointers = append(dropletPointers, d) // Copy pointers for safe iteration + } + + return dropletPointers +}