Switch to libvirt-go and libvirt-go-xml

This commit is contained in:
Scott Ellis 2023-12-15 00:26:37 -08:00
parent 74c4ca5fa1
commit 7f1162052a
3 changed files with 47 additions and 50 deletions

2
go.mod
View File

@ -14,4 +14,6 @@ require (
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
libvirt.org/go/libvirt v1.9008.0 // indirect
libvirt.org/go/libvirtxml v1.9008.0 // indirect
)

4
go.sum
View File

@ -61,3 +61,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
libvirt.org/go/libvirt v1.9008.0 h1:LLpjuSQm9gChnx7I/44SLLg/eyvTnJpcMAFmKot65Zc=
libvirt.org/go/libvirt v1.9008.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ=
libvirt.org/go/libvirtxml v1.9008.0 h1:xo2U9SqUsufTFtbyjiqs6oDdF329cvtRdqttWN7eojk=
libvirt.org/go/libvirtxml v1.9008.0/go.mod h1:7Oq2BLDstLr/XtoQD8Fr3mfDNrzlI3utYKySXF2xkng=

View File

@ -15,20 +15,16 @@ import (
"errors"
"flag"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/antchfx/xmlquery"
"github.com/digitalocean/go-libvirt"
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
"libvirt.org/go/libvirt"
"libvirt.org/go/libvirtxml"
"log"
)
func main() {
var iface string // Interface we'll listen on
var buffer = int32(1600) // Buffer for packets received
var iface string // Interface we'll listen on
var buffer = int32(1600) // Buffer for packets received
var filter = "udp and broadcast and (len = 102 or len = 144 or len=234)" // PCAP filter to catch UDP WOL packets
flag.StringVar(&iface, "interface", "", "Network interface name to listen on")
@ -75,77 +71,72 @@ func GrabMACAddr(packet gopacket.Packet) (string, error) {
func WakeVirtualMachine(mac string) bool {
// Connect to the local libvirt socket
c, err := net.DialTimeout("unix", "/var/run/libvirt/libvirt-sock", 2*time.Second)
connection, err := libvirt.NewConnect("qemu+tcp:///system")
if err != nil {
log.Fatalf("failed to dial libvirt: %v", err)
}
l := libvirt.New(c)
if err := l.Connect(); err != nil {
log.Fatalf("failed to connect: %v", err)
}
defer connection.Close()
// Get a list of all VMs (aka Domains) configured so we can loop through them
flags := libvirt.ConnectListDomainsActive | libvirt.ConnectListDomainsInactive
domains, _, err := l.ConnectListAllDomains(1, flags)
// Get a list of all inactive VMs (aka Domains) configured so we can loop through them
domains, err := connection.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_INACTIVE)
if err != nil {
log.Fatalf("failed to retrieve domains: %v", err)
}
for _, d := range domains {
//fmt.Printf("%d\t%s\t%x\n", d.ID, d.Name, d.UUID)
for _, domain := range domains {
// Now we get the XML Description for each domain
xmldesc, err := l.DomainGetXMLDesc(d, 0)
xmldesc, err := domain.GetXMLDesc(0)
if err != nil {
log.Fatalf("failed retrieving interfaces: %v", err)
log.Fatalf("failed retrieving XML: %v", err)
}
// Feed the XML output into xmlquery
querydoc, err := xmlquery.Parse(strings.NewReader(xmldesc))
// Get the details for each domain
domcfg := &libvirtxml.Domain{}
err = domcfg.Unmarshal(xmldesc)
if err != nil {
log.Fatalf("Failed to parse XML: %v", err)
log.Fatalf("failed retrieving domain configuration: %v", err)
}
// Perform an xmlquery to look for the MAC address in the XML
for _, list := range xmlquery.Find(querydoc, "//domain/devices/interface/mac/@address") {
// Use the strings.EqualFold function to do a case-insensitive comparison of MACs
if strings.EqualFold(list.InnerText(), mac) {
stateInt, _, err := l.DomainGetState(d, 0)
// Loop through each interface found
for _, iface := range domcfg.Devices.Interfaces {
domainmac := iface.MAC.Address
if domainmac == mac {
// We'll use the name later, so may as well get it here
name := domcfg.Name
// Get the state of the VM and take action
state, _, err := domain.GetState()
if err != nil {
log.Fatalf("failed to check domain state: %v", err)
}
state := libvirt.DomainState(stateInt)
// fmt.Printf("Domain state is %v\n", state)
switch state {
case libvirt.DomainShutoff, libvirt.DomainCrashed:
fmt.Printf("Waking system: %s at MAC %s\n", d.Name, mac)
if err := l.DomainCreate(d); err != nil {
case libvirt.DOMAIN_SHUTDOWN, libvirt.DOMAIN_SHUTOFF, libvirt.DOMAIN_CRASHED:
fmt.Printf("Waking system: %s at MAC %s\n", name, mac)
if err := domain.Create(); err != nil {
log.Fatalf("Failed to start domain: %v", err)
}
case libvirt.DomainPmsuspended:
fmt.Printf("PM Wakeup system: %s at MAC %s\n", d.Name, mac)
if err := l.DomainPmWakeup(d, 0); err != nil {
log.Fatalf("Failed to pm wakeup domain: %v", err)
case libvirt.DOMAIN_PMSUSPENDED:
fmt.Printf("Unsuspending system: %s at MAC %s\n", name, mac)
if err := domain.Create(); err != nil {
log.Fatalf("Failed to unsuspend domain: %v", err)
}
case libvirt.DomainPaused:
fmt.Printf("Resume system: %s at MAC %s\n", d.Name, mac)
if err := l.DomainResume(d); err != nil {
case libvirt.DOMAIN_PAUSED:
fmt.Printf("Resuming system: %s at MAC %s\n", name, mac)
if err := domain.Create(); err != nil {
log.Fatalf("Failed to resume domain: %v", err)
}
default:
fmt.Printf("System %s is already running or in a state that cannot be woken from. State: %d\n", d.Name, state)
default:
fmt.Printf("System is already running or in a state that cannot be woken from. State: %d\n", state)
}
}
}
}
if err := l.Disconnect(); err != nil {
log.Fatalf("failed to disconnect: %v", err)
}
return true
}