2024-10-31 15:43:25 -05:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2024-11-01 08:03:20 -05:00
|
|
|
"os"
|
2024-11-01 05:50:42 -05:00
|
|
|
"time"
|
2024-10-31 15:43:25 -05:00
|
|
|
|
2024-10-31 22:14:11 -05:00
|
|
|
pb "go.wit.com/lib/protobuf/virtbuf"
|
2024-11-01 06:41:20 -05:00
|
|
|
"go.wit.com/lib/virtigolib"
|
2024-10-31 15:43:25 -05:00
|
|
|
"go.wit.com/log"
|
2024-11-01 08:03:20 -05:00
|
|
|
|
|
|
|
// "libvirt.org/go/libvirt"
|
2024-11-01 05:50:42 -05:00
|
|
|
"libvirt.org/go/libvirtxml"
|
2024-10-31 15:43:25 -05:00
|
|
|
)
|
|
|
|
|
2024-11-01 08:03:20 -05:00
|
|
|
// attempts to import the *libvirt.Domain directly from the hypervisor
|
2024-10-31 15:43:25 -05:00
|
|
|
|
|
|
|
func importDomain(w http.ResponseWriter, r *http.Request) (string, error) {
|
2024-11-01 11:17:52 -05:00
|
|
|
var result string
|
2024-11-01 08:03:20 -05:00
|
|
|
domainName := r.URL.Query().Get("domainName")
|
2024-11-01 12:48:31 -05:00
|
|
|
force := r.URL.Query().Get("force")
|
2024-11-01 08:03:20 -05:00
|
|
|
if domainName == "" {
|
2024-11-01 11:17:52 -05:00
|
|
|
result = "importDomain() failed. name is blank " + r.URL.Path
|
2024-10-31 15:43:25 -05:00
|
|
|
log.Warn(result)
|
|
|
|
fmt.Fprintln(w, result)
|
|
|
|
return "", errors.New(result)
|
|
|
|
}
|
|
|
|
|
2024-11-01 08:03:20 -05:00
|
|
|
// a LocalOnly record should already have been created by hypervisor.Poll()
|
|
|
|
d := me.cluster.FindDropletByName(domainName)
|
2024-10-31 15:43:25 -05:00
|
|
|
if d == nil {
|
2024-11-01 12:48:31 -05:00
|
|
|
result = "libvirt domain " + domainName + " could not be found on any hypervisor\n"
|
2024-10-31 15:43:25 -05:00
|
|
|
log.Info(result)
|
|
|
|
fmt.Fprintln(w, result)
|
|
|
|
return result, errors.New(result)
|
|
|
|
}
|
2024-11-01 08:03:20 -05:00
|
|
|
|
|
|
|
// if it's not local only, don't attempt this for now
|
2024-11-01 02:00:46 -05:00
|
|
|
if d.LocalOnly == "" {
|
2024-11-01 12:48:31 -05:00
|
|
|
if force == "true" {
|
|
|
|
result = "LocalOnly is blank. force=true. PROCEEDING WITH DANGER\n"
|
|
|
|
log.Warn(result)
|
|
|
|
fmt.Fprint(w, result)
|
|
|
|
} else {
|
|
|
|
result = "LocalOnly is blank. SKIP. merge not supported yet. force=" + force
|
|
|
|
log.Log(WARN, result)
|
|
|
|
fmt.Fprintln(w, result)
|
|
|
|
return result, errors.New(result)
|
|
|
|
}
|
2024-11-01 02:00:46 -05:00
|
|
|
}
|
|
|
|
|
2024-11-01 08:03:20 -05:00
|
|
|
/*
|
|
|
|
// it probably doesn't really matter what the state it
|
|
|
|
if d.Current.State != pb.DropletState_OFF {
|
|
|
|
result := "error: libvirt domain " + name + " is not off"
|
|
|
|
log.Info(result)
|
|
|
|
fmt.Fprintln(w, result)
|
|
|
|
return result, errors.New(result)
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
// get the hypervisor record for what it's worth
|
2024-11-01 02:00:46 -05:00
|
|
|
h := findHypervisorByName(d.Current.Hypervisor)
|
|
|
|
if h == nil {
|
2024-11-01 11:17:52 -05:00
|
|
|
result = "unknown hypervisor = " + d.Current.Hypervisor
|
2024-10-31 22:14:11 -05:00
|
|
|
log.Log(WARN, result)
|
|
|
|
fmt.Fprintln(w, result)
|
2024-11-01 02:00:46 -05:00
|
|
|
return result, errors.New(result)
|
2024-10-31 22:14:11 -05:00
|
|
|
}
|
2024-11-01 05:50:42 -05:00
|
|
|
|
2024-11-01 08:03:20 -05:00
|
|
|
// exports and builds a libvirt.Domain from the hypervisor
|
|
|
|
domcfg, err := ExportLibvirtDomain(h.pb, domainName)
|
2024-11-01 05:50:42 -05:00
|
|
|
if err != nil {
|
2024-11-01 11:17:52 -05:00
|
|
|
result = fmt.Sprintf("ExportLibvirtDomain() failed", err)
|
|
|
|
log.Warn(result)
|
|
|
|
fmt.Fprintln(w, result)
|
2024-11-01 05:50:42 -05:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2024-11-01 08:03:20 -05:00
|
|
|
// merges and updates the droplet protobuf based on the libvirt XML
|
|
|
|
events, err := virtigolib.MergelibvirtDomain(d, domcfg)
|
2024-11-01 05:50:42 -05:00
|
|
|
if err != nil {
|
2024-11-01 11:17:52 -05:00
|
|
|
result = fmt.Sprintf("MerglibvirtDomain() failed for", d.Hostname, err)
|
|
|
|
log.Warn(result)
|
|
|
|
fmt.Fprintln(w, result)
|
|
|
|
return "", errors.New(result)
|
2024-11-01 05:50:42 -05:00
|
|
|
}
|
2024-11-01 08:03:20 -05:00
|
|
|
|
|
|
|
// check what was non-standard and make a note of it. Save it in the protobuf
|
|
|
|
s, err := virtigolib.DumpNonStandardXML(domcfg)
|
2024-11-01 06:41:20 -05:00
|
|
|
if err != nil {
|
2024-11-01 11:17:52 -05:00
|
|
|
result = s + "\n"
|
|
|
|
result = fmt.Sprintln("DumpNonStandardXML() on", domcfg.Name, "failed for", err)
|
|
|
|
log.Info(result)
|
2024-11-01 06:41:20 -05:00
|
|
|
return "", err
|
|
|
|
}
|
2024-11-01 11:17:52 -05:00
|
|
|
result += s
|
2024-11-01 08:03:20 -05:00
|
|
|
|
|
|
|
// everything worked. add the events
|
|
|
|
for _, e := range events {
|
2024-11-01 06:41:20 -05:00
|
|
|
me.cluster.AddEvent(e)
|
|
|
|
}
|
2024-11-01 05:50:42 -05:00
|
|
|
|
2024-11-01 11:17:52 -05:00
|
|
|
result += fmt.Sprintln("importDomain() worked")
|
2024-11-01 08:51:25 -05:00
|
|
|
|
|
|
|
// remote LocalOnly flag
|
|
|
|
d.LocalOnly = ""
|
|
|
|
|
|
|
|
// probably be safe and don't let this move around the cluster
|
|
|
|
d.PreferredHypervisor = d.Current.Hypervisor
|
|
|
|
|
2024-10-31 22:14:11 -05:00
|
|
|
log.Log(WARN, result)
|
2024-10-31 15:43:25 -05:00
|
|
|
fmt.Fprintln(w, result)
|
2024-11-01 08:51:25 -05:00
|
|
|
log.Warn("Everything worked. Saving config files")
|
|
|
|
if err := me.cluster.ConfigSave(); err != nil {
|
|
|
|
log.Warn("configsave error", err)
|
|
|
|
os.Exit(-1)
|
|
|
|
}
|
2024-11-01 05:50:42 -05:00
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// this must be bool in string because accumulated output is sometimes
|
|
|
|
// written to STDOUT, sometimes to http
|
|
|
|
func (h *HyperT) importDomain(d *pb.Droplet) (bool, string) {
|
|
|
|
ready, result := me.cluster.DropletReady(d)
|
|
|
|
if !ready {
|
|
|
|
return false, result
|
2024-11-01 02:00:46 -05:00
|
|
|
}
|
2024-11-01 05:50:42 -05:00
|
|
|
|
|
|
|
url := "http://" + h.pb.Hostname + ":2520/import?domain=" + d.Hostname
|
|
|
|
var msg string
|
|
|
|
var data []byte
|
|
|
|
msg = d.FormatJSON()
|
|
|
|
data = []byte(msg) // Convert the string to []byte
|
|
|
|
req, err := httpPost(url, data)
|
|
|
|
if err != nil {
|
|
|
|
return false, fmt.Sprintln("error:", err)
|
|
|
|
}
|
|
|
|
log.Info("http post url:", url)
|
|
|
|
log.Info("http post data:", msg)
|
|
|
|
|
|
|
|
result = "EVENT import droplet url: " + url + "\n"
|
|
|
|
result += "EVENT import droplet response: " + string(req)
|
|
|
|
|
|
|
|
// increment the counter for a start attempt working
|
|
|
|
d.Current.StartAttempts += 1
|
|
|
|
|
|
|
|
// mark the cluster as unstable so droplet starts can be throttled
|
|
|
|
me.unstable = time.Now()
|
|
|
|
|
|
|
|
return true, result
|
|
|
|
}
|
|
|
|
|
2024-11-01 08:03:20 -05:00
|
|
|
func ExportLibvirtDomain(h *pb.Hypervisor, domainName string) (*libvirtxml.Domain, error) {
|
|
|
|
// attempt to get the domain record from virtigo
|
|
|
|
xml, err := postImportDomain(h.Hostname, domainName)
|
|
|
|
if err != nil {
|
|
|
|
log.Warn(err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert the xml into a libvirt object
|
|
|
|
domcfg := &libvirtxml.Domain{}
|
|
|
|
err = domcfg.Unmarshal(string(xml))
|
|
|
|
if err != nil {
|
|
|
|
log.Warn("Unmarshal failed", domainName, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return domcfg, nil
|
|
|
|
}
|
|
|
|
|
2024-11-01 06:12:12 -05:00
|
|
|
func postImportDomain(hypervisor string, domain string) ([]byte, error) {
|
|
|
|
url := "http://" + hypervisor + ":2520/import?domain=" + domain
|
2024-11-01 05:50:42 -05:00
|
|
|
var msg string
|
|
|
|
var data []byte
|
2024-11-01 06:12:12 -05:00
|
|
|
msg = "import " + domain
|
2024-11-01 05:50:42 -05:00
|
|
|
data = []byte(msg) // Convert the string to []byte
|
|
|
|
req, err := httpPost(url, data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
2024-10-31 15:43:25 -05:00
|
|
|
}
|