// Copyright 2024 WIT.COM Inc Licensed GPL 3.0 package main import ( "errors" "fmt" pb "go.wit.com/lib/protobuf/virtbuf" "go.wit.com/log" "libvirt.org/go/libvirtxml" ) // import a libvirt xml file func addDomainDroplet(domcfg *libvirtxml.Domain) (*DropletT, error) { if domcfg == nil { return nil, errors.New("domcfg == nil") } d, _ := findDomain(domcfg) if d != nil { return d, errors.New(d.pb.Hostname + " droplet exists. need to update instead") } // this is a new unknown droplet (not in the config file) d = new(DropletT) d.pb = me.cluster.AddDroplet(domcfg.UUID, domcfg.Name, 2, 2*1024*1024) d.pb.StartState = pb.DropletState_OFF d.CurrentState = pb.DropletState_UNKNOWN me.droplets = append(me.droplets, d) me.changed = true if updateDroplet(d, domcfg) { if me.changed { log.Info("updateDroplet() worked. droplet changed") } else { log.Verbose("updateDroplet() worked. nothing changed") } } else { log.Info("updateDroplet() failed for", d.pb.Hostname) return d, errors.New("update failed for " + domcfg.Name) } log.Info("added new droplet", domcfg.Name, domcfg.UUID) dumpNonStandardXML(domcfg) return d, nil } func findDomain(domcfg *libvirtxml.Domain) (*DropletT, error) { var found *DropletT if domcfg == nil { return nil, errors.New("domcfg == nil") } for _, d := range me.droplets { if d.pb.Hostname == domcfg.Name { if d.pb.Uuid != domcfg.UUID { fmt.Println("CHANGED UUID", d.pb.Uuid, domcfg.UUID) d.pb.Uuid = domcfg.UUID me.changed = true } if found == nil { found = d } else { fmt.Println("FOUND TWICE", d.pb.Uuid, domcfg.Name, domcfg.UUID) return d, errors.New("Found Twice") } } if d.pb.Uuid == domcfg.UUID { if d.pb.Hostname != domcfg.Name { fmt.Println("protobuf has: UUID and Name:", d.pb.Uuid, d.pb.Hostname) fmt.Println("libvirt has: UUID and Name:", domcfg.UUID, domcfg.Name) fmt.Println("FOUND UUID WITH MIS-MATCHED NAME", domcfg.Name, domcfg.UUID) return d, errors.New("UUID with mis-matched names") } } } return found, nil } func updateDroplet(d *DropletT, domcfg *libvirtxml.Domain) bool { var ok bool = true if d == nil { return false } if domcfg == nil { return false } if !updateMemory(d, domcfg) { log.Info("updateMemory() failed") ok = false } // update arch & machine if (domcfg.OS != nil) && (domcfg.OS.Type != nil) { // OS Type: &{Arch:x86_64 Machine:pc-i440fx-5.2 Type:hvm} t := domcfg.OS.Type if d.pb.QemuArch != t.Arch { d.pb.QemuArch = t.Arch me.changed = true } if d.pb.QemuMachine != t.Machine { d.pb.QemuMachine = t.Machine me.changed = true } } // check cpus if d.pb.Cpus != int64(domcfg.VCPU.Value) { // fmt.Printf("cpus changed. VCPU = %+v\n", domcfg.VCPU) fmt.Printf("cpus changed. from %d to %d\n", d.pb.Cpus, domcfg.VCPU.Value) d.pb.Cpus = int64(domcfg.VCPU.Value) me.changed = true } // update spice port if domcfg.Devices.Graphics != nil { for _, g := range domcfg.Devices.Graphics { if g.Spice == nil { continue } var s *libvirtxml.DomainGraphicSpice s = g.Spice // fmt.Printf("Spice: %d %+v %s\n", i, s, s.AutoPort) if s.AutoPort == "yes" { // should ignore either way } else { if d.pb.SpicePort != int64(s.Port) { // print out, but ignore the port number d.pb.SpicePort = int64(s.Port) fmt.Printf("Spice Port set to = %d\n", s.Port) me.changed = true } } } } // check type if domcfg.Type != "kvm" { fmt.Printf("not kvm. Virt type == %s\n", domcfg.Type) ok = false } if !updateNetwork(d, domcfg) { log.Info("updateNetwork() failed") ok = false } if !updateDisk(d, domcfg) { log.Info("updateDisk() failed") ok = false } return ok } // returns false if something went wrong func updateMemory(d *DropletT, domcfg *libvirtxml.Domain) bool { if (d == nil) || (domcfg == nil) { return false } if domcfg.Memory == nil { return true } // check memory if domcfg.Memory.Unit == "KiB" { var m int64 m = int64(domcfg.Memory.Value * 1024) if d.pb.Memory != m { d.pb.Memory = m me.changed = true fmt.Printf("Memory changed %s to %d %s\n", pb.HumanFormatBytes(d.pb.Memory), domcfg.Memory.Value, domcfg.Memory.Unit) } return true } if domcfg.Memory.Unit == "MiB" { var m int64 m = int64(domcfg.Memory.Value * 1024 * 1024) if d.pb.Memory != m { d.pb.Memory = m me.changed = true fmt.Printf("Memory changed %s to %d %s\n", pb.HumanFormatBytes(d.pb.Memory), domcfg.Memory.Value, domcfg.Memory.Unit) } return true } if domcfg.Memory.Unit == "GiB" { var m int64 m = int64(domcfg.Memory.Value * 1024 * 1024 * 1024) if d.pb.Memory != m { d.pb.Memory = m me.changed = true fmt.Printf("Memory changed %s, %d %s\n", pb.HumanFormatBytes(d.pb.Memory), domcfg.Memory.Value, domcfg.Memory.Unit) } return true } fmt.Println("Unknown Memory Unit", domcfg.Memory.Unit) return false } // returns false if something went wrong func updateNetwork(d *DropletT, domcfg *libvirtxml.Domain) bool { if (d == nil) || (domcfg == nil) { return false } var macs []string // Iterate over the network interfaces and print the MAC addresses for _, iface := range domcfg.Devices.Interfaces { // fmt.Printf("iface: %+v\n", iface) // fmt.Printf("MAC: %+v\n", iface.MAC) // fmt.Printf("Source: %+v\n", iface.Source) // fmt.Printf("Bridge: %+v\n", iface.Source.Bridge) // fmt.Printf("Model: %+v\n", iface.Model) if iface.MAC != nil { // iface.MAC.Address = "aa:bb:aa:bb:aa:ff" // fmt.Printf("MAC Address: %+v\n", iface.MAC) // log.Info("Interface:", iface.Target, "MAC Address:", iface.MAC.Address) // fmt.Printf("source: %+v\n", iface.Source) macs = append(macs, iface.MAC.Address) } else { fmt.Printf("Interface Target: %+v, MAC Address not available\n", iface.Target) } } for _, mac := range macs { var found bool = false for i, eth := range d.pb.Networks { if eth.Mac == mac { log.Verbose("OKAY. FOUND ETH:", i, eth.Mac, eth.Name) found = true } } if !found { var eth *pb.Network eth = new(pb.Network) eth.Mac = mac eth.Name = "worldbr" d.pb.Networks = append(d.pb.Networks, eth) me.changed = true } } log.Verbose("mac addrs:", macs) return true } /* from vm3-with-nvme-1.5GB-sec.xml
*/ // returns false if something went wrong func updateDisk(d *DropletT, domcfg *libvirtxml.Domain) bool { if (d == nil) || (domcfg == nil) { return false } for _, disk := range domcfg.Devices.Disks { var t *libvirtxml.DomainDiskSourceFile t = disk.Source.File if t == nil { fmt.Println("disk.Source.File == nil") continue } filename := t.File if filename == "" { fmt.Println("No disk source file found.") continue } var found bool = false for _, disk := range d.pb.Disks { if disk.Filename == filename { log.Verbose("OKAY. FOUND filename", filename) found = true } } if !found { var disk *pb.Disk disk = new(pb.Disk) disk.Filename = filename d.pb.Disks = append(d.pb.Disks, disk) log.Info("New filename", filename) me.changed = true } } return true }