summaryrefslogtreecommitdiffstats
path: root/src/dma/cmd/infofetch/virsh_domain.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/dma/cmd/infofetch/virsh_domain.go')
-rw-r--r--src/dma/cmd/infofetch/virsh_domain.go264
1 files changed, 264 insertions, 0 deletions
diff --git a/src/dma/cmd/infofetch/virsh_domain.go b/src/dma/cmd/infofetch/virsh_domain.go
new file mode 100644
index 00000000..b79f5bdd
--- /dev/null
+++ b/src/dma/cmd/infofetch/virsh_domain.go
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2017 Red Hat
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "encoding/xml"
+ "fmt"
+ libvirt "github.com/libvirt/libvirt-go"
+ "log"
+)
+
+type instance struct {
+ Name string `xml:"name"`
+ Owner struct {
+ User string `xml:"user"`
+ Project string `xml:"project"`
+ } `xml:"owner"`
+ Flavor struct {
+ Name string `xml:"name,attr"`
+ } `xml:"flavor"`
+}
+
+type domain struct {
+ Name string `xml:"name"`
+ Devices struct {
+ Interfaces []struct {
+ Type string `xml:"type,attr"`
+ Mac struct {
+ Address string `xml:"address,attr"`
+ } `xml:"mac"`
+ Target struct {
+ Dev string `xml:"dev,attr"`
+ } `xml:"target"`
+ } `xml:"interface"`
+ } `xml:"devices"`
+}
+
+type osVMAnnotation struct {
+ Name string
+ Owner string
+ Project string
+ Flavor string
+}
+
+type osVMInterfaceAnnotation struct {
+ Type string
+ MacAddr string
+ Target string
+ VMName string
+}
+
+func parseNovaMetadata(metadata string) (*osVMAnnotation, error) {
+ data := new(instance)
+
+ if err := xml.Unmarshal([]byte(metadata), data); err != nil {
+ log.Println("XML Unmarshal error:", err)
+ return nil, err
+ }
+ log.Printf("Get name: %s user: %s, project: %s, flavor: %s", data.Name, data.Owner.User, data.Owner.Project, data.Flavor.Name)
+ return &osVMAnnotation{
+ Name: data.Name,
+ Owner: data.Owner.User,
+ Project: data.Owner.Project,
+ Flavor: data.Flavor.Name}, nil
+}
+
+func parseXMLForMAC(dumpxml string) (*[]osVMInterfaceAnnotation, error) {
+ data := new(domain)
+
+ if err := xml.Unmarshal([]byte(dumpxml), data); err != nil {
+ log.Println("XML Unmarshal error:", err)
+ return nil, err
+ }
+
+ ifAnnotation := make([]osVMInterfaceAnnotation, len(data.Devices.Interfaces))
+ for i, v := range data.Devices.Interfaces {
+ log.Printf("Interface type: %s, mac_addr: %s, target_dev: %s", v.Type, v.Mac.Address, v.Target.Dev)
+ ifAnnotation[i] = osVMInterfaceAnnotation{
+ Type: v.Type,
+ MacAddr: v.Mac.Address,
+ Target: v.Target.Dev,
+ VMName: data.Name}
+ }
+ return &ifAnnotation, nil
+}
+
+func setInterfaceAnnotation(ifInfo *[]osVMInterfaceAnnotation, vmIfInfoChan chan string) {
+ for _, v := range *ifInfo {
+ ifInfoJSON, err := json.Marshal(v)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ }
+ infoPool.Set(fmt.Sprintf("if/%s/%s", v.Target, "network"), string(ifInfoJSON))
+
+ vmIfInfoChan <- fmt.Sprintf("if/%s/%s", v.Target, "network")
+ }
+ return
+}
+
+func domainEventLifecycleCallback(vmIfInfo chan string) func(c *libvirt.Connect, d *libvirt.Domain, event *libvirt.DomainEventLifecycle) {
+
+ return func(c *libvirt.Connect,
+ d *libvirt.Domain, event *libvirt.DomainEventLifecycle) {
+ domName, _ := d.GetName()
+
+ switch event.Event {
+ case libvirt.DOMAIN_EVENT_DEFINED:
+ // VM defined: vmname (libvirt, nova), user, project, flavor
+ // Redis: <vnname>/vminfo
+ log.Printf("Event defined: domName: %s, event: %v", domName, event)
+ metadata, err := d.GetMetadata(libvirt.DOMAIN_METADATA_ELEMENT, "http://openstack.org/xmlns/libvirt/nova/1.0", libvirt.DOMAIN_AFFECT_CONFIG)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ }
+ vmInfo, err := parseNovaMetadata(metadata)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ }
+ vmInfoJSON, err := json.Marshal(vmInfo)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ }
+ infoPool.Set(fmt.Sprintf("vm/%s/%s", domName, "vminfo"), string(vmInfoJSON))
+ case libvirt.DOMAIN_EVENT_STARTED:
+ // VM started: interface type, interface mac addr, intarface type
+ // Redis: <vnname>/interfaces
+ log.Printf("Event started: domName: %s, event: %v", domName, event)
+
+ xml, err := d.GetXMLDesc(0)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ }
+ ifInfo, err := parseXMLForMAC(xml)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ }
+ setInterfaceAnnotation(ifInfo, vmIfInfo)
+
+ ifInfoJSON, err := json.Marshal(ifInfo)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ }
+ infoPool.Set(fmt.Sprintf("vm/%s/%s", domName, "interfaces"), string(ifInfoJSON))
+ case libvirt.DOMAIN_EVENT_UNDEFINED:
+ log.Printf("Event undefined: domName: %s, event: %v", domName, event)
+ vmIFInfo, err := infoPool.Get(fmt.Sprintf("vm/%s/%s", domName, "interfaces"))
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ } else {
+ var interfaces []osVMInterfaceAnnotation
+ err = json.Unmarshal([]byte(vmIFInfo), &interfaces)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ } else {
+ for _, v := range interfaces {
+ infoPool.Del(fmt.Sprintf("if/%s/%s", v.Target, "network"))
+ infoPool.Del(fmt.Sprintf("if/%s/%s", v.Target, "neutron_network"))
+ }
+ }
+ }
+ infoPool.Del(fmt.Sprintf("vm/%s/%s", domName, "vminfo"))
+ infoPool.Del(fmt.Sprintf("vm/%s/%s", domName, "interfaces"))
+ default:
+ log.Printf("Event misc: domName: %s, event: %v", domName, event)
+ }
+ }
+}
+
+// GetActiveDomain gets all active domain information from libvirt and it should be called at startup to get
+// current running domain information
+func GetActiveDomain(conn *libvirt.Connect, vmIfInfoChan chan string) error {
+ doms, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE)
+ if err != nil {
+ log.Fatalf("libvirt dom list error: %s", err)
+ return err
+ }
+
+ for _, d := range doms {
+ name, err := d.GetName()
+
+ // Get VM Info
+ metadata, err := d.GetMetadata(libvirt.DOMAIN_METADATA_ELEMENT, "http://openstack.org/xmlns/libvirt/nova/1.0", libvirt.DOMAIN_AFFECT_CONFIG)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ return err
+ }
+ vmInfo, err := parseNovaMetadata(metadata)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ return err
+ }
+ vmInfoJSON, err := json.Marshal(vmInfo)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ return err
+ }
+ infoPool.Set(fmt.Sprintf("vm/%s/%s", name, "vminfo"), string(vmInfoJSON))
+
+ // Get Network info
+ xml, err := d.GetXMLDesc(0)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ return err
+ }
+ ifInfo, err := parseXMLForMAC(xml)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ return err
+ }
+ setInterfaceAnnotation(ifInfo, vmIfInfoChan)
+
+ ifInfoJSON, err := json.Marshal(ifInfo)
+ if err != nil {
+ log.Fatalf("Err: %v", err)
+ return err
+ }
+ infoPool.Set(fmt.Sprintf("vm/%s/%s", name, "interfaces"), string(ifInfoJSON))
+ }
+ return nil
+}
+
+// RunVirshEventLoop is event loop to watch libvirt update
+func RunVirshEventLoop(ctx context.Context, conn *libvirt.Connect, vmIfInfoChan chan string) error {
+ callbackID, err := conn.DomainEventLifecycleRegister(nil, domainEventLifecycleCallback(vmIfInfoChan))
+ if err != nil {
+ log.Fatalf("Err: callbackid: %d %v", callbackID, err)
+ }
+
+ libvirt.EventAddTimeout(5000, func(timer int) { return }) // 5000 = 5sec
+ log.Printf("Entering libvirt event loop()")
+EVENTLOOP:
+ for {
+ select {
+ case <-ctx.Done():
+ break EVENTLOOP
+ default:
+ if err := libvirt.EventRunDefaultImpl(); err != nil {
+ log.Fatalf("%v", err)
+ }
+ }
+ }
+ log.Printf("Quitting libvirt event loop()")
+
+ if err := conn.DomainEventDeregister(callbackID); err != nil {
+ log.Fatalf("%v", err)
+ }
+ return nil
+}