2 #############################################################################
3 #Copyright 2017 Parker Berberian and others #
5 #Licensed under the Apache License, Version 2.0 (the "License"); #
6 #you may not use this file except in compliance with the License. #
7 #You may obtain a copy of the License at #
9 # http://www.apache.org/licenses/LICENSE-2.0 #
11 #Unless required by applicable law or agreed to in writing, software #
12 #distributed under the License is distributed on an "AS IS" BASIS, #
13 #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
14 #See the License for the specific language governing permissions and #
15 #limitations under the License. #
16 #############################################################################
22 import xml.dom.minidom
23 from domain import Domain
24 from network import Network
25 from utilities import Utilities
30 This class talks to the Libvirt api.
31 Given a config file, this class should create all networks and
34 TODO: convert prints to logging and remove uneeded pass statements
37 def __init__(self, hostAddr, net_conf=None, dom_conf=None):
40 hostAddr is the ip address of the host
41 net_conf and dom_conf are the paths
45 self.URI = "qemu+ssh://root@"+str(hostAddr)+"/system"
46 self.hypervisor = None
49 self.net_conf = net_conf
50 self.dom_conf = dom_conf
52 def setLogger(self, log):
54 Saves the logger in self.log
60 starts the previously defined master node
62 for dom in self.domains:
63 if 'master' in dom.name():
71 boots every defined vm with 'slave' in its name
73 for dom in self.domains:
74 if 'slave' in dom.name():
77 self.log.info("Booting %s", dom.name())
79 self.log.exception("%s", "failed to boot domain")
82 def getMacs(self, domName):
84 returns a dictionary with a network name
85 mapped to the mac address of the domain on that net
88 dom = self.hypervisor.lookupByName(domName)
89 xmlDesc = dom.XMLDesc(0)
90 parsedXML = xml.dom.minidom.parseString(xmlDesc)
91 interfacesXML = parsedXML.getElementsByTagName('interface')
93 for iface in interfacesXML:
94 src = iface.getElementsByTagName('source')[0]
95 mac = iface.getElementsByTagName('mac')[0]
99 self.log.exception("%s", "Domain not found")
101 def defineVM(self, xmlConfig):
103 Generic method to define a persistent vm with the
105 Assumes that self.hypervisor is already connected.
107 if self.checkForVM(xmlConfig):
108 vm = self.hypervisor.defineXML(xmlConfig)
110 name = self.getName(xmlConfig)
111 self.log.error("Failed to define vm %s. exiting", name)
114 self.log.info("Successfully created vm %s", vm.name())
116 self.domains.append(vm)
118 def checkForVM(self, xmlConfig):
120 Checks if another vm with the same name exists
121 on the remote host already. If it does, it will
125 vms = self.hypervisor.listAllDomains(0)
128 names.append(dom.name())
129 vmName = Utilities.getName(xmlConfig)
131 self.log.warning("domain %s already exists", vmName)
132 self.log.warning("%s", "Atempting to delete it")
133 self.deleteVM(vmName)
139 def deleteVM(self, name):
141 removes the given vm from the remote host
144 vm = self.hypervisor.lookupByName(name)
147 active = vm.isActive()
148 persistent = vm.isPersistent()
153 self.log.exception("%s", "Failed to destroy vm")
159 self.log.exception("%s", "Failed to undefine domain")
162 def openConnection(self):
164 opens a connection to the remote host
165 and stores it in self.hypervisor
167 self.log.info("Attempting to connect to libvirt at %s", self.host)
169 hostHypervisor = libvirt.open(self.URI)
172 "Failed to connect to %s. Trying again", self.host
176 hostHypervisor = libvirt.open(self.URI)
178 self.log.exception("Cannot connect to %s. Exiting", self.host)
181 if hostHypervisor is None:
182 self.log.error("Failed to connect to %s. Exiting", self.host)
184 self.hypervisor = hostHypervisor
186 def restartVM(self, vm):
188 causes the given vm to reboot
190 dom = self.hypervisor.lookupByName(vm)
197 Closes connection to remote hypervisor
199 self.log.info("Closing connection to the hypervisor %s", self.host)
200 self.hypervisor.close()
202 def defineAllDomains(self, path):
204 Defines a domain from all the xml files in a directory
206 files = Utilities.getXMLFiles(path)
208 for xml_desc in files:
209 definitions.append(xml_desc.read())
211 for definition in definitions:
212 self.defineVM(definition)
214 def createAllNetworks(self, path):
216 Creates a network from all xml files in a directory
218 files = Utilities.getXMLFiles(path)
220 for xml_desc in files:
221 definitions.append(Utilities.fileToString(xml_desc))
223 for definition in definitions:
224 self.createNet(definition)
226 def createNet(self, config):
228 creates the network on the remote host
229 config is the xml in string representation
230 that defines the network
232 if self.checkNet(config):
233 network = self.hypervisor.networkDefineXML(config)
236 name = self.getName(config)
237 self.log.warning("Failed to define network %s", name)
239 if network.isActive() == 1:
241 self.log.info("Successfully defined network %s", net)
242 self.networks.append(network)
244 def checkNet(self, config):
246 Checks if another net with the same name exists, and
247 deletes that network if one is found
250 netName = Utilities.getName(config)
251 if netName not in self.hypervisor.listNetworks():
253 else: # net name is already used
255 "Network %s already exists. Trying to delete it", netName
257 network = self.hypervisor.networkLookupByName(netName)
258 self.deleteNet(network)
262 def deleteNet(self, net):
264 removes the given network from the host
266 active = net.isActive()
267 persistent = net.isPersistent()
272 self.log.warning("%s", "Failed to destroy network")
278 self.log.warning("%s", "Failed to undefine network")
282 This method does all the work of this class,
283 Parsing the net and vm config files and creating
284 all the requested nets/domains
285 returns a list of all networks and a list of all domains
286 as Network and Domain objects
288 nets = self.makeNetworks(self.net_conf)
289 doms = self.makeDomains(self.dom_conf)
292 def makeNetworks(self, conf):
294 Given a path to a config file, this method
295 parses the config and creates all requested networks,
296 and returns them in a list of Network objects
299 definitions = Network.parseConfigFile(conf)
300 for definition in definitions:
301 network = Network(definition)
302 networks.append(network)
303 self.createNet(network.toXML())
306 def makeDomains(self, conf):
308 Given a path to a config file, this method
309 parses the config and creates all requested vm's,
310 and returns them in a list of Domain objects
313 definitions = Domain.parseConfigFile(conf)
314 for definition in definitions:
315 domain = Domain(definition)
316 domains.append(domain)
317 self.defineVM(domain.toXML())
321 def getName(xmlString):
323 given xml with a name tag, this returns the value of name
328 xmlDoc = xml.dom.minidom.parseString(xmlString)
329 nameNode = xmlDoc.documentElement.getElementsByTagName('name')
330 name = str(nameNode[0].firstChild.nodeValue)