diff options
Diffstat (limited to 'src/dma/cmd/infofetch/virsh_domain.go')
-rw-r--r-- | src/dma/cmd/infofetch/virsh_domain.go | 264 |
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 +} |