diff options
| -rw-r--r-- | addDroplet.go | 3 | ||||
| -rw-r--r-- | http.go | 3 | ||||
| -rw-r--r-- | libvirtxml.go | 982 | ||||
| -rw-r--r-- | main.go | 3 | ||||
| -rw-r--r-- | start.go | 62 |
5 files changed, 8 insertions, 1045 deletions
diff --git a/addDroplet.go b/addDroplet.go index 7212ed8..c2d15b8 100644 --- a/addDroplet.go +++ b/addDroplet.go @@ -11,6 +11,7 @@ import ( pb "go.wit.com/lib/protobuf/virtbuf" "go.wit.com/log" "libvirt.org/go/libvirtxml" + "go.wit.com/lib/virtigoxml" ) // import a libvirt xml file @@ -48,7 +49,7 @@ func addDomainDroplet(domcfg *libvirtxml.Domain) (*DropletT, []*pb.Event, error) return d, alle, errors.New("update failed for " + domcfg.Name) } log.Info("added new droplet", domcfg.Name, domcfg.UUID) - dumpNonStandardXML(domcfg) + virtigoxml.DumpNonStandardXML(domcfg) return d, alle, nil } @@ -9,6 +9,7 @@ import ( "go.wit.com/lib/gui/shell" pb "go.wit.com/lib/protobuf/virtbuf" "go.wit.com/log" + "go.wit.com/lib/virtigoxml" ) // remove '?' part and trailing '/' @@ -65,7 +66,7 @@ func okHandler(w http.ResponseWriter, r *http.Request) { } if tmp == "/dumplibvirtxml" { - dumpLibvirtxmlDomainNames() + virtigoxml.DumpLibvirtxmlDomainNames() return } diff --git a/libvirtxml.go b/libvirtxml.go deleted file mode 100644 index 470b332..0000000 --- a/libvirtxml.go +++ /dev/null @@ -1,982 +0,0 @@ -// Copyright 2024 WIT.COM Inc Licensed GPL 3.0 - -package main - -import ( - "encoding/xml" - "fmt" - "os" - "path/filepath" - "reflect" - "strings" - - "go.wit.com/log" - "libvirt.org/go/libvirtxml" -) - -func makeStandardXml(d *DropletT) *libvirtxml.Domain { - log.Info("create new xml file for:", d.pb.Hostname) - domcfg := &libvirtxml.Domain{} - - addDefaultXml(domcfg, "standard.x86") - addDefaultXml(domcfg, "memory") - addDefaultXml(domcfg, "network") - addDefaultXml(domcfg, "spice") - addDefaultXml(domcfg, "qcow") - addDefaultXml(domcfg, d.pb.Hostname) - - return domcfg -} - -func writeoutXml(domcfg *libvirtxml.Domain, filename string) bool { - xmldoc, err := domcfg.Marshal() - - if err != nil { - fmt.Println("can't make xml file error:\n", err) - return false - } - - outfile := "/tmp/" + filename + ".xml" - regfile, _ := os.OpenFile(outfile, os.O_RDWR|os.O_CREATE, 0666) - fmt.Fprintln(regfile, xmldoc) - - log.Info("File is in", outfile) - regfile.Close() - return true -} - -func setDiskFilename(domcfg *libvirtxml.Domain, filename string) { - for i, x := range domcfg.Devices.Disks { - // Create a new DomainDiskSourceFile struct - newSource := &libvirtxml.DomainDiskSourceFile{ - File: filename, // Set the file name here - } - - // Assign it to the disk's source - domcfg.Devices.Disks[i].Source.File = newSource - - // fmt.Printf("Disk Source %s\n", name) - fmt.Printf("Disk Device %s\n", x.Source.File) - } -} - -func addDefaultXml(d *libvirtxml.Domain, filename string) { - fullname := "resources/xml/" + filename + ".xml" - pfile, err := resources.ReadFile(fullname) - if err != nil { - log.Println("ERROR:", err) - return - } - - err = d.Unmarshal(string(pfile)) - if err != nil { - log.Info("Marshal failed on file", filename) - return - } -} - -func readXml(filename string) (*libvirtxml.Domain, error) { - log.Verbose("parse xml file:", filename) - - hostname := filepath.Base(filename) - hostname = strings.TrimSuffix(hostname, ".xml") - - pfile, err := os.ReadFile(filename) - if err != nil { - log.Println("ERROR:", err) - return nil, err - } - - domcfg := &libvirtxml.Domain{} - - err = domcfg.Unmarshal(string(pfile)) - if err != nil { - log.Info("Marshal failed on file", filename, err) - return nil, ErrorParseXML - } - - if domcfg.Name != hostname { - log.Info("ERROR: filename:", filename) - log.Info("ERROR: domcfg.Name != name", domcfg.Name, hostname) - log.Info("ERROR: xml filenames must match the xml name") - os.Exit(-1) - } - - return domcfg, nil -} - -func (d *DropletT) mergeXml(filename string) error { - log.Info("merge xml file:", filename) - - pfile, err := os.ReadFile(filename) - if err != nil { - log.Println("ERROR:", err) - return ErrorNoFile - } - - err = d.xml.Unmarshal(string(pfile)) - if err != nil { - log.Info("Marshal failed on file", filename) - return ErrorParseXML - } - return nil -} - -func setSimpleDisk(domcfg *libvirtxml.Domain, filename string) { - // Clear out the existing disks (if any) - domcfg.Devices.Disks = nil - - // Define a new disk with "mynew.qcow2" - newDisk := libvirtxml.DomainDisk{ - Device: "disk", - Driver: &libvirtxml.DomainDiskDriver{ - Name: "qemu", - Type: "qcow2", - }, - Source: &libvirtxml.DomainDiskSource{ - File: &libvirtxml.DomainDiskSourceFile{ - File: filename, - }, - }, - Target: &libvirtxml.DomainDiskTarget{ - Dev: "vda", - Bus: "virtio", - }, - } - - // Add the new disk to the domain configuration - domcfg.Devices.Disks = append(domcfg.Devices.Disks, newDisk) -} - -func getMacs(domcfg *libvirtxml.Domain) []string { - var macs []string - // Iterate over the network interfaces and print the MAC addresses - for _, iface := range domcfg.Devices.Interfaces { - if iface.MAC != nil { - // iface.MAC.Address = "aa:bb:aa:bb:aa:ff" - fmt.Printf("MAC Address: %+v\n", iface.MAC) - // fmt.Printf("Interface: %s, MAC Address: %s\n", iface.Target.Dev, iface.MAC.Address) - macs = append(macs, iface.MAC.Address) - } else { - fmt.Printf("Interface: %s, MAC Address: not available\n", iface.Target.Dev) - } - } - return macs -} - -// removes all the ethernet interfaces -func clearEthernet(domcfg *libvirtxml.Domain) { - // Clear out the existing disks (if any) - domcfg.Devices.Interfaces = nil -} - -// add a new ethernet interface with mac assigned to bridge name -func addEthernetBridge(domcfg *libvirtxml.Domain, mac string, brname string) { - // Define a new disk with "mynew.qcow2" - // type DomainInterfaceType string - - var ib *libvirtxml.DomainInterfaceSourceBridge - ib = new(libvirtxml.DomainInterfaceSourceBridge) - ib.Bridge = brname - - newNet := libvirtxml.DomainInterface{ - MAC: &libvirtxml.DomainInterfaceMAC{ - Address: mac, - }, - Source: &libvirtxml.DomainInterfaceSource{ - Bridge: ib, - }, - Model: &libvirtxml.DomainInterfaceModel{ - Type: "virtio", - }, - /* this is for raw tap. use this for people who don't - who don't have bridge groups or proper cluster backend networking - literally leaving this blank makes the interface 'tap0' - */ - // Target: &libvirtxml.DomainInterfaceTarget{ - // }, - } - - // Add the new disk to the domain configuration - domcfg.Devices.Interfaces = append(domcfg.Devices.Interfaces, newNet) -} - -// makes an ethernet interface with qemu on dom0 as 'tapXXX' -// doesn't require a bridge group or any changes to dom0 networking (probably) -func addEthernetTap(domcfg *libvirtxml.Domain, mac string) { - newNet := libvirtxml.DomainInterface{ - MAC: &libvirtxml.DomainInterfaceMAC{ - Address: mac, - }, - /* this is for raw tap. use this for people who don't - who don't have bridge groups or proper cluster backend networking - literally leaving this blank makes the interface 'tap0' - */ - Target: &libvirtxml.DomainInterfaceTarget{}, - } - - // Add the new disk to the domain configuration - domcfg.Devices.Interfaces = append(domcfg.Devices.Interfaces, newNet) -} - -func setRandomMacs(domcfg *libvirtxml.Domain) { - for i, x := range domcfg.Devices.Interfaces { - // Create a new DomainDiskInterfaces struct - newMac := &libvirtxml.DomainInterfaceMAC{ - Address: "aa:bb:cc:dd:ee:ff", // make sure this is unique - } - - // Assign it to the disk's source - domcfg.Devices.Interfaces[i].MAC = newMac - - // fmt.Printf("Disk Source %s\n", name) - // fmt.Printf("mac addr %+v\n", x.MAC) - fmt.Printf("mac addr %s\n", x.MAC.Address) - } -} - -// go through the libvirt xml object and dump out everything -// that is "standard". This is just a way to double check that -// there might be something interesting in a VM -// 'standard' here means what I think is standard -func dumpNonStandardXML(domcfg *libvirtxml.Domain) (string, error) { - // dump type - if domcfg.Type == "kvm" { - domcfg.Type = "" - } else { - fmt.Printf("type: %+v\n", domcfg.Type) - } - - // dump normal OS settings - var standardOS bool = false - if domcfg.OS != nil { - if domcfg.OS.Type != nil { - // OS Type: &{Arch:x86_64 Machine:pc-i440fx-5.2 Type:hvm} - t := domcfg.OS.Type - if t.Arch == "x86_64" || t.Machine == "pc-i440fx-5.2" { - standardOS = true - } - } - } - if standardOS { - domcfg.OS = nil - } else { - fmt.Printf("OS: %+v\n", domcfg.OS) - fmt.Printf("OS Type: %+v\n", domcfg.OS.Type) - } - - // ignore XMLName and IOThreads probably - // skip is hard coded in isDomainEmpty() function - // fmt.Printf("XMLName: %+v\n", domcfg.XMLName) - // fmt.Printf("IOThreads: %+v\n", domcfg.IOThreads) - - // dump all the clock stuff if it's standard - var normalclock bool = true - if domcfg.Clock.Offset != "utc" { - normalclock = false - } - for i, t := range domcfg.Clock.Timer { - // fmt.Printf("Test Clock Timer: %d , %s , %+v\n", i, t.Name, t) - switch t.Name { - case "rtc": - if t.TickPolicy != "catchup" { - fmt.Printf("Clock Name: %+v , %+v\n", i, t) - normalclock = false - } - case "pit": - if t.TickPolicy != "delay" { - fmt.Printf("Clock Name: %+v , %+v\n", i, t) - normalclock = false - } - case "hpet": - if t.Present != "no" { - fmt.Printf("Clock Name: %+v , %+v\n", i, t) - normalclock = false - } - default: - fmt.Printf("Clock Name: %+v , %+v\n", i, t) - normalclock = false - } - } - if normalclock { - domcfg.Clock = nil - } else { - fmt.Printf("Clock was 'nonstandard' %+v\n", domcfg.Clock.Timer) - } - - // probably just dump Features for now - // fmt.Printf("Features: %+v\n", domcfg.Features) - // fmt.Printf("Feature VMPort: %+v\n", domcfg.Features.VMPort) - // ignore if ACPI is set or not - var featurematch bool = true - if domcfg.Features.ACPI != nil { - domcfg.Features.ACPI = nil - } else { - featurematch = false - } - // ignore if APIC is set or not - if domcfg.Features.APIC != nil { - domcfg.Features.APIC = nil - } else { - featurematch = false - } - // what is VMPort anyway? - if domcfg.Features.VMPort != nil { - if domcfg.Features.VMPort.State == "off" { - domcfg.Features.VMPort = nil - } - } else { - featurematch = false - } - // screwit, if all three of those match just erase - // this. not sure what uses it anyway but it's probably obscure - // and I'm not using it on any of my machines right now - // also, this is dumb that I'm doing this but I want to - // fine tooth comb through this right now - // also, I don't have a boss so nobody can tell me what to do - if featurematch { - domcfg.Features = nil - } - - // fmt.Printf("Features: %+v\n", domcfg.Features) - - // for i, f := range domcfg.Features { - // fmt.Printf("Feature: %+v , %+v\n", i, f) - // } - - // these should always just be strings? - domcfg.Name = "" - domcfg.UUID = "" - - // todo: actually check these for anything different - domcfg.Memory = nil - domcfg.CurrentMemory = nil - domcfg.VCPU = nil - // is this always "host-passthrough" and "host-model"? - // only Fabrice knows :) - if domcfg.CPU != nil { - switch domcfg.CPU.Mode { - case "host-passthrough": - domcfg.CPU = nil - case "host-model": - domcfg.CPU = nil - case "custom": - updatedXML, _ := xml.MarshalIndent(domcfg.CPU, "", " ") - log.Info("Ignoring custom CPU Start") - fmt.Println(string(updatedXML)) - log.Info("Ignoring custom CPU End (--xml-ignore-cpu=true)") - if argv.IgnoreCpu { - domcfg.CPU = nil - } - default: - fmt.Printf("unknown CPU: %+v\n", domcfg.CPU) - fmt.Printf("unknown CPU Model: %+v\n", domcfg.CPU.Model) - fmt.Printf("unknown CPU Mode: %+v\n", domcfg.CPU.Mode) - updatedXML, _ := xml.MarshalIndent(domcfg.CPU, "", " ") - log.Info("Non-Standard XML Start") - fmt.Println(string(updatedXML)) - log.Info("Non-Standard XML End") - } - } - - var secnormal bool = true - if len(domcfg.SecLabel) != 0 { - for _, sec := range domcfg.SecLabel { - switch sec.Model { - case "apparmor": - // log.Info("ignoring SecLabel apparmor. not supported yet") - // log.Info("you must set this later if you need this") - // xmlAny(sec) - case "dac": - // log.Info("ignoring SecLabel dac. not supported yet") - // log.Info("you must set this later if you need this") - // xmlAny(sec) - default: - fmt.Printf("unknown SecLabel: %+v\n", sec) - fmt.Printf("unknown SecLabel.Model: %+v\n", sec.Model) - xmlAny(sec) - secnormal = false - } - } - } - if secnormal { - domcfg.SecLabel = nil - } - - // ignore Metadata - // this is probably something about what kind of OS you might be running - // todo: get this directly from the disk image - if domcfg.Metadata != nil { - var s string - s = domcfg.Metadata.XML - log.Info("Not saving Domain.Metadata.XML:", s) - log.Info("todo: get this from disk image") - domcfg.Metadata = nil - } - - // ignore Resource - if domcfg.Resource != nil { - if domcfg.Resource.Partition == "/machine" { - domcfg.Resource = nil - } else { - fmt.Printf("non-standard Domain.Resource: %+v\n", domcfg.Resource) - } - } - - // ignore Resource - if domcfg.ID != nil { - // ignore domain id - domcfg.ID = nil - } - - // this will move elsewhere in the protobuf someday - // ignore all these for now - if domcfg.OnPoweroff != "" { // normally "destroy" - domcfg.OnPoweroff = "" - } - if domcfg.OnCrash != "" { // normally "restart", often "destroy" - domcfg.OnCrash = "" - } - if domcfg.OnReboot != "" { // normally "restart" - domcfg.OnReboot = "" - } - // same with PM. move to protobuf - domcfg.PM = nil - - // only keep non-qemu stuff - var qemu bool = true - for _, disk := range domcfg.Devices.Disks { - if disk.Driver.Name != "qemu" { - fmt.Printf("- Disk: %s, Device: %s, Source: %s\n", disk.Device, disk.Driver.Name, disk.Source.File.File) - fmt.Printf("FOUND NON QEMU DISK\n") - fmt.Printf("FOUND NON QEMU DISKS\n") - qemu = false - } else { - } - } - if qemu { - domcfg.Devices.Disks = nil - } else { - // fmt.Printf("FOUND NON QEMU DISKS\n") - } - - // network interfaces get processed elsewhere - domcfg.Devices.Interfaces = nil - - // look for strange stuff here - var normalPCI bool = true - var keepPCI []libvirtxml.DomainController - for _, controller := range domcfg.Devices.Controllers { - switch controller.Type { - case "usb": - switch controller.Model { - case "ich9-ehci1": - case "piix3-uhci": - case "qemu-xhci": - // fmt.Printf("OK USB: %s, %d\n", controller.Model, *controller.Index) - case "ich9-uhci1": - // fmt.Printf("OK USB: %s, %d\n", controller.Model, *controller.Index) - case "ich9-uhci2": - // fmt.Printf("OK USB: %s, %d\n", controller.Model, *controller.Index) - case "ich9-uhci3": - // fmt.Printf("OK USB: %s, %d\n", controller.Model, *controller.Index) - default: - keepPCI = append(keepPCI, controller) - normalPCI = false - fmt.Printf("USB: %s, %d\n", controller.Model, *controller.Index) - // Domain:0xc0002d2760 Bus:0xc0002d2768 Slot:0xc0002d2770 Function:0xc0002d2778 MultiFunction: - pci := controller.Address.PCI - fmt.Printf("USB: Domain: %+v Slot %d Function %d\n", *pci.Domain, *pci.Slot, *pci.Function) - } - case "ide": - // fmt.Printf("IGNORE IDE\n") - case "virtio-serial": - // fmt.Printf("IGNORE virtio-serial\n") - case "sata": - // fmt.Printf("SATA: %s, %d\n", controller.Model, *controller.Index) - // fmt.Printf("SATA: %+v\n", controller) - case "scsi": - switch controller.Model { - case "virtio-scsi": - // fmt.Printf("IGNORE SCSI: lsilogic\n") - case "lsilogic": - // fmt.Printf("IGNORE SCSI: lsilogic\n") - default: - keepPCI = append(keepPCI, controller) - normalPCI = false - } - case "pci": - // these are the strings I've found so far - switch controller.Model { - case "pci-root": - case "pcie-root": - case "pcie-root-port": - case "pcie-to-pci-bridge": - default: - fmt.Printf("PCI: %s, %d\n", controller.Model, *controller.Index) - // Domain:0xc0002d2760 Bus:0xc0002d2768 Slot:0xc0002d2770 Function:0xc0002d2778 MultiFunction: - if controller.Address == nil { - fmt.Printf("PCI: controller.Address = nil\n") - } else { - pci := controller.Address.PCI - fmt.Printf("PCI: Domain: %+v Slot %d Function %d\n", *pci.Domain, *pci.Slot, *pci.Function) - } - normalPCI = false - keepPCI = append(keepPCI, controller) - } - default: - fmt.Printf("? controllerType: %s: %+v\n", controller.Type, controller) - normalPCI = false - keepPCI = append(keepPCI, controller) - } - } - if normalPCI { - domcfg.Devices.Controllers = nil - } else { - domcfg.Devices.Controllers = keepPCI - } - - // ignore serial and console - domcfg.Devices.Serials = nil - domcfg.Devices.Consoles = nil - - // ignore sound - domcfg.Devices.Sounds = nil - - // ignore input - domcfg.Devices.Inputs = nil - - // ignore MemoryBalloon. This is cool, but no mortal humans - // are going to use it at this point. By that I mean me. - // someday this will be in protobuf? - domcfg.Devices.MemBalloon = nil - - if domcfg.Devices.Emulator == "/usr/bin/qemu-system-x86_64" { - domcfg.Devices.Emulator = "" - } - - // ignore Graphics == Spice when AutoPort = 'yes' - var normalSpice bool = true - if domcfg.Devices.Graphics != nil { - for i, g := range domcfg.Devices.Graphics { - if g.VNC != nil { - // ignore vnc settings - // fmt.Printf("Ignore Graphics VNC settings: %d %+v\n", i, g) - continue - } - if g.Spice != nil { - // this is all moved to updateDroplet() - // this is a spice definition, just ignore it - // because port mappings and network access will be handled - // somewhere else someday - // fmt.Printf("Graphics: %d %+v\n", i, g) - 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 { - // print out, but ignore the port number - // fmt.Printf("Spice Port = %d\n", s.Port) - } - continue - } - // figure out what to do with non-spice stuff - fmt.Printf("Unknown Graphics: %d %+v\n", i, g) - normalSpice = false - } - } - if normalSpice { - domcfg.Devices.Graphics = nil - } - - // blank out emulator. should be in dom0 - switch domcfg.Devices.Emulator { - case "": - domcfg.Devices.Emulator = "" - case "/usr/bin/kvm": - domcfg.Devices.Emulator = "" - case "/usr/bin/kvm-spice": - domcfg.Devices.Emulator = "" - default: - fmt.Printf("Unknown Emulator: %s\n", domcfg.Devices.Emulator) - } - - // ignore Channels == SpiceVMC - normalSpice = true - if domcfg.Devices.Channels != nil { - for _, c := range domcfg.Devices.Channels { - if c.Source != nil { - s := c.Source - if s != nil { - // fmt.Printf("Channels: %+v\n", s.SpiceVMC) - } else { - fmt.Printf("? Channels: %+v\n", c) - normalSpice = false - } - } else { - fmt.Printf("? Channels: %+v\n", c) - normalSpice = false - } - } - } - if normalSpice { - domcfg.Devices.Channels = nil - } - - // this is probably for spice to have keyboard and mouse input - normalSpice = true - if domcfg.Devices.RedirDevs != nil { - for _, c := range domcfg.Devices.RedirDevs { - s := c.Source - if s != nil { - if s.SpiceVMC != nil { - // this is the normal USB redirection (I guess) - } else { - normalSpice = false - } - } else { - normalSpice = false - } - // fmt.Printf("? RedirDevs: %+v\n", c) - // fmt.Printf("? RedirDevs Source: %+v\n", s) - // fmt.Printf("? RedirDevs SpiceVMC: %d\n", *s.SpiceVMC) - // fmt.Printf("? RedirDevs Address: %+v\n", c.Address) - // fmt.Printf("? RedirDevs USB: %+v\n", c.Address.USB) - } - } - if normalSpice { - domcfg.Devices.RedirDevs = nil - } - - var normalRNGs bool = true - if domcfg.Devices.RNGs != nil { - for _, rng := range domcfg.Devices.RNGs { - if rng.Model == "virtio" { - // nothing to do for this - } else { - fmt.Printf("? RNGs: %+v\n", rng) - normalRNGs = false - } - } - } - if normalRNGs { - domcfg.Devices.RNGs = nil - } - - // don't copy this over here yet. - // probably most domU's don't really use/need it set to what is in the XML - var normalVideo bool = true - if domcfg.Devices.Videos != nil { - for _, v := range domcfg.Devices.Videos { - switch v.Model.Type { - case "qxl": - if v.Model.VRam == 65536 { - // standard qxl video - } else { - fmt.Printf("? Video: %+v\n", v) - fmt.Printf("? Video Model: %+v\n", v.Model) - normalVideo = false - } - case "cirrus": - case "virtio": - // this should always be standard - //fmt.Printf("? Video: %+v\n", v) - //fmt.Printf("? Video Model: %+v\n", v.Model) - //fmt.Printf("? Video Address: %+v\n", v.Address) - //fmt.Printf("? Video PCI: %+v\n", v.Address.PCI) - default: - fmt.Printf("? Video: %+v\n", v) - fmt.Printf("? Video Model: %+v\n", v.Model) - normalVideo = false - } - } - } - if normalVideo { - domcfg.Devices.Videos = nil - } - - return finalEmptyCheck(domcfg) -} - -// this tries the final zero'ing out of the XML -// todo: if this fails, put the remaining XML in the protobuf file? -func finalEmptyCheck(domcfg *libvirtxml.Domain) (string, error) { - // dumpLibvirtxmlDomainNames() - if libvirtxmlDomainDevicesEmpty(*domcfg.Devices) { - // fmt.Println("Domain Devices are empty") - domcfg.Devices = nil - } else { - return warnUserOfNonStandardXML(domcfg) - } - - if libvirtxmlDomainEmpty(*domcfg) { - domcfg = nil - return warnUserOfNonStandardXML(domcfg) - } - - final, err := warnUserOfNonStandardXML(domcfg) - if err != nil { - fmt.Printf("todo: improve this libvirtXML parsing. %v\n", err) - os.Exit(-1) - } - if final != "" { - me.changed = true - } - return final, nil -} - -func xmlAny(a any) (string, error) { - updatedXML, err := xml.MarshalIndent(a, "", " ") - if err != nil { - fmt.Printf("Failed to marshal updated XML: %v\n", err) - return "", err - } - final := string(updatedXML) - if final == "" { - // everything seems to have been parsed pretty standard - return "", nil - } - log.Info("Non-Standard XML Start") - fmt.Println(final) - log.Info("Non-Standard XML End") - return final, nil -} - -func warnUserOfNonStandardXML(domcfg *libvirtxml.Domain) (string, error) { - updatedXML, err := xml.MarshalIndent(domcfg, "", " ") - if err != nil { - fmt.Printf("Failed to marshal updated XML: %v\n", err) - return "", err - } - final := string(updatedXML) - if final == "" { - // everything seems to have been parsed pretty standard - return "", nil - } - log.Info("Non-Standard XML Start") - fmt.Println(string(updatedXML)) - log.Info("Non-Standard XML End") - log.Info("") - log.Info("This XML must be removed by hand. Put this in the protobuf?") - return string(updatedXML), nil -} - -// dump out all the fields in libvirtxml.DomainDeviceList -func dumpLibvirtxmlDomainNames() { - var domain libvirtxml.Domain - t := reflect.TypeOf(domain) - - fmt.Println("Fields in libvirtxml.Domain:") - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - fmt.Println("Domain:", field.Name) - } - - var device libvirtxml.DomainDeviceList - t = reflect.TypeOf(device) - - fmt.Println("Fields in libvirtxml.DomainDeviceList:") - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - fmt.Println("DomainDeviceList:", field.Name) - } - - var iface libvirtxml.DomainInterface - t = reflect.TypeOf(iface) - - fmt.Println("Fields in libvirtxml.DomainInterface:") - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - fmt.Println("DomainInterface:", field.Name) - } - - var isource libvirtxml.DomainInterfaceSource - listFields(isource, "libvirtxml.DomainInterfaceSource") - - var ibridge libvirtxml.DomainInterfaceSourceBridge - listFields(ibridge, "libvirtxml.DomainInterfaceSourceBridge") -} - -func listFields(a any, name string) { - t := reflect.TypeOf(a) - - fmt.Println("Fields in", name) - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - fmt.Println(name, field.Name) - } -} - -// dump out all the fields in libvirtxml.DomainDeviceList -func libvirtxmlDomainDevicesEmpty(mydom libvirtxml.DomainDeviceList) bool { - var empty bool = true - // Get the reflection object of the variable - v := reflect.ValueOf(mydom) - - // Ensure that we are working with a struct - if v.Kind() == reflect.Struct { - // fmt.Println("Fields and values in libvirtxml.DomainDeviceList:") - - // Loop through each field in the struct - for i := 0; i < v.NumField(); i++ { - // Get field name - field := v.Type().Field(i).Name - - // Get field value - value := v.Field(i) - - if !value.IsValid() { - fmt.Printf("Field: %s is nil or invalid\n", field) - continue - } - - // Check if the field is a string, array, or slice - switch value.Kind() { - case reflect.String: - if value.String() != "" { - fmt.Printf("Field: %s is a String with value: %s\n", field, value.String()) - empty = false - } - case reflect.Slice: - if value.Len() != 0 { - fmt.Printf("Field: %s is a Slice with length: %d\n", field, value.Len()) - empty = false - } - case reflect.Array: - if value.Len() != 0 { - fmt.Printf("Field: %s is an Array with length: %d\n", field, value.Len()) - empty = false - } - case reflect.Ptr: - if !value.IsValid() { - fmt.Println("Field ptr: value:", value) - fmt.Printf("Field ptr: %s is of type: %s\n", field, value.Kind()) - empty = false - } - default: - fmt.Printf("Field: %s is of type: %s\n", field, value.Kind()) - empty = false - } - // Print the field name and value - // fmt.Printf("Field: %s, Value: %v\n", field, value) - } - } else { - fmt.Println("Provided variable is not a struct.") - } - return empty -} - -// dump out all the fields in libvirtxml.DomainDeviceList -func libvirtxmlDomainEmpty(mydom libvirtxml.Domain) bool { - var empty bool = true - // Get the reflection object of the variable - v := reflect.ValueOf(mydom) - - // Ensure that we are working with a struct - if v.Kind() == reflect.Struct { - // fmt.Println("Fields and values in libvirtxml.DomainDeviceList:") - - // Loop through each field in the struct - for i := 0; i < v.NumField(); i++ { - // Get field name - field := v.Type().Field(i).Name - - // Get field value - value := v.Field(i) - - if !value.IsValid() { - fmt.Printf("Field: %s is invalid\n", field) - continue - } - // processed as Domain.Metadata & Domain.Resource - // if (field == "IOThreads") || (field == "XMLName") { - // fmt.Printf("Field: %s is: %s\n", field, value.String()) - // continue - // } - - // Check if the field is a string, array, or slice - switch value.Kind() { - case reflect.String: - if value.String() != "" { - fmt.Printf("Field: %s is a String with value: %s\n", field, value.String()) - empty = false - } - case reflect.Slice: - if value.Len() != 0 { - fmt.Printf("Field: %s is a Slice with length: %d\n", field, value.Len()) - empty = false - } - case reflect.Array: - if value.Len() != 0 { - fmt.Printf("Field: %s is an Array with length: %d\n", field, value.Len()) - empty = false - } - case reflect.Struct: - if IsStructEmptyOrNil(value) { - // fmt.Printf("XML Field ignore empty Struct %s\n", field) - } else { - fmt.Printf("Field Struct is not empty %s is %+v\n", field, value) - empty = false - } - case reflect.Uint: - // probably ignore ints. when has that ever gone wrong? - case reflect.Ptr: - if value.IsValid() { - if value.IsNil() { - // this means the value is actually nil - } else { - // there is something still here in the libvirt XML - fmt.Printf("Field Valid? field %s is of type: %s\n", field, value.Kind()) - fmt.Println("Field Valid? ptr: value:", value) - empty = false - } - } else { - fmt.Println("Invalid Field ptr: value:", value) - fmt.Printf("Invalid Field ptr: %s is of type: %s\n", field, value.Kind()) - empty = false - } - default: - fmt.Printf("Field: %s is of type: %s\n", field, value.Kind()) - empty = false - } - // Print the field name and value - // fmt.Printf("Field: %s, Value: %v\n", field, value) - } - } else { - fmt.Println("Provided variable is not a struct.") - } - return empty -} - -// IsStructEmptyOrNil checks if a struct or pointer to struct is empty, blank, or nil -func IsStructEmptyOrNil(value interface{}) bool { - val := reflect.ValueOf(value) - - // If the value is a pointer, check if it's nil and dereference it if not - if val.Kind() == reflect.Ptr { - if val.IsNil() { - return true - } - val = val.Elem() - } - - // Ensure we're dealing with a struct after potential dereferencing - if val.Kind() != reflect.Struct { - return false // Not a struct - } - - // Check each field in the struct for its zero value - for i := 0; i < val.NumField(); i++ { - field := val.Field(i) - // Skip unexported fields as we can't access them - if !field.CanInterface() { - continue - } - if !reflect.DeepEqual(field.Interface(), reflect.Zero(field.Type()).Interface()) { - return false // Found a non-zero field - } - } - - return true // All fields are zero values -} @@ -12,6 +12,7 @@ import ( "go.wit.com/dev/alexflint/arg" pb "go.wit.com/lib/protobuf/virtbuf" "go.wit.com/log" + "go.wit.com/lib/virtigoxml" ) var Version string @@ -63,7 +64,7 @@ func main() { checkUniqueFilenames() for _, filename := range argv.Xml { - domcfg, err := readXml(filename) + domcfg, err := virtigoxml.ReadXml(filename) if err != nil { // parsing the libvirt xml file failed log.Info("error:", filename, err) @@ -4,7 +4,6 @@ package main import ( "os" - "path/filepath" "go.wit.com/lib/virtigoxml" "go.wit.com/log" @@ -24,6 +23,7 @@ func newStart(start string) { newAddXml(domcfg, "standard.x86") // addDefaultXml(domcfg, "memory") // addDefaultXml(domcfg, "network") + newAddXml(domcfg, "spice") newAddXml(domcfg, "qcow") virtigoxml.StartDropletXml(d, domcfg, start) @@ -41,65 +41,6 @@ func newAddXml(domcfg *libvirtxml.Domain, filename string) error { } /* -// generate the XML for 'virsh create' -func startDropletXml(start string) { - - - domcfg.Type = "kvm" - domcfg.Name = d.Hostname - domcfg.UUID = d.Uuid - - var i uint - i = uint(d.Memory / (1024 * 1024)) - - // var tmp string - // tmp = domcfg.VCPU - domcfg.VCPU = new(libvirtxml.DomainVCPU) - domcfg.VCPU.Value = uint(d.Cpus) - - domcfg.Memory = new(libvirtxml.DomainMemory) - domcfg.Memory.Value = i - domcfg.Memory.Unit = "MiB" - - fmt.Printf("Virt Memory %d %s\n", domcfg.Memory.Value, domcfg.Memory.Unit) - - // addEthernet(domcfg, "04:44:33:11:22:11", "worldbr") - // addEthernet(domcfg, "04:44:33:33:44:55", "greenbr") - - var count int = 0 - for _, n := range d.Networks { - log.Info("add network", d.Hostname, "mac addr", n.Mac, "interface", n.Name) - if n.Name != "worldbr" { - log.Info("OVERRIDE BRIDGE WITH 'worldbr'") - } - addEthernetBridge(domcfg, n.Mac, "worldbr") - // addEthernetTap(domcfg, n.Mac) - count += 1 - } - if count == 1 { - // this is normal - } else { - log.Info("WRONG NUMBER OF ETHERNET INTERFACES:", count) - } - - // add a check here to make these unique - // setRandomMacs(domcfg) - - for _, disk := range d.Disks { - fullname := findDisk(disk.Filename) - if fullname == "" { - log.Info("can not find disk", d.Hostname, "dir", disk.Filepath, "filename", disk.Filename) - os.Exit(-1) - } else { - // qcow := "/home/nfs/" + d.Hostname + ".qcow2" - setSimpleDisk(domcfg, fullname) - } - } - - writeoutXml(domcfg, d.Hostname) - os.Exit(-1) -} -*/ func findDisk(filename string) string { for _, dirname := range me.cluster.Dirs { @@ -119,3 +60,4 @@ func findDisk(filename string) string { } return "" } +*/ |
