summaryrefslogtreecommitdiff
path: root/importDomain.go
blob: b629bcea612a614b2f8aeb7ab28f23283701b76f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package main

import (
	"errors"
	"fmt"
	"net/http"
	"os"
	"time"

	"go.wit.com/lib/protobuf/virtpb"
	"go.wit.com/lib/virtigolib"
	"go.wit.com/log"

	// "libvirt.org/go/libvirt"
	"libvirt.org/go/libvirtxml"
)

// attempts to import the *libvirt.Domain directly from the hypervisor

func importDomain(w http.ResponseWriter, r *http.Request) (string, error) {
	var result string
	domainName := r.URL.Query().Get("domainName")
	force := r.URL.Query().Get("force")
	if domainName == "" {
		result = "importDomain() failed. name is blank " + r.URL.Path
		log.Warn(result)
		fmt.Fprintln(w, result)
		return "", errors.New(result)
	}

	// a LocalOnly record should already have been created by hypervisor.Poll()
	d := me.cluster.FindDropletByName(domainName)
	if d == nil {
		result = "libvirt domain " + domainName + " could not be found on any hypervisor\n"
		log.Info(result)
		fmt.Fprintln(w, result)
		return result, errors.New(result)
	}

	// if it's not local only, don't attempt this for now
	if d.LocalOnly == "" {
		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)
		}
	}

	/*
		// 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
	h := findHypervisorByName(d.Current.Hypervisor)
	if h == nil {
		result = "unknown hypervisor = " + d.Current.Hypervisor
		log.Log(WARN, result)
		fmt.Fprintln(w, result)
		return result, errors.New(result)
	}

	// exports and builds a libvirt.Domain from the hypervisor
	domcfg, err := ExportLibvirtDomain(h.pb, domainName)
	if err != nil {
		result = fmt.Sprint("ExportLibvirtDomain() failed", err)
		log.Warn(result)
		fmt.Fprintln(w, result)
		return "", err
	}

	// merges and updates the droplet protobuf based on the libvirt XML
	events, err := virtigolib.MergelibvirtDomain(d, domcfg)
	if err != nil {
		result = fmt.Sprint("MerglibvirtDomain() failed for", d.Hostname, err)
		log.Warn(result)
		fmt.Fprintln(w, result)
		return "", errors.New(result)
	}

	// check what was non-standard and make a note of it. Save it in the protobuf
	s, err := virtigolib.DumpNonStandardXML(domcfg)
	if err != nil {
		result = s + "\n"
		result = fmt.Sprintln("DumpNonStandardXML() on", domcfg.Name, "failed for", err)
		log.Info(result)
		return "", err
	}
	result += s

	// everything worked. add the events
	for _, e := range events {
		me.cluster.AddEvent(e)
	}

	result += fmt.Sprintln("importDomain() worked")

	// remote LocalOnly flag
	d.LocalOnly = ""

	// probably be safe and don't let this move around the cluster
	d.PreferredHypervisor = d.Current.Hypervisor

	log.Log(WARN, result)
	fmt.Fprintln(w, result)
	log.Warn("Everything worked. Saving config files")
	if err := me.cluster.ConfigSave(); err != nil {
		log.Warn("configsave error", err)
		os.Exit(-1)
	}
	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 *virtpb.Droplet) (bool, string) {
	ready, result := me.cluster.DropletReady(d)
	if !ready {
		return false, result
	}

	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
}

func ExportLibvirtDomain(h *virtpb.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
}

func postImportDomain(hypervisor string, domain string) ([]byte, error) {
	url := "http://" + hypervisor + ":2520/import?domain=" + domain
	var msg string
	var data []byte
	msg = "import " + domain
	data = []byte(msg) // Convert the string to []byte
	req, err := httpPost(url, data)
	if err != nil {
		return nil, err
	}

	return req, nil
}