2 * Copyright 2017 Red Hat
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
24 libvirt "github.com/libvirt/libvirt-go"
28 type instance struct {
29 Name string `xml:"name"`
31 User string `xml:"user"`
32 Project string `xml:"project"`
35 Name string `xml:"name,attr"`
40 Name string `xml:"name"`
43 Type string `xml:"type,attr"`
45 Address string `xml:"address,attr"`
48 Dev string `xml:"dev,attr"`
54 type osVMAnnotation struct {
61 type osVMInterfaceAnnotation struct {
68 func parseNovaMetadata(metadata string) (*osVMAnnotation, error) {
71 if err := xml.Unmarshal([]byte(metadata), data); err != nil {
72 log.Println("XML Unmarshal error:", err)
75 log.Printf("Get name: %s user: %s, project: %s, flavor: %s", data.Name, data.Owner.User, data.Owner.Project, data.Flavor.Name)
76 return &osVMAnnotation{
78 Owner: data.Owner.User,
79 Project: data.Owner.Project,
80 Flavor: data.Flavor.Name}, nil
83 func parseXMLForMAC(dumpxml string) (*[]osVMInterfaceAnnotation, error) {
86 if err := xml.Unmarshal([]byte(dumpxml), data); err != nil {
87 log.Println("XML Unmarshal error:", err)
91 ifAnnotation := make([]osVMInterfaceAnnotation, len(data.Devices.Interfaces))
92 for i, v := range data.Devices.Interfaces {
93 log.Printf("Interface type: %s, mac_addr: %s, target_dev: %s", v.Type, v.Mac.Address, v.Target.Dev)
94 ifAnnotation[i] = osVMInterfaceAnnotation{
96 MacAddr: v.Mac.Address,
100 return &ifAnnotation, nil
103 func setInterfaceAnnotation(ifInfo *[]osVMInterfaceAnnotation, vmIfInfoChan chan string) {
104 for _, v := range *ifInfo {
105 ifInfoJSON, err := json.Marshal(v)
107 log.Fatalf("Err: %v", err)
109 infoPool.Set(fmt.Sprintf("if/%s/%s", v.Target, "network"), string(ifInfoJSON))
111 vmIfInfoChan <- fmt.Sprintf("if/%s/%s", v.Target, "network")
116 func domainEventLifecycleCallback(vmIfInfo chan string) func(c *libvirt.Connect, d *libvirt.Domain, event *libvirt.DomainEventLifecycle) {
118 return func(c *libvirt.Connect,
119 d *libvirt.Domain, event *libvirt.DomainEventLifecycle) {
120 domName, _ := d.GetName()
123 case libvirt.DOMAIN_EVENT_DEFINED:
124 // VM defined: vmname (libvirt, nova), user, project, flavor
125 // Redis: <vnname>/vminfo
126 log.Printf("Event defined: domName: %s, event: %v", domName, event)
127 metadata, err := d.GetMetadata(libvirt.DOMAIN_METADATA_ELEMENT, "http://openstack.org/xmlns/libvirt/nova/1.0", libvirt.DOMAIN_AFFECT_CONFIG)
129 log.Fatalf("Err: %v", err)
131 vmInfo, err := parseNovaMetadata(metadata)
133 log.Fatalf("Err: %v", err)
135 vmInfoJSON, err := json.Marshal(vmInfo)
137 log.Fatalf("Err: %v", err)
139 infoPool.Set(fmt.Sprintf("vm/%s/%s", domName, "vminfo"), string(vmInfoJSON))
140 case libvirt.DOMAIN_EVENT_STARTED:
141 // VM started: interface type, interface mac addr, intarface type
142 // Redis: <vnname>/interfaces
143 log.Printf("Event started: domName: %s, event: %v", domName, event)
145 xml, err := d.GetXMLDesc(0)
147 log.Fatalf("Err: %v", err)
149 ifInfo, err := parseXMLForMAC(xml)
151 log.Fatalf("Err: %v", err)
153 setInterfaceAnnotation(ifInfo, vmIfInfo)
155 ifInfoJSON, err := json.Marshal(ifInfo)
157 log.Fatalf("Err: %v", err)
159 infoPool.Set(fmt.Sprintf("vm/%s/%s", domName, "interfaces"), string(ifInfoJSON))
160 case libvirt.DOMAIN_EVENT_UNDEFINED:
161 log.Printf("Event undefined: domName: %s, event: %v", domName, event)
162 vmIFInfo, err := infoPool.Get(fmt.Sprintf("vm/%s/%s", domName, "interfaces"))
164 log.Fatalf("Err: %v", err)
166 var interfaces []osVMInterfaceAnnotation
167 err = json.Unmarshal([]byte(vmIFInfo), &interfaces)
169 log.Fatalf("Err: %v", err)
171 for _, v := range interfaces {
172 infoPool.Del(fmt.Sprintf("if/%s/%s", v.Target, "network"))
173 infoPool.Del(fmt.Sprintf("if/%s/%s", v.Target, "neutron_network"))
177 infoPool.Del(fmt.Sprintf("vm/%s/%s", domName, "vminfo"))
178 infoPool.Del(fmt.Sprintf("vm/%s/%s", domName, "interfaces"))
180 log.Printf("Event misc: domName: %s, event: %v", domName, event)
185 // GetActiveDomain gets all active domain information from libvirt and it should be called at startup to get
186 // current running domain information
187 func GetActiveDomain(conn *libvirt.Connect, vmIfInfoChan chan string) error {
188 doms, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE)
190 log.Fatalf("libvirt dom list error: %s", err)
194 for _, d := range doms {
195 name, err := d.GetName()
198 metadata, err := d.GetMetadata(libvirt.DOMAIN_METADATA_ELEMENT, "http://openstack.org/xmlns/libvirt/nova/1.0", libvirt.DOMAIN_AFFECT_CONFIG)
200 log.Fatalf("Err: %v", err)
203 vmInfo, err := parseNovaMetadata(metadata)
205 log.Fatalf("Err: %v", err)
208 vmInfoJSON, err := json.Marshal(vmInfo)
210 log.Fatalf("Err: %v", err)
213 infoPool.Set(fmt.Sprintf("vm/%s/%s", name, "vminfo"), string(vmInfoJSON))
216 xml, err := d.GetXMLDesc(0)
218 log.Fatalf("Err: %v", err)
221 ifInfo, err := parseXMLForMAC(xml)
223 log.Fatalf("Err: %v", err)
226 setInterfaceAnnotation(ifInfo, vmIfInfoChan)
228 ifInfoJSON, err := json.Marshal(ifInfo)
230 log.Fatalf("Err: %v", err)
233 infoPool.Set(fmt.Sprintf("vm/%s/%s", name, "interfaces"), string(ifInfoJSON))
238 // RunVirshEventLoop is event loop to watch libvirt update
239 func RunVirshEventLoop(ctx context.Context, conn *libvirt.Connect, vmIfInfoChan chan string) error {
240 callbackID, err := conn.DomainEventLifecycleRegister(nil, domainEventLifecycleCallback(vmIfInfoChan))
242 log.Fatalf("Err: callbackid: %d %v", callbackID, err)
245 libvirt.EventAddTimeout(5000, func(timer int) { return }) // 5000 = 5sec
246 log.Printf("Entering libvirt event loop()")
253 if err := libvirt.EventRunDefaultImpl(); err != nil {
254 log.Fatalf("%v", err)
258 log.Printf("Quitting libvirt event loop()")
260 if err := conn.DomainEventDeregister(callbackID); err != nil {
261 log.Fatalf("%v", err)