182 lines
5.1 KiB
Go
182 lines
5.1 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"math/rand"
|
||
|
"net"
|
||
|
|
||
|
"github.com/sirupsen/logrus"
|
||
|
"libvirt.org/go/libvirt"
|
||
|
"libvirt.org/go/libvirtxml"
|
||
|
|
||
|
"mkvm/config"
|
||
|
"mkvm/libvirtx"
|
||
|
"mkvm/volumes/pools"
|
||
|
)
|
||
|
|
||
|
type domainModifier func(*libvirtxml.Domain) error
|
||
|
|
||
|
func getLibvirtAndPool() (*libvirt.Connect, pools.StoragePool, error) {
|
||
|
conn, err := libvirtx.New()
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
defer conn.Close()
|
||
|
|
||
|
pool, err := pools.GetPool(conn, config.C.StoragePool)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
return conn, pool, nil
|
||
|
}
|
||
|
|
||
|
func getServerListenAddress(conn *libvirt.Connect) (string, error) {
|
||
|
serverInterfaceName := ""
|
||
|
if config.C.Network != "" {
|
||
|
nicSource.Network = &libvirtxml.DomainInterfaceSourceNetwork{Network: config.C.Network}
|
||
|
libvirtnet, err := conn.LookupNetworkByName(config.C.Network)
|
||
|
if err != nil {
|
||
|
logrus.WithField("network", config.C.Network).Error("error finding libvirt network")
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
xmlstr, err := libvirtnet.GetXMLDesc(0)
|
||
|
if err != nil {
|
||
|
logrus.WithField("network", config.C.Network).Error("error getting network xml description")
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
var net libvirtxml.Network
|
||
|
if err := net.Unmarshal(xmlstr); err != nil {
|
||
|
logrus.WithField("network", config.C.Network).Error("error parsing network xml description")
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
serverInterfaceName = net.Bridge.Name
|
||
|
} else if config.C.Bridge != "" {
|
||
|
nicSource.Bridge = &libvirtxml.DomainInterfaceSourceBridge{Bridge: config.C.Bridge}
|
||
|
serverInterfaceName = config.C.Bridge
|
||
|
} else {
|
||
|
return "", errors.New("no network or bridge configured")
|
||
|
}
|
||
|
|
||
|
serverInterface, err := net.InterfaceByName(serverInterfaceName)
|
||
|
if err != nil {
|
||
|
logrus.Error("error finding local network interface to run server on")
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
serverInterfaceAddrs, err := serverInterface.Addrs()
|
||
|
if err != nil {
|
||
|
logrus.Error("error finding local network interface's IP")
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
if len(serverInterfaceAddrs) == 0 {
|
||
|
return "", fmt.Errorf("bridge interface %s does not have an IP on this machine", serverInterfaceName)
|
||
|
}
|
||
|
|
||
|
serverBindIP, _, err := net.ParseCIDR(serverInterfaceAddrs[0].String())
|
||
|
if err != nil {
|
||
|
logrus.WithField("interface", serverInterfaceName).WithField("address", serverInterfaceAddrs[0].String()).Error("error parsing local address")
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
port := rand.Intn(65535-1025) + 1025
|
||
|
return fmt.Sprintf("%s:%d", serverBindIP, port), nil
|
||
|
}
|
||
|
|
||
|
func createDomain(conn *libvirt.Connect, pool pools.StoragePool, name string, modifiers ...domainModifier) error {
|
||
|
interfaces := []libvirtxml.DomainInterface{
|
||
|
{
|
||
|
Model: &libvirtxml.DomainInterfaceModel{Type: "virtio"},
|
||
|
Source: &nicSource,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// Create the domain
|
||
|
domainXML := &libvirtxml.Domain{
|
||
|
Type: "kvm",
|
||
|
Name: name,
|
||
|
Memory: &libvirtxml.DomainMemory{Value: uint(argMemoryMB), Unit: "MiB"},
|
||
|
VCPU: &libvirtxml.DomainVCPU{Value: uint(argCPUs)},
|
||
|
OS: &libvirtxml.DomainOS{
|
||
|
Type: &libvirtxml.DomainOSType{Arch: "x86_64", Type: "hvm"},
|
||
|
BootDevices: []libvirtxml.DomainBootDevice{{Dev: "hd"}},
|
||
|
},
|
||
|
Features: &libvirtxml.DomainFeatureList{
|
||
|
ACPI: &libvirtxml.DomainFeature{},
|
||
|
APIC: &libvirtxml.DomainFeatureAPIC{},
|
||
|
VMPort: &libvirtxml.DomainFeatureState{State: "off"},
|
||
|
},
|
||
|
CPU: &libvirtxml.DomainCPU{Mode: "host-model"},
|
||
|
Devices: &libvirtxml.DomainDeviceList{
|
||
|
Emulator: "/usr/bin/kvm",
|
||
|
Disks: []libvirtxml.DomainDisk{pool.GetDomainDiskXML(name)},
|
||
|
Channels: []libvirtxml.DomainChannel{
|
||
|
{
|
||
|
Source: &libvirtxml.DomainChardevSource{
|
||
|
UNIX: &libvirtxml.DomainChardevSourceUNIX{Path: "/var/lib/libvirt/qemu/f16x86_64.agent", Mode: "bind"},
|
||
|
},
|
||
|
Target: &libvirtxml.DomainChannelTarget{
|
||
|
VirtIO: &libvirtxml.DomainChannelTargetVirtIO{Name: "org.qemu.guest_agent.0"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
Consoles: []libvirtxml.DomainConsole{{Target: &libvirtxml.DomainConsoleTarget{}}},
|
||
|
Serials: []libvirtxml.DomainSerial{{}},
|
||
|
Interfaces: interfaces,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, modifier := range modifiers {
|
||
|
if err := modifier(domainXML); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
domainXMLString, err := domainXML.Marshal()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
logrus.Debug("defining domain from xml")
|
||
|
domain, err := conn.DomainDefineXML(domainXMLString)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error defining domain from xml description: %v", err)
|
||
|
}
|
||
|
|
||
|
logrus.Debug("booting domain")
|
||
|
err = domain.Create()
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error creating domain: %v", err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func setSMBIOS(smbios map[int]map[string]string) domainModifier {
|
||
|
return func(d *libvirtxml.Domain) error {
|
||
|
qemuArgs := []libvirtxml.DomainQEMUCommandlineArg{}
|
||
|
if d.QEMUCommandline != nil {
|
||
|
qemuArgs = d.QEMUCommandline.Args
|
||
|
}
|
||
|
|
||
|
for smbiosType, values := range smbios {
|
||
|
arg := libvirtxml.DomainQEMUCommandlineArg{
|
||
|
Value: fmt.Sprintf("type=%d", smbiosType),
|
||
|
}
|
||
|
for key, value := range values {
|
||
|
arg.Value = fmt.Sprintf("%s,%s=%s", arg.Value, key, value)
|
||
|
}
|
||
|
qemuArgs = append(qemuArgs, libvirtxml.DomainQEMUCommandlineArg{Value: "-smbios"}, arg)
|
||
|
}
|
||
|
|
||
|
d.QEMUCommandline = &libvirtxml.DomainQEMUCommandline{Args: qemuArgs}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
}
|