summaryrefslogtreecommitdiffstats
path: root/laas-fog/source/api/libvirt_api.py
diff options
context:
space:
mode:
Diffstat (limited to 'laas-fog/source/api/libvirt_api.py')
-rw-r--r--laas-fog/source/api/libvirt_api.py331
1 files changed, 331 insertions, 0 deletions
diff --git a/laas-fog/source/api/libvirt_api.py b/laas-fog/source/api/libvirt_api.py
new file mode 100644
index 0000000..4e19736
--- /dev/null
+++ b/laas-fog/source/api/libvirt_api.py
@@ -0,0 +1,331 @@
+"""
+#############################################################################
+#Copyright 2017 Parker Berberian and others #
+# #
+#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. #
+#############################################################################
+"""
+
+import libvirt
+import time
+import xml.dom
+import xml.dom.minidom
+from domain import Domain
+from network import Network
+from utilities import Utilities
+
+
+class Libvirt:
+ """
+ This class talks to the Libvirt api.
+ Given a config file, this class should create all networks and
+ domains.
+
+ TODO: convert prints to logging and remove uneeded pass statements
+ """
+
+ def __init__(self, hostAddr, net_conf=None, dom_conf=None):
+ """
+ init function
+ hostAddr is the ip address of the host
+ net_conf and dom_conf are the paths
+ to the config files
+ """
+ self.host = hostAddr
+ self.URI = "qemu+ssh://root@"+str(hostAddr)+"/system"
+ self.hypervisor = None
+ self.domains = []
+ self.networks = []
+ self.net_conf = net_conf
+ self.dom_conf = dom_conf
+
+ def setLogger(self, log):
+ """
+ Saves the logger in self.log
+ """
+ self.log = log
+
+ def bootMaster(self):
+ """
+ starts the previously defined master node
+ """
+ for dom in self.domains:
+ if 'master' in dom.name():
+ try:
+ dom.create()
+ except Exception:
+ pass
+
+ def bootSlaves(self):
+ """
+ boots every defined vm with 'slave' in its name
+ """
+ for dom in self.domains:
+ if 'slave' in dom.name():
+ try:
+ dom.create()
+ self.log.info("Booting %s", dom.name())
+ except Exception:
+ self.log.exception("%s", "failed to boot domain")
+ time.sleep(5)
+
+ def getMacs(self, domName):
+ """
+ returns a dictionary with a network name
+ mapped to the mac address of the domain on that net
+ """
+ try:
+ dom = self.hypervisor.lookupByName(domName)
+ xmlDesc = dom.XMLDesc(0)
+ parsedXML = xml.dom.minidom.parseString(xmlDesc)
+ interfacesXML = parsedXML.getElementsByTagName('interface')
+ netDict = {}
+ for iface in interfacesXML:
+ src = iface.getElementsByTagName('source')[0]
+ mac = iface.getElementsByTagName('mac')[0]
+ netDict[src] = mac
+ return netDict
+ except Exception:
+ self.log.exception("%s", "Domain not found")
+
+ def defineVM(self, xmlConfig):
+ """
+ Generic method to define a persistent vm with the
+ given config.
+ Assumes that self.hypervisor is already connected.
+ """
+ if self.checkForVM(xmlConfig):
+ vm = self.hypervisor.defineXML(xmlConfig)
+ if vm is None:
+ name = self.getName(xmlConfig)
+ self.log.error("Failed to define vm %s. exiting", name)
+ exit(1)
+ else:
+ self.log.info("Successfully created vm %s", vm.name())
+ pass
+ self.domains.append(vm)
+
+ def checkForVM(self, xmlConfig):
+ """
+ Checks if another vm with the same name exists
+ on the remote host already. If it does, it will
+ delete that vm
+ """
+ allGood = False
+ vms = self.hypervisor.listAllDomains(0)
+ names = []
+ for dom in vms:
+ names.append(dom.name())
+ vmName = Utilities.getName(xmlConfig)
+ if vmName in names:
+ self.log.warning("domain %s already exists", vmName)
+ self.log.warning("%s", "Atempting to delete it")
+ self.deleteVM(vmName)
+ allGood = True
+ else:
+ allGood = True
+ return allGood
+
+ def deleteVM(self, name):
+ """
+ removes the given vm from the remote host
+ """
+ try:
+ vm = self.hypervisor.lookupByName(name)
+ except:
+ return
+ active = vm.isActive()
+ persistent = vm.isPersistent()
+ if active:
+ try:
+ vm.destroy()
+ except:
+ self.log.exception("%s", "Failed to destroy vm")
+
+ if persistent:
+ try:
+ vm.undefine()
+ except:
+ self.log.exception("%s", "Failed to undefine domain")
+ pass
+
+ def openConnection(self):
+ """
+ opens a connection to the remote host
+ and stores it in self.hypervisor
+ """
+ self.log.info("Attempting to connect to libvirt at %s", self.host)
+ try:
+ hostHypervisor = libvirt.open(self.URI)
+ except:
+ self.log.warning(
+ "Failed to connect to %s. Trying again", self.host
+ )
+ time.sleep(5)
+ try:
+ hostHypervisor = libvirt.open(self.URI)
+ except:
+ self.log.exception("Cannot connect to %s. Exiting", self.host)
+ exit(1)
+
+ if hostHypervisor is None:
+ self.log.error("Failed to connect to %s. Exiting", self.host)
+ exit(1)
+ self.hypervisor = hostHypervisor
+
+ def restartVM(self, vm):
+ """
+ causes the given vm to reboot
+ """
+ dom = self.hypervisor.lookupByName(vm)
+ dom.destroy()
+ time.sleep(15)
+ dom.create()
+
+ def close(self):
+ """
+ Closes connection to remote hypervisor
+ """
+ self.log.info("Closing connection to the hypervisor %s", self.host)
+ self.hypervisor.close()
+
+ def defineAllDomains(self, path):
+ """
+ Defines a domain from all the xml files in a directory
+ """
+ files = Utilities.getXMLFiles(path)
+ definitions = []
+ for xml_desc in files:
+ definitions.append(xml_desc.read())
+
+ for definition in definitions:
+ self.defineVM(definition)
+
+ def createAllNetworks(self, path):
+ """
+ Creates a network from all xml files in a directory
+ """
+ files = Utilities.getXMLFiles(path)
+ definitions = []
+ for xml_desc in files:
+ definitions.append(Utilities.fileToString(xml_desc))
+
+ for definition in definitions:
+ self.createNet(definition)
+
+ def createNet(self, config):
+ """
+ creates the network on the remote host
+ config is the xml in string representation
+ that defines the network
+ """
+ if self.checkNet(config):
+ network = self.hypervisor.networkDefineXML(config)
+
+ if network is None:
+ name = self.getName(config)
+ self.log.warning("Failed to define network %s", name)
+ network.create()
+ if network.isActive() == 1:
+ net = network.name()
+ self.log.info("Successfully defined network %s", net)
+ self.networks.append(network)
+
+ def checkNet(self, config):
+ """
+ Checks if another net with the same name exists, and
+ deletes that network if one is found
+ """
+ allGood = False
+ netName = Utilities.getName(config)
+ if netName not in self.hypervisor.listNetworks():
+ return True
+ else: # net name is already used
+ self.log.warning(
+ "Network %s already exists. Trying to delete it", netName
+ )
+ network = self.hypervisor.networkLookupByName(netName)
+ self.deleteNet(network)
+ allGood = True
+ return allGood
+
+ def deleteNet(self, net):
+ """
+ removes the given network from the host
+ """
+ active = net.isActive()
+ persistent = net.isPersistent()
+ if active:
+ try:
+ net.destroy()
+ except:
+ self.log.warning("%s", "Failed to destroy network")
+
+ if persistent:
+ try:
+ net.undefine()
+ except:
+ self.log.warning("%s", "Failed to undefine network")
+
+ def go(self):
+ """
+ This method does all the work of this class,
+ Parsing the net and vm config files and creating
+ all the requested nets/domains
+ returns a list of all networks and a list of all domains
+ as Network and Domain objects
+ """
+ nets = self.makeNetworks(self.net_conf)
+ doms = self.makeDomains(self.dom_conf)
+ return doms, nets
+
+ def makeNetworks(self, conf):
+ """
+ Given a path to a config file, this method
+ parses the config and creates all requested networks,
+ and returns them in a list of Network objects
+ """
+ networks = []
+ definitions = Network.parseConfigFile(conf)
+ for definition in definitions:
+ network = Network(definition)
+ networks.append(network)
+ self.createNet(network.toXML())
+ return networks
+
+ def makeDomains(self, conf):
+ """
+ Given a path to a config file, this method
+ parses the config and creates all requested vm's,
+ and returns them in a list of Domain objects
+ """
+ domains = []
+ definitions = Domain.parseConfigFile(conf)
+ for definition in definitions:
+ domain = Domain(definition)
+ domains.append(domain)
+ self.defineVM(domain.toXML())
+ return domains
+
+ @staticmethod
+ def getName(xmlString):
+ """
+ given xml with a name tag, this returns the value of name
+ eg:
+ <name>Parker</name>
+ returns 'Parker'
+ """
+ xmlDoc = xml.dom.minidom.parseString(xmlString)
+ nameNode = xmlDoc.documentElement.getElementsByTagName('name')
+ name = str(nameNode[0].firstChild.nodeValue)
+ return name