def cleanupExtraDiskImages(dir, references, diskSection): """ Check if any image file is referred more than once, and remove extra copies @param dir: directory path to Ovf file/disk image files @type dir: String """ # Get file list from References and list of disks from DiskSection refList = Ovf.getDict(references)['children'] diskList = Ovf.getDict(diskSection)['children'] # Clean up image files referred more than once for file in refList: fileId = file['ovf:id'] source = os.path.join(dir, file['ovf:href']) fileRefCnt = 0 prefCnt = 0 for disk in diskList: if disk.has_key('ovf:fileRef'): if disk['ovf:fileRef'] == fileId: fileRefCnt = fileRefCnt + 1 if disk.has_key('ovf:parentRef'): if disk['ovf:parentRef'] == fileId: prefCnt = prefCnt + 1 if fileRefCnt >= 2 or prefCnt >= 2: if os.path.isfile(source): os.remove(source)
def doChecksum(self, stamp="auto"): """ This method will optionally take a time stamp. If the file is not local it will use time.gmtime() to set the checksumstamp. Otherwise it will use the file descriptor to extract that information. The method will then perform a SHA1 checksum of the file and store the results in checksum. @type stamp: time in UTC. @param stamp: Time stamp of the file. (Last modify) """ refFile = self.getFileObject() if stamp != "auto": self.setChecksum(stamp) else: mtime = None try: mtime = os.fstat(refFile.fileno())[stat.ST_MTIME] except: # if the fstat failed, call with no args self.setChecksumStamp() if mtime != None: # if fstat succeeded call it with mtime self.setChecksumStamp(mtime) self.checksum = Ovf.sha1sumFile(refFile)
def getOvfNetworks(virtualHardware, configId=None): """ Retrieves network interface information for the virtual machine from the Ovf file. @param virtualHardware: Ovf VirtualSystem Node @type virtualHardware: DOM Element @param configId: configuration name @type configId: String @return: list of dictionaries, seeL{Interface Elements<networkElement>} @rtype: list @todo: stubbed function, needs work """ netList = [] rasd = Ovf.getDict(virtualHardware, configId)['children'] ovfNetDeviceList = [] for resource in rasd: if resource['name'] == 'Item': if resource['rasd:ResourceType'] == '10': ovfNetDeviceList.append(resource) # create a dict for each device # We currently only support the libvirt virtual network interface. We # also assume that the network currently exists. The default network # is "default". for netDevice in ovfNetDeviceList: netAttach = 'default' if netDevice.has_key('rasd:Connection'): netConnect = netDevice['rasd:Connection'] netList.append(dict(interfaceType = 'network', sourceName = netConnect)) return netList
def getReferencedFilesFromManifest(fileName): """ get a list of OvfReferencedFile objects mentioned in OVF Manifest file @type fileName: string @param fileName: path to a file to read Manifest file from @rtype : list of OvfReferencedFile objects @return: list of OvfReferencedFile objects that appear in manifest """ try: files = [] mfFD = open(fileName, "r") # only need to read the contents line = mfFD.readline() prefix = "SHA1(" while line: txt = line.strip() newLn = txt.split("=") # newLn is what we get from the .mf file # when you append newLn it returns as a list so need to add both elements partial = newLn[0].split(prefix) # this will return ['', 'ourOVF.ovf)'] # still need to take out the last ) name = partial[1].split(")") # the above line will return ['Ubuntu-0.vmdk', ''] href = name[0] # the name will always be stored in name[0] sumSection = txt.split("=") ans = sumSection[1].strip() path = Ovf.href2abspath(href, fileName) files.append(OvfReferencedFile.OvfReferencedFile(path, href, ans)) line = mfFD.readline() # get the next line return files except Exception, e: raise e
def getPropertiesForNode(node, configuration): """ Generate an array of tuples containing the environment information for a single node (vs or vsc) @type node: DOM node @param node: virtual system or collection DOM node @type configuration: String @param configuration: Configuration being used. Can be None @rtype: array @return: array of [(key, property node, value)] """ # Start by getting all product sections for this node productSections = Ovf.getChildNodes(node, (Ovf.hasTagName, 'ProductSection')) retArray = [] # it's valid for there to be 0 product sections if productSections != []: for prodNode in productSections: prodClass = prodNode.getAttribute('ovf:class') if prodClass != '': prodClass += '.' prodInstance = prodNode.getAttribute('ovf:instance') if prodInstance != '': prodInstance = '.' + prodInstance properties = Ovf.getChildNodes(prodNode, (Ovf.hasTagName, 'Property')) if properties == []: continue for propertyNode in properties: attributes = Ovf.getAttributes(propertyNode) if not attributes.has_key('ovf:key'): raise RuntimeError, 'Node missing required attribute ' +\ 'ovf:key' propKey = prodClass + attributes['ovf:key'] + prodInstance retArray.append((propKey, propertyNode, getPropertyDefaultValue(propertyNode,\ configuration))) return retArray
def getOvfVcpu(virtualHardware, configId=None): """ Retrieves the number of virtual CPUs to be allocated for the virtual machine from the Ovf file. @param virtualHardware: Ovf VirtualSystem Node @type virtualHardware: DOM Element @param configId: configuration name @type configId: String @return: quantity of virtual cpu's @rtype: String """ vcpu = '' rasd = Ovf.getDict(virtualHardware, configId)['children'] for resource in rasd: if(resource.has_key('rasd:ResourceType') and resource['rasd:ResourceType'] == '3'): vcpu = resource['rasd:VirtualQuantity'] return vcpu
def getPropertyDefaultValue(propertyNode, configuration): """ Get the default value for a given Property node. The value returned is based on this priority: 1) value node for given configuration 2) ovf:value attribute 3) first Value node @type propertyNode: DOM node @param propertyNode: Property node @type configuration: String @param configuration: Configuration used to select the default value @rtype: String @return: Default value for node. May be empty string. None if no default was specified """ if not configuration: docNode = propertyNode.parentNode while docNode.nodeType != Node.DOCUMENT_NODE: docNode = docNode.parentNode if not docNode: raise RuntimeError, "Unable to find document node" configuration = Ovf.getDefaultConfiguration(docNode) if configuration: valueNodes = Ovf.getChildNodes(propertyNode, (Ovf.hasTagName, 'Value'), (Ovf.hasAttribute, 'ovf:configuration', configuration)) if valueNodes: attributes = Ovf.getAttributes(valueNodes[0]) if attributes.has_key('ovf:value'): return attributes['ovf:value'] attributes = Ovf.getAttributes(propertyNode) if attributes.has_key('ovf:value'): return attributes['ovf:value'] valueNodes = Ovf.getChildNodes(propertyNode, (Ovf.hasTagName, 'Value')) if valueNodes: attributes = Ovf.getAttributes(valueNodes[0]) if attributes.has_key('ovf:value'): return attributes['ovf:value'] return None
def getOvfCurrentMemory(virtualHardware, configId=None): """ Retrieves the amount of memory (kB) to be allocated for the virtual machine from the Ovf file. @note: DSP0004 v2.5.0 outlines the Programmatic Unit forms for OVF. This pertains specifically to rasd:AllocationUnits, which accepts both the current and deprecated forms. New implementations should not use Unit Qualifiers as this form is deprecated. - PUnit form, as in "byte * 2^20" - PUnit form w/ Units Qualifier(deprecated), as in "MegaBytes" @param virtualHardware: Ovf VirtualSystem Node @type virtualHardware: DOM Element @param configId: configuration name @type configId: String @return: memory in kB @rtype: String """ memory = '' # TODO: needs to use bound:normal, if it is present rasd = Ovf.getDict(virtualHardware, configId)['children'] for resource in rasd: if(resource.has_key('rasd:ResourceType') and resource['rasd:ResourceType'] == '4'): memoryQuantity = resource['rasd:VirtualQuantity'] memoryUnits = resource['rasd:AllocationUnits'] if(memoryUnits.startswith('byte') or memoryUnits.startswith('bit')): # Calculate PUnit numerical factor memoryUnits = memoryUnits.replace('^','**') # Determine PUnit Quantifier DMTF DSP0004, {byte, bit} # Convert to kilobytes memoryUnits = memoryUnits.split(' ', 1) quantifier = memoryUnits[0] if quantifier == 'byte': memoryUnits[0] = '2**-10' elif quantifier == 'bit': memoryUnits[0] = '2**-13' else: raise ValueError("Incompatible PUnit quantifier for memory.") memoryUnits = ' '.join(memoryUnits) memoryFactor = int(eval(memoryUnits)) else: if memoryUnits.startswith('Kilo'): memoryFactor = 1 elif memoryUnits.startswith('Mega'): memoryFactor = 1024 elif memoryUnits.startswith('Giga'): memoryFactor = 2048 else: raise ValueError("Incompatible PUnit quantifier for memory.") if memoryUnits.endswith('Bytes'): memoryFactor *= 1 elif memoryUnits.endswith('Bits'): memoryFactor *= 0.125 else: raise ValueError("Incompatible PUnit quantifier for memory.") memory = str(int(memoryQuantity) * memoryFactor) return memory
def getOvfStartup(ovf): """ Returns a schedule representing the startup order for a virtual appliance from an ovf. @param ovf: Ovf file @type ovf: DOM Document @rtype: dictionary @return: startup dictionary of domains """ startupDict = dict(boot='', entities=dict()) systems = startupDict['entities'] # Create a list of all startup sections startupSections = Ovf.getNodes(ovf, (Ovf.hasTagName, 'StartupSection')) # Create an entry in startup dictionary for each entry in Startup sections for section in startupSections: for item in section.getElementsByTagName('Item'): attributes = [] for i in range(item.attributes.length): attr = item.attributes.item(i) if attr.name == 'ovf:id': sysId = attr.value elif(attr.name == 'ovf:order' or attr.name == 'ovf:startDelay'): attributes.append((attr.name, attr.value)) # Store attribute pairs in dicitonary virtualSys = dict(attributes) if not virtualSys.has_key('ovf:order'): virtualSys['ovf:order'] = '0' if not virtualSys.has_key('ovf:startDelay'): virtualSys['ovf:startDelay'] = '0' parentId = section.parentNode.getAttribute('ovf:id') if not systems.has_key(parentId): systems[parentId] = dict(systems=[]) systems[parentId]['systems'].append(sysId) systems[sysId] = virtualSys # Create a default entry for each system not in a startup section for each in Ovf.getNodes(ovf, (Ovf.hasTagName, 'VirtualSystem')): sysId = each.getAttribute('ovf:id') # If parentNode is Envelope, set as root system # else, trace back and fill in missing parent info if each.parentNode.tagName == 'Envelope': startupDict['boot'] = sysId systems[sysId] = {'ovf:order':'0', 'ovf:startDelay':'0'} else: parent = each.parentNode parentId = parent.getAttribute('ovf:id') if systems.has_key(parentId) and \ not systems.has_key(sysId): systems[parentId]['systems'].append(sysId) systems[sysId] = {'ovf:order':'0', 'ovf:startDelay':'0'} # set parent info if parent.parentNode.tagName == 'Envelope': startupDict['boot'] = parentId while(not systems.has_key(parentId)): systems[parentId] = {'systems':[]} systems[parentId]['systems'].append(sysId) systems[sysId] = {'ovf:order':'0', 'ovf:startDelay':'0'} # Increment, if not at root if parent.parentNode.tagName == 'Envelope': startupDict['boot'] = parentId else: parent = parent.parentNode sysId = parentId parentId = parent.getAttribute('ovf:id') return startupDict
def getOvfDomains(ovf, path, hypervisor=None, configId=None, envDirectory=None): """ Returns a dictionary with all of the VirtualSystems in an ovf listed as keys with the libvirt domain, for the specified configuration, stored as the value. @param ovf: Ovf file @type ovf: DOM Document @param path: path to Ovf file @type path: String @param configId: configuration name @type configId: String @todo: needs work, very basic, assumes hypervisor type """ domains = dict() #directory = os.path.abspath(path.rsplit("/", 1)[0]) directory = path if configId == None: configId = Ovf.getDefaultConfiguration(ovf) else: if not Ovf.isConfiguration(ovf, configId): raise RuntimeError("OvfLibvirt.getOvfDomains: configuration " + configId + " not found.") # Get Nodes references = Ovf.getElementsByTagName(ovf, 'References') diskSection = Ovf.getElementsByTagName(ovf, 'DiskSection') if len(references) is not 1: raise NotImplementedError("OvfLibvirt.getOvfDomain: Unable to locate" + " a single References node.") elif len(diskSection) is not 1: raise NotImplementedError("OvfLibvirt.getOvfDomain: Unable to locate" + " a single DiskSection node.") else: refs = references[0] disks = diskSection[0] # For each system, create libvirt domain description for system in Ovf.getNodes(ovf, (Ovf.hasTagName, 'VirtualSystem')): ovfId = system.getAttribute('ovf:id') # Get VirtualHardwareSection virtualHardwareSection = Ovf.getElementsByTagName(system, 'VirtualHardwareSection') if len(virtualHardwareSection) is not 1: raise NotImplementedError("OvfLibvirt.getOvfDomain: Unable to locate" + " a single VirtualHardwareSection node.") else: virtualHardware = virtualHardwareSection[0] #metadata name = nameElement(ovfId) #resources memory = memoryElement(getOvfMemory(virtualHardware, configId)) vcpu = vcpuElement(getOvfVcpu(virtualHardware, configId)) #domain if not hypervisor: hypervisor = OvfPlatform.getVsSystemType(system) else: hypervisor = hypervisor.lower() domainType = getDomainTypeForVsType(hypervisor) domain = domainElement(domainType) #boot bootElements(domain, hypervisor) #time clock = clockElement('utc') #features features = featuresElement(acpi=True) #life cycle onPowerOff = onPowerOffElement('destroy') onReboot = onRebootElement('restart') onCrash = onCrashElement('destroy') #devices - graphics graphics = graphicsElement('vnc', 'localhost', '-1') #devices - console console = consoleElement('pty', '0') #devices devices = devicesElement(graphics, console) #disks envFile = None if envDirectory: envFile = os.path.join(envDirectory, ovfId + '.iso') diskDicts = getOvfDisks(virtualHardware, directory, refs, disks, configId, envFile) for dsk in diskDicts: addDevice(devices, diskElement(dsk)) #network netDicts = getOvfNetworks(virtualHardware, configId) for networkDict in netDicts: network = networkElement(networkDict) addDevice(devices, network) #document document = libvirtDocument(domain, name, memory, vcpu, clock, features, onPowerOff, onReboot, onCrash, devices) domains[ovfId] = Ovf.xmlString(document) # Delete any extra copies of disk images cleanupExtraDiskImages(directory, refs, disks) return domains
def getOvfDisks(virtualHardware, dir, references, diskSection=None, configId=None, envFile=None): """ Retrieves disk device information for the virtual machine from the Ovf file. @param ovf: Ovf file @type ovf: DOM Document @param virtualHardware: Ovf VirtualSystem Node @type virtualHardware: DOM Element @param configId: configuration name @type configId: String @return: list of dictionaries, see L{Disk Element<diskElement>} @rtype: list """ disks = () logicalNames = ['hda', 'hdb', 'hdd', 'hde', 'hdf'] rasd = Ovf.getDict(virtualHardware, configId)['children'] ovfDiskList = [] for resource in rasd: if resource['name'] == 'Item': if resource['rasd:ResourceType'] == '14': ovfDiskList.append(('fd', resource)) elif resource['rasd:ResourceType'] == '15': ovfDiskList.append(('cdrom', resource)) elif resource['rasd:ResourceType'] == '17': ovfDiskList.append(('disk', resource)) for each in ovfDiskList: hostResources = [] #resource dictionary ovfDisk = each[1] #disk device: hd, fd, or cdrom device = each[0] #source file source = None hostResource = ovfDisk['rasd:HostResource'] resourceId = hostResource.rsplit('/', 1).pop() if hostResource.startswith('ovf:/disk/'): diskList = Ovf.getDict(diskSection)['children'] for child in diskList: #Create a tuple (file hostResource, diskId, referred?) #flag to check if the disk is referred referedDisk = 0 if child['ovf:diskId'] == resourceId: #Check for disks referred as parentRef. #Add parentRef disk first, as it could be the # operating system disk image if child.has_key('ovf:parentRef'): parentref = child['ovf:parentRef'] for pref in diskList: if pref['ovf:diskId'] == parentref: referedDisk = 1 hostResource = 'ovf://file/' + pref['ovf:fileRef'] diskResource = (hostResource, resourceId, referedDisk) hostResources.append(diskResource) break # and the disk referred by the VMs HostResource #Check to see if the fileRef is referred by more than one disk referedDisk = 0 hostResource = 'ovf://file/' + child['ovf:fileRef'] refcnt = 0 myFileRef = child['ovf:fileRef'] for disk in diskList: if disk['ovf:fileRef'] == myFileRef: refcnt = refcnt + 1 if refcnt > 1: referedDisk = 1 diskResource = (hostResource, resourceId, referedDisk) hostResources.append(diskResource) for resource in hostResources: (hostResource, diskId, referedDisk) = resource resourceId = hostResource.rsplit('/', 1).pop() refList = Ovf.getDict(references)['children'] for child in refList: if child['ovf:id'] == resourceId: source = os.path.join(dir, child['ovf:href']) if referedDisk == 1: simage = source diskpath = source.rsplit('/', 1)[0] diskimage = source.rsplit('/', 1)[1] diskname = diskimage.split('.')[0] disknameext = diskimage.split('.')[1] newdiskname = diskname + '-' + diskId if disknameext != None: source = os.path.join(diskpath, newdiskname + '.' + disknameext) else: source = os.path.join(diskpath, newdiskname) dimage = source shutil.copy(simage, dimage) if source == None: raise ValueError(hostResource) #target bus parentType = None parentId = int(ovfDisk['rasd:Parent']) for presource in rasd: if presource['name'] == 'Item': instId = int(presource['rasd:InstanceID']) if instId == parentId: parentType = presource['rasd:ResourceType'] break if(parentType == '5'): bus = 'ide' elif(parentType == '6'): bus = 'scsi' else: raise ValueError #default not read-only ro = False #target device if(device == 'cdrom'): ro = True dev = 'hdc' else: dev = logicalNames.pop(0) libvirtDisk = dict(diskType='file', diskDevice=device, sourceFile=source, targetBus=bus, targetDev=dev, readonly=ro) disks += (libvirtDisk,) # add the environment iso if envFile: disks += (dict(diskType = 'file', targetDev = 'hdc:cdrom', sourceFile = os.path.abspath(envFile), targetBus = 'ide', diskDevice = 'cdrom', readonly = True),) return disks