def _create_task(self, cb, params): """Asynchronous function which actually creates the snapshot. Parameters: cb -- a callback function to signal the Task's progress. params -- a dict with the following values: "vm_name": the name of the VM where the snapshot will be created. "name": the snapshot name. """ vm_name = params['vm_name'] name = params['name'] cb('building snapshot XML') root_elem = E.domainsnapshot() root_elem.append(E.name(name)) xml = ET.tostring(root_elem, encoding='utf-8') try: cb('fetching snapshot domain') vir_dom = VMModel.get_vm(vm_name, self.conn) cb('creating snapshot') vir_dom.snapshotCreateXML(xml, 0) except (NotFoundError, OperationFailed, libvirt.libvirtError), e: raise OperationFailed('KCHSNAP0002E', { 'name': name, 'vm': vm_name, 'err': e.message })
def get_disk_used_by(objstore, conn, path): try: with objstore as session: try: used_by = session.get('storagevolume', path)['used_by'] except (KeyError, NotFoundError): wok_log.info('Volume %s not found in obj store.' % path) used_by = [] # try to find this volume in existing vm vms_list = VMsModel.get_vms(conn) for vm in vms_list: dom = VMModel.get_vm(vm, conn) storages = get_vm_disks(dom) for disk in storages.keys(): d_info = get_vm_disk_info(dom, disk) if path == d_info['path']: used_by.append(vm) try: session.store('storagevolume', path, {'used_by': used_by}) except Exception as e: # Let the exception be raised. If we allow disks' # used_by to be out of sync, data corruption could # occour if a disk is added to two guests # unknowingly. wok_log.error( 'Unable to store storage volume id in' ' objectstore due error: %s', e.message) raise OperationFailed('KCHVOL0017E', {'err': e.message}) except Exception as e: # This exception is going to catch errors returned by 'with', # specially ones generated by 'session.store'. It is outside # to avoid conflict with the __exit__ function of 'with' raise OperationFailed('KCHVOL0017E', {'err': e.message}) return used_by
def create(self, vm_name, params={}): """Create a snapshot with the current domain state. The VM must be stopped and contain only disks with format 'qcow2'; otherwise an exception will be raised. Parameters: vm_name -- the name of the VM where the snapshot will be created. params -- a dict with the following values: "name": The snapshot name (optional). If omitted, a default value based on the current time will be used. Return: A Task running the operation. """ vir_dom = VMModel.get_vm(vm_name, self.conn) if DOM_STATE_MAP[vir_dom.info()[0]] != u'shutoff': raise InvalidOperation('KCHSNAP0001E', {'vm': vm_name}) # if the VM has a non-CDROM disk with type 'raw', abort. for storage_name in self.vmstorages.get_list(vm_name): storage = self.vmstorage.lookup(vm_name, storage_name) type = storage['type'] format = storage['format'] if type != u'cdrom' and format != u'qcow2': raise InvalidOperation('KCHSNAP0010E', {'vm': vm_name, 'format': format}) name = params.get('name', unicode(int(time.time()))) task_params = {'vm_name': vm_name, 'name': name} taskid = add_task(u'/plugins/kimchi/vms/%s/snapshots/%s' % (vm_name, name), self._create_task, self.objstore, task_params) return self.task.lookup(taskid)
def _get_available_bus_address(self, bus_type, vm_name): if bus_type not in ['ide']: return dict() # libvirt limitation of just 1 ide controller # each controller have at most 2 buses and each bus 2 units. dom = VMModel.get_vm(vm_name, self.conn) disks = self.get_list(vm_name) valid_id = [('0', '0'), ('0', '1'), ('1', '0'), ('1', '1')] controller_id = '0' for dev_name in disks: disk = get_device_node(dom, dev_name) if disk.target.attrib['bus'] == 'ide': controller_id = disk.address.attrib['controller'] bus_id = disk.address.attrib['bus'] unit_id = disk.address.attrib['unit'] if (bus_id, unit_id) in valid_id: valid_id.remove((bus_id, unit_id)) continue if not valid_id: raise OperationFailed('KCHVMSTOR0014E', {'type': 'ide', 'limit': 4}) else: address = {'controller': controller_id, 'bus': valid_id[0][0], 'unit': valid_id[0][1]} return dict(address=address)
def delete(self, vm_name, dev_name): try: bus_type = self.lookup(vm_name, dev_name)['bus'] dom = VMModel.get_vm(vm_name, self.conn) except NotFoundError: raise if (bus_type not in HOTPLUG_TYPE and DOM_STATE_MAP[dom.info()[0]] != 'shutoff'): raise InvalidOperation('KCHVMSTOR0011E') try: disk = get_device_node(dom, dev_name) path = get_vm_disk_info(dom, dev_name)['path'] if path is None or len(path) < 1: path = self.lookup(vm_name, dev_name)['path'] # This has to be done before it's detached. If it wasn't # in the obj store, its ref count would have been updated # by get_disk_used_by() if path is not None: used_by = get_disk_used_by(self.objstore, self.conn, path) else: wok_log.error("Unable to decrement volume used_by on" " delete because no path could be found.") dom.detachDeviceFlags(etree.tostring(disk), get_vm_config_flag(dom, 'all')) except Exception as e: raise OperationFailed("KCHVMSTOR0010E", {'error': e.message}) if used_by is not None and vm_name in used_by: used_by.remove(vm_name) set_disk_used_by(self.objstore, path, used_by) else: wok_log.error("Unable to update %s:%s used_by on delete." % (vm_name, dev_name))
def delete(self, vmid, dev_name): dom = VMModel.get_vm(vmid, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostdev = root.devices.hostdev except AttributeError: raise NotFoundError('KCHVMHDEV0001E', {'vmid': vmid, 'dev_name': dev_name}) pci_devs = [(DeviceModel.deduce_dev_name(e, self.conn), e) for e in hostdev if e.attrib['type'] == 'pci'] for e in hostdev: if DeviceModel.deduce_dev_name(e, self.conn) == dev_name: xmlstr = etree.tostring(e) dom.detachDeviceFlags( xmlstr, get_vm_config_flag(dom, mode='all')) if e.attrib['type'] == 'pci': self._delete_affected_pci_devices(dom, dev_name, pci_devs) break else: raise NotFoundError('KCHVMHDEV0001E', {'vmid': vmid, 'dev_name': dev_name})
def _get_available_bus_address(self, bus_type, vm_name): if bus_type not in ['ide']: return dict() # libvirt limitation of just 1 ide controller # each controller have at most 2 buses and each bus 2 units. dom = VMModel.get_vm(vm_name, self.conn) disks = self.get_list(vm_name) valid_id = [('0', '0'), ('0', '1'), ('1', '0'), ('1', '1')] controller_id = '0' for dev_name in disks: disk = get_device_node(dom, dev_name) if disk.target.attrib['bus'] == 'ide': controller_id = disk.address.attrib['controller'] bus_id = disk.address.attrib['bus'] unit_id = disk.address.attrib['unit'] if (bus_id, unit_id) in valid_id: valid_id.remove((bus_id, unit_id)) continue if not valid_id: raise OperationFailed('KCHVMSTOR0014E', { 'type': 'ide', 'limit': 4 }) else: address = { 'controller': controller_id, 'bus': valid_id[0][0], 'unit': valid_id[0][1] } return dict(address=address)
def update(self, vm, mac, params): dom = VMModel.get_vm(vm, self.conn) iface = self._get_vmiface(vm, mac) if iface is None: raise NotFoundError("KCHVMIF0001E", {'name': vm, 'iface': mac}) # cannot change mac address in a running system if DOM_STATE_MAP[dom.info()[0]] != "shutoff": raise InvalidOperation('KCHVMIF0011E') # mac address is a required parameter if 'mac' not in params: raise MissingParameter('KCHVMIF0008E') # new mac address must be unique if self._get_vmiface(vm, params['mac']) is not None: raise InvalidParameter('KCHVMIF0009E', {'name': vm, 'mac': params['mac']}) flags = 0 if dom.isPersistent(): flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG # remove the current nic xml = etree.tostring(iface) dom.detachDeviceFlags(xml, flags=flags) # add the nic with the desired mac address iface.mac.attrib['address'] = params['mac'] xml = etree.tostring(iface) dom.attachDeviceFlags(xml, flags=flags) return [vm, params['mac']]
def delete(self, vmid, dev_name): dom = VMModel.get_vm(vmid, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostdev = root.devices.hostdev except AttributeError: raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name }) pci_devs = [(DeviceModel.deduce_dev_name(e, self.conn), e) for e in hostdev if e.attrib['type'] == 'pci'] for e in hostdev: if DeviceModel.deduce_dev_name(e, self.conn) == dev_name: xmlstr = etree.tostring(e) dom.detachDeviceFlags(xmlstr, get_vm_config_flag(dom, mode='all')) if e.attrib['type'] == 'pci': self._delete_affected_pci_devices(dom, dev_name, pci_devs) break else: raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name })
def lookup(self, vmid, dev_name): dom = VMModel.get_vm(vmid, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostdev = root.devices.hostdev except AttributeError: raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name }) dev_model = DeviceModel(conn=self.conn) for e in hostdev: deduced_name = DeviceModel.deduce_dev_name(e, self.conn) if deduced_name == dev_name: dev_info = dev_model.lookup(dev_name) return { 'name': dev_name, 'type': e.attrib['type'], 'product': dev_info.get('product', None), 'vendor': dev_info.get('vendor', None) } raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name })
def _get_ips(self, vm, mac, network): ips = [] # Return empty list if shutoff, even if leases still valid or ARP # cache has entries for this MAC. conn = self.conn.get() dom = VMModel.get_vm(vm, self.conn) if DOM_STATE_MAP[dom.info()[0]] == "shutoff": return ips # An iface may have multiple IPs # An IP could have been assigned without libvirt. # First check the ARP cache. with open('/proc/net/arp') as f: ips = [line.split()[0] for line in f.xreadlines() if mac in line] # Some ifaces may be inactive, so if the ARP cache didn't have them, # and they happen to be assigned via DHCP, we can check there too. net = conn.networkLookupByName(network) leases = getDHCPLeases(net, mac) for lease in leases: ip = lease.get('ipaddr') if ip not in ips: ips.append(ip) return ips
def _create_task(self, cb, params): """Asynchronous function which actually creates the snapshot. Parameters: cb -- a callback function to signal the Task's progress. params -- a dict with the following values: "vm_name": the name of the VM where the snapshot will be created. "name": the snapshot name. """ vm_name = params['vm_name'] name = params['name'] cb('building snapshot XML') root_elem = E.domainsnapshot() root_elem.append(E.name(name)) xml = ET.tostring(root_elem, encoding='utf-8') try: cb('fetching snapshot domain') vir_dom = VMModel.get_vm(vm_name, self.conn) cb('creating snapshot') vir_dom.snapshotCreateXML(xml, 0) except (NotFoundError, OperationFailed, libvirt.libvirtError), e: raise OperationFailed('KCHSNAP0002E', {'name': name, 'vm': vm_name, 'err': e.message})
def create(self, vm, params): conn = self.conn.get() networks = conn.listNetworks() + conn.listDefinedNetworks() networks = map(lambda x: x.decode('utf-8'), networks) if params['type'] == 'network': network = params.get("network") if network is None: raise MissingParameter('KCHVMIF0007E') if network not in networks: raise InvalidParameter('KCHVMIF0002E', {'name': vm, 'network': network}) macs = (iface.mac.get('address') for iface in self.get_vmifaces(vm, self.conn)) # user defined customized mac address if 'mac' in params and params['mac']: # make sure it is unique if params['mac'] in macs: raise InvalidParameter('KCHVMIF0009E', {'name': vm, 'mac': params['mac']}) # otherwise choose a random mac address else: while True: params['mac'] = VMIfacesModel.random_mac() if params['mac'] not in macs: break dom = VMModel.get_vm(vm, self.conn) os_data = VMModel.vm_get_os_metadata(dom) os_version, os_distro = os_data xml = get_iface_xml(params, conn.getInfo()[0], os_distro, os_version) flags = 0 if dom.isPersistent(): flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG if DOM_STATE_MAP[dom.info()[0]] != "shutoff": flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE dom.attachDeviceFlags(xml, flags) return params['mac']
def _attach_pci_device(self, vmid, dev_info): self._validate_pci_passthrough_env() dom = VMModel.get_vm(vmid, self.conn) # Due to libvirt limitation, we don't support live assigne device to # vfio driver. driver = ('vfio' if DOM_STATE_MAP[dom.info()[0]] == "shutoff" and self.caps.kernel_vfio else 'kvm') # on powerkvm systems it must be vfio driver. distro, _, _ = platform.linux_distribution() if distro == 'IBM_PowerKVM': driver = 'vfio' # Attach all PCI devices in the same IOMMU group dev_model = DeviceModel(conn=self.conn) devs_model = DevicesModel(conn=self.conn) affected_names = devs_model.get_list( _passthrough_affected_by=dev_info['name']) passthrough_names = devs_model.get_list(_cap='pci', _passthrough='true') group_names = list(set(affected_names) & set(passthrough_names)) pci_infos = [dev_model.lookup(dev_name) for dev_name in group_names] pci_infos.append(dev_info) # all devices in the group that is going to be attached to the vm # must be detached from the host first with RollbackContext() as rollback: for pci_info in pci_infos: try: dev = self.conn.get().nodeDeviceLookupByName( pci_info['name']) dev.dettach() except Exception: raise OperationFailed('KCHVMHDEV0005E', {'name': pci_info['name']}) else: rollback.prependDefer(dev.reAttach) rollback.commitAll() device_flags = get_vm_config_flag(dom, mode='all') with RollbackContext() as rollback: for pci_info in pci_infos: pci_info['detach_driver'] = driver xmlstr = self._get_pci_device_xml(pci_info) try: dom.attachDeviceFlags(xmlstr, device_flags) except libvirt.libvirtError: wok_log.error( 'Failed to attach host device %s to VM %s: \n%s', pci_info['name'], vmid, xmlstr) raise rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags) rollback.commitAll() return dev_info['name']
def _attach_pci_device(self, vmid, dev_info): self._validate_pci_passthrough_env() dom = VMModel.get_vm(vmid, self.conn) # Due to libvirt limitation, we don't support live assigne device to # vfio driver. driver = ('vfio' if DOM_STATE_MAP[dom.info()[0]] == "shutoff" and self.caps.kernel_vfio else 'kvm') # on powerkvm systems it must be vfio driver. distro, _, _ = platform.linux_distribution() if distro == 'IBM_PowerKVM': driver = 'vfio' # Attach all PCI devices in the same IOMMU group dev_model = DeviceModel(conn=self.conn) devs_model = DevicesModel(conn=self.conn) affected_names = devs_model.get_list( _passthrough_affected_by=dev_info['name']) passthrough_names = devs_model.get_list( _cap='pci', _passthrough='true') group_names = list(set(affected_names) & set(passthrough_names)) pci_infos = [dev_model.lookup(dev_name) for dev_name in group_names] pci_infos.append(dev_info) # all devices in the group that is going to be attached to the vm # must be detached from the host first with RollbackContext() as rollback: for pci_info in pci_infos: try: dev = self.conn.get().nodeDeviceLookupByName( pci_info['name']) dev.dettach() except Exception: raise OperationFailed('KCHVMHDEV0005E', {'name': pci_info['name']}) else: rollback.prependDefer(dev.reAttach) rollback.commitAll() device_flags = get_vm_config_flag(dom, mode='all') with RollbackContext() as rollback: for pci_info in pci_infos: pci_info['detach_driver'] = driver xmlstr = self._get_pci_device_xml(pci_info) try: dom.attachDeviceFlags(xmlstr, device_flags) except libvirt.libvirtError: wok_log.error( 'Failed to attach host device %s to VM %s: \n%s', pci_info['name'], vmid, xmlstr) raise rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags) rollback.commitAll() return dev_info['name']
def get_list(self, vmid): dom = VMModel.get_vm(vmid, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostdev = root.devices.hostdev except AttributeError: return [] return [DeviceModel.deduce_dev_name(e, self.conn) for e in hostdev]
def get_list(self, vm_name): vir_dom = VMModel.get_vm(vm_name, self.conn) try: vir_snaps = vir_dom.listAllSnapshots(0) return sorted([s.getName().decode('utf-8') for s in vir_snaps], key=unicode.lower) except libvirt.libvirtError, e: raise OperationFailed('KCHSNAP0005E', {'vm': vm_name, 'err': e.message})
def get_list(self, vm_name): vir_dom = VMModel.get_vm(vm_name, self.conn) try: vir_snaps = vir_dom.listAllSnapshots(0) return sorted([s.getName().decode('utf-8') for s in vir_snaps], key=unicode.lower) except libvirt.libvirtError, e: raise OperationFailed('KCHSNAP0005E', { 'vm': vm_name, 'err': e.message })
def lookup(self, vm_name): vir_dom = VMModel.get_vm(vm_name, self.conn) try: vir_snap = vir_dom.snapshotCurrent(0) snap_name = vir_snap.getName().decode('utf-8') except libvirt.libvirtError, e: if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN_SNAPSHOT: return {} raise OperationFailed('KCHSNAP0008E', {'vm': vm_name, 'err': e.message})
def get_vmsnapshot(self, vm_name, name): vir_dom = VMModel.get_vm(vm_name, self.conn) try: return vir_dom.snapshotLookupByName(name, 0) except libvirt.libvirtError, e: code = e.get_error_code() if code == libvirt.VIR_ERR_NO_DOMAIN_SNAPSHOT: raise NotFoundError('KCHSNAP0003E', {'name': name, 'vm': vm_name}) else: raise OperationFailed('KCHSNAP0004E', {'name': name, 'vm': vm_name, 'err': e.message})
def lookup(self, vm_name): vir_dom = VMModel.get_vm(vm_name, self.conn) try: vir_snap = vir_dom.snapshotCurrent(0) snap_name = vir_snap.getName().decode('utf-8') except libvirt.libvirtError, e: if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN_SNAPSHOT: return {} raise OperationFailed('KCHSNAP0008E', { 'vm': vm_name, 'err': e.message })
def delete(self, vm, mac): dom = VMModel.get_vm(vm, self.conn) iface = self._get_vmiface(vm, mac) if iface is None: raise NotFoundError("KCHVMIF0001E", {'name': vm, 'iface': mac}) flags = 0 if dom.isPersistent(): flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG if DOM_STATE_MAP[dom.info()[0]] != "shutoff": flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE dom.detachDeviceFlags(etree.tostring(iface), flags)
def revert(self, vm_name, name): try: vir_dom = VMModel.get_vm(vm_name, self.conn) vir_snap = self.get_vmsnapshot(vm_name, name) vir_dom.revertToSnapshot(vir_snap, 0) # get vm name recorded in the snapshot and return new uri params vm_new_name = xpath_get_text(vir_snap.getXMLDesc(0), 'domain/name')[0] return [vm_new_name, name] except libvirt.libvirtError, e: raise OperationFailed('KCHSNAP0009E', {'name': name, 'vm': vm_name, 'err': e.message})
def revert(self, vm_name, name): try: vir_dom = VMModel.get_vm(vm_name, self.conn) vir_snap = self.get_vmsnapshot(vm_name, name) vir_dom.revertToSnapshot(vir_snap, 0) # get vm name recorded in the snapshot and return new uri params vm_new_name = xpath_get_text(vir_snap.getXMLDesc(0), 'domain/name')[0] return [vm_new_name, name] except libvirt.libvirtError, e: raise OperationFailed('KCHSNAP0009E', { 'name': name, 'vm': vm_name, 'err': e.message })
def get_vmsnapshot(self, vm_name, name): vir_dom = VMModel.get_vm(vm_name, self.conn) try: return vir_dom.snapshotLookupByName(name, 0) except libvirt.libvirtError, e: code = e.get_error_code() if code == libvirt.VIR_ERR_NO_DOMAIN_SNAPSHOT: raise NotFoundError('KCHSNAP0003E', { 'name': name, 'vm': vm_name }) else: raise OperationFailed('KCHSNAP0004E', { 'name': name, 'vm': vm_name, 'err': e.message })
def update(self, vm_name, dev_name, params): old_disk_used_by = None new_disk_used_by = None dom = VMModel.get_vm(vm_name, self.conn) dev_info = self.lookup(vm_name, dev_name) if dev_info['type'] != 'cdrom': raise InvalidOperation("KCHVMSTOR0006E") params['path'] = params.get('path', '') old_disk_path = dev_info['path'] new_disk_path = params['path'] if new_disk_path != old_disk_path: # An empty path means a CD-ROM was empty or ejected: if old_disk_path is not '': old_disk_used_by = get_disk_used_by( self.objstore, self.conn, old_disk_path) if new_disk_path is not '': new_disk_used_by = get_disk_used_by( self.objstore, self.conn, new_disk_path) dev_info.update(params) dev, xml = get_disk_xml(dev_info) try: dom.updateDeviceFlags(xml, get_vm_config_flag(dom, 'all')) except Exception as e: raise OperationFailed("KCHVMSTOR0009E", {'error': e.message}) try: if old_disk_used_by is not None and \ vm_name in old_disk_used_by: old_disk_used_by.remove(vm_name) set_disk_used_by(self.objstore, old_disk_path, old_disk_used_by) if new_disk_used_by is not None: new_disk_used_by.append(vm_name) set_disk_used_by(self.objstore, new_disk_path, new_disk_used_by) except Exception as e: wok_log.error("Unable to update dev used_by on update due to" " %s:" % e.message) return dev
def update(self, vm_name, dev_name, params): old_disk_used_by = None new_disk_used_by = None dom = VMModel.get_vm(vm_name, self.conn) dev_info = self.lookup(vm_name, dev_name) if dev_info['type'] != 'cdrom': raise InvalidOperation("KCHVMSTOR0006E") params['path'] = params.get('path', '') old_disk_path = dev_info['path'] new_disk_path = params['path'] if new_disk_path != old_disk_path: # An empty path means a CD-ROM was empty or ejected: if old_disk_path is not '': old_disk_used_by = get_disk_used_by(self.objstore, self.conn, old_disk_path) if new_disk_path is not '': new_disk_used_by = get_disk_used_by(self.objstore, self.conn, new_disk_path) dev_info.update(params) dev, xml = get_disk_xml(dev_info) try: dom.updateDeviceFlags(xml, get_vm_config_flag(dom, 'all')) except Exception as e: raise OperationFailed("KCHVMSTOR0009E", {'error': e.message}) try: if old_disk_used_by is not None and \ vm_name in old_disk_used_by: old_disk_used_by.remove(vm_name) set_disk_used_by(self.objstore, old_disk_path, old_disk_used_by) if new_disk_used_by is not None: new_disk_used_by.append(vm_name) set_disk_used_by(self.objstore, new_disk_path, new_disk_used_by) except Exception as e: wok_log.error("Unable to update dev used_by on update due to" " %s:" % e.message) return dev
def _get_unavailable_devices(self): vm_list = VMsModel.get_vms(self.conn) unavailable_devs = [] for vm in vm_list: dom = VMModel.get_vm(vm, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostDevices = root.devices.hostdev except AttributeError: continue vm_devs = [DeviceModel.deduce_dev_name(e, self.conn) for e in hostDevices] for dev in vm_devs: unavailable_devs.append(dev) return unavailable_devs
def _get_unavailable_devices(self): vm_list = VMsModel.get_vms(self.conn) unavailable_devs = [] for vm in vm_list: dom = VMModel.get_vm(vm, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostdev = root.devices.hostdev except AttributeError: continue vm_devs = [DeviceModel.deduce_dev_name(e, self.conn) for e in hostdev] for dev in vm_devs: unavailable_devs.append(dev) return unavailable_devs
def lookup(self, vmid, dev_name): dom = VMModel.get_vm(vmid, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostdev = root.devices.hostdev except AttributeError: raise NotFoundError('KCHVMHDEV0001E', {'vmid': vmid, 'dev_name': dev_name}) dev_model = DeviceModel(conn=self.conn) for e in hostdev: deduced_name = DeviceModel.deduce_dev_name(e, self.conn) if deduced_name == dev_name: dev_info = dev_model.lookup(dev_name) return {'name': dev_name, 'type': e.attrib['type'], 'product': dev_info.get('product', None), 'vendor': dev_info.get('vendor', None)} raise NotFoundError('KCHVMHDEV0001E', {'vmid': vmid, 'dev_name': dev_name})
def create(self, vm_name, params={}): """Create a snapshot with the current domain state. The VM must be stopped and contain only disks with format 'qcow2'; otherwise an exception will be raised. Parameters: vm_name -- the name of the VM where the snapshot will be created. params -- a dict with the following values: "name": The snapshot name (optional). If omitted, a default value based on the current time will be used. Return: A Task running the operation. """ vir_dom = VMModel.get_vm(vm_name, self.conn) if DOM_STATE_MAP[vir_dom.info()[0]] != u'shutoff': raise InvalidOperation('KCHSNAP0001E', {'vm': vm_name}) # if the VM has a non-CDROM disk with type 'raw', abort. for storage_name in self.vmstorages.get_list(vm_name): storage = self.vmstorage.lookup(vm_name, storage_name) type = storage['type'] format = storage['format'] if type != u'cdrom' and format != u'qcow2': raise InvalidOperation('KCHSNAP0010E', { 'vm': vm_name, 'format': format }) name = params.get('name', unicode(int(time.time()))) task_params = {'vm_name': vm_name, 'name': name} taskid = add_task( u'/plugins/kimchi/vms/%s/snapshots/%s' % (vm_name, name), self._create_task, self.objstore, task_params) return self.task.lookup(taskid)
def get_disk_used_by(objstore, conn, path): try: with objstore as session: try: used_by = session.get('storagevolume', path)['used_by'] except (KeyError, NotFoundError): wok_log.info('Volume %s not found in obj store.' % path) used_by = [] # try to find this volume in existing vm vms_list = VMsModel.get_vms(conn) for vm in vms_list: dom = VMModel.get_vm(vm, conn) storages = get_vm_disks(dom) for disk in storages.keys(): d_info = get_vm_disk_info(dom, disk) if path == d_info['path']: used_by.append(vm) try: session.store('storagevolume', path, {'used_by': used_by}) except Exception as e: # Let the exception be raised. If we allow disks' # used_by to be out of sync, data corruption could # occour if a disk is added to two guests # unknowingly. wok_log.error('Unable to store storage volume id in' ' objectstore due error: %s', e.message) raise OperationFailed('KCHVOL0017E', {'err': e.message}) except Exception as e: # This exception is going to catch errors returned by 'with', # specially ones generated by 'session.store'. It is outside # to avoid conflict with the __exit__ function of 'with' raise OperationFailed('KCHVOL0017E', {'err': e.message}) return used_by
def _attach_usb_device(self, vmid, dev_info): xmlstr = self._get_usb_device_xml(dev_info) dom = VMModel.get_vm(vmid, self.conn) dom.attachDeviceFlags(xmlstr, get_vm_config_flag(dom, mode='all')) return dev_info['name']
def get_vmifaces(vm, conn): dom = VMModel.get_vm(vm, conn) xml = dom.XMLDesc(0) root = objectify.fromstring(xml) return root.devices.findall("interface")
def get_list(self, vm_name): dom = VMModel.get_vm(vm_name, self.conn) return get_vm_disks(dom).keys()
def create(self, vm_name, params): vol_model = None # Path will never be blank due to API.json verification. # There is no need to cover this case here. if not ('vol' in params) ^ ('path' in params): raise InvalidParameter("KCHVMSTOR0017E") dom = VMModel.get_vm(vm_name, self.conn) params['bus'] = _get_device_bus(params['type'], dom) params['format'] = 'raw' dev_list = [ dev for dev, bus in get_vm_disks(dom).iteritems() if bus == params['bus'] ] dev_list.sort() if len(dev_list) == 0: params['index'] = 0 else: char = dev_list.pop()[2] params['index'] = string.ascii_lowercase.index(char) + 1 if (params['bus'] not in HOTPLUG_TYPE and DOM_STATE_MAP[dom.info()[0]] != 'shutoff'): raise InvalidOperation('KCHVMSTOR0011E') if params.get('vol'): try: pool = params['pool'] vol_model = StorageVolumeModel(conn=self.conn, objstore=self.objstore) vol_info = vol_model.lookup(pool, params['vol']) except KeyError: raise InvalidParameter("KCHVMSTOR0012E") except Exception as e: raise InvalidParameter("KCHVMSTOR0015E", {'error': e}) if len(vol_info['used_by']) != 0: raise InvalidParameter("KCHVMSTOR0016E") valid_format = { "disk": ["raw", "bochs", "qcow", "qcow2", "qed", "vmdk"], "cdrom": "iso" } if vol_info['type'] == 'file': if (params['type'] == 'disk' and vol_info['format'] in valid_format[params['type']]): params['format'] = vol_info['format'] else: raise InvalidParameter("KCHVMSTOR0018E", { "format": vol_info['format'], "type": params['type'] }) if (params['format'] == 'raw' and not vol_info['isvalid']): message = 'This is not a valid RAW disk image.' raise OperationFailed('KCHVMSTOR0008E', {'error': message}) params['path'] = vol_info['path'] params['disk'] = vol_info['type'] params.update(self._get_available_bus_address(params['bus'], vm_name)) # Add device to VM dev, xml = get_disk_xml(params) try: dom = VMModel.get_vm(vm_name, self.conn) dom.attachDeviceFlags(xml, get_vm_config_flag(dom, 'all')) except Exception as e: raise OperationFailed("KCHVMSTOR0008E", {'error': e.message}) # Don't put a try-block here. Let the exception be raised. If we # allow disks used_by to be out of sync, data corruption could # occour if a disk is added to two guests unknowingly. if params.get('vol'): used_by = vol_info['used_by'] used_by.append(vm_name) set_disk_used_by(self.objstore, params['path'], used_by) return dev
def create(self, vm_name, params): vol_model = None # Path will never be blank due to API.json verification. # There is no need to cover this case here. if not ('vol' in params) ^ ('path' in params): raise InvalidParameter("KCHVMSTOR0017E") dom = VMModel.get_vm(vm_name, self.conn) params['bus'] = _get_device_bus(params['type'], dom) params['format'] = 'raw' dev_list = [dev for dev, bus in get_vm_disks(dom).iteritems() if bus == params['bus']] dev_list.sort() if len(dev_list) == 0: params['index'] = 0 else: char = dev_list.pop()[2] params['index'] = string.ascii_lowercase.index(char) + 1 if (params['bus'] not in HOTPLUG_TYPE and DOM_STATE_MAP[dom.info()[0]] != 'shutoff'): raise InvalidOperation('KCHVMSTOR0011E') if params.get('vol'): try: pool = params['pool'] vol_model = StorageVolumeModel(conn=self.conn, objstore=self.objstore) vol_info = vol_model.lookup(pool, params['vol']) except KeyError: raise InvalidParameter("KCHVMSTOR0012E") except Exception as e: raise InvalidParameter("KCHVMSTOR0015E", {'error': e}) if len(vol_info['used_by']) != 0: raise InvalidParameter("KCHVMSTOR0016E") valid_format = { "disk": ["raw", "bochs", "qcow", "qcow2", "qed", "vmdk"], "cdrom": "iso"} if vol_info['type'] == 'file': if (params['type'] == 'disk' and vol_info['format'] in valid_format[params['type']]): params['format'] = vol_info['format'] else: raise InvalidParameter("KCHVMSTOR0018E", {"format": vol_info['format'], "type": params['type']}) if (params['format'] == 'raw' and not vol_info['isvalid']): message = 'This is not a valid RAW disk image.' raise OperationFailed('KCHVMSTOR0008E', {'error': message}) params['path'] = vol_info['path'] params['disk'] = vol_info['type'] params.update(self._get_available_bus_address(params['bus'], vm_name)) # Add device to VM dev, xml = get_disk_xml(params) try: dom = VMModel.get_vm(vm_name, self.conn) dom.attachDeviceFlags(xml, get_vm_config_flag(dom, 'all')) except Exception as e: raise OperationFailed("KCHVMSTOR0008E", {'error': e.message}) # Don't put a try-block here. Let the exception be raised. If we # allow disks used_by to be out of sync, data corruption could # occour if a disk is added to two guests unknowingly. if params.get('vol'): used_by = vol_info['used_by'] used_by.append(vm_name) set_disk_used_by(self.objstore, params['path'], used_by) return dev
def _get_device_bus(dev_type, dom): try: version, distro = VMModel.vm_get_os_metadata(dom) except: version, distro = ('unknown', 'unknown') return lookup(distro, version)[dev_type + '_bus']
def lookup(self, vm_name, dev_name): # Retrieve disk xml and format return dict dom = VMModel.get_vm(vm_name, self.conn) return get_vm_disk_info(dom, dev_name)
def _get_device_bus(dev_type, dom): try: version, distro = VMModel.vm_get_os_metadata(dom) except: version, distro = ('unknown', 'unknown') return lookup(distro, version)[dev_type+'_bus']