Adding basic wrappers

- Wrapper for Host connection called Host
  - Allows for easier usage of the API
- Wrapper for Domains called GuestVM, which pulls VM information and allows
  other functionality in a better way
This commit is contained in:
Matthew Stobbs 2024-03-10 20:32:11 -06:00
parent bab4f2506b
commit 4ecf05bf82
4 changed files with 147 additions and 9 deletions

View File

@ -54,3 +54,21 @@ Overall goals:
- Connection management (to other libvirt hosts) - Connection management (to other libvirt hosts)
- Create a simple WebUI with HTMX to monitor that stuff - Create a simple WebUI with HTMX to monitor that stuff
- Add the ability to manage that stuff once we can monitor it, through the WebUI. - Add the ability to manage that stuff once we can monitor it, through the WebUI.
## Special XMLNS definitions
### Domain
- `xmlns:clustvirt="https://git.staur.ca/stobbsm/clustvirt"`
```xml
<metadata>
<clustvirt:data xmlns:clustvirt="https://git.staur.ca/stobbsm/clustvirt/">
<clustvirt:created>true</clustvirt:created>
<clustvirt:managed>true</clustvirt:managed>
<clustvirt:currentHost>host01</clustvirt:currentHost>
<clustvirt:origHost>host02</clustvirt:origHost>
<clustvirt:baseGuest>rocky-9-base</clustvirt:baseGuest>
</clustvirt:data>
</metadata>
```

53
domains.go Normal file
View File

@ -0,0 +1,53 @@
package main
import (
"fmt"
"log"
"libvirt.org/go/libvirt"
)
// GuestVM holds a handle is used to communicate to Domains
type GuestVM struct {
Name string
ID uint
BackupXML string
close chan struct{}
closeErr chan error
dom *libvirt.Domain
}
// GetGuest loads guest information by name and returns it
func GetGuest(name string, conn *libvirt.Connect) (*GuestVM, error) {
g := &GuestVM{
Name: name,
close: make(chan struct{}),
closeErr: make(chan error),
}
var err error
if g.dom, err = conn.LookupDomainByName(name); err != nil {
return nil, err
}
if g.ID, err = g.dom.GetID(); err != nil {
return nil, err
}
if g.BackupXML, err = g.dom.BackupGetXMLDesc(0); err != nil {
return nil, err
}
go func() {
defer close(g.closeErr)
<-g.close
g.closeErr <- g.dom.Free()
}()
return g, nil
}
// Close closes an open connection
func (g *GuestVM) Close() error {
close(g.close)
return <-g.closeErr
}

69
host.go Normal file
View File

@ -0,0 +1,69 @@
package main
import (
"fmt"
"libvirt.org/go/libvirt"
)
// Host holds information and acts as a connection handle for a Host
// If a connection is closed prematurely, will re-open the connection and
// try the attempted method again
type Host struct {
HostName string
conn *libvirt.Connect
close chan struct{}
closeErr chan error
}
// ConnectHost creates a host connection wrapper that can be used regularly
func ConnectHost(host string) (*Host, error) {
h := &Host{
HostName: host,
}
if err := h.connect(); err != nil {
return nil, err
}
h.close = make(chan struct{})
h.closeErr = make(chan error)
go func() {
defer close(h.closeErr)
<-h.close
_, err := h.conn.Close()
h.closeErr <- err
}()
return h, nil
}
// connect creates a host connection
func (h *Host) connect() error {
var err error
h.conn, err = libvirt.NewConnect(
fmt.Sprintf("qemu+ssh://%s/system", h.HostName),
)
return err
}
// GetGuestByName returns a GuestVM instance that exists on the given host
func (h *Host) GetGuestByName(name string) (*GuestVM, error) {
if g, err := GetGuest(name, h.conn); err == nil {
return g, nil
} else {
lverr, ok := err.(libvirt.Error)
if ok && lverr.Code == libvirt.ERR_INVALID_CONN {
// try again after creating a new connection
return GetGuest(name, h.conn)
}
return nil, err
}
}
// Close triggers closing the host connection
func (h *Host) Close() error {
close(h.close)
return <-h.closeErr
}

16
main.go
View File

@ -2,23 +2,21 @@ package main
import ( import (
"log" "log"
"libvirt.org/go/libvirt"
) )
func main() { func main() {
log.Println("Starting clustvirt, the libvirt cluster manager") log.Println("Starting clustvirt, the libvirt cluster manager")
// Try connecting to libvirt // Try connecting to libvirt
conn, err := libvirt.NewConnect("qemu+ssh://earth.staur.ca/system") doms, err := GetDomsInCluster("earth.staur.ca", "mars.staur.ca", "venus.staur.ca")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer conn.Close() log.Printf("Domains in cluster: %d", len(doms))
// Example of xmlns uri found in libvirt xml definition:
doms, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE | libvirt.CONNECT_LIST_DOMAINS_INACTIVE) // xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0"
if err != nil { for _, d := range doms {
log.Fatal(err) log.Println(d.GetMetadata(0, "", 0))
d.Free()
} }
log.Printf("Domains on earth: %d", len(doms))
} }