Example #1
0
    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
            })
Example #2
0
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
Example #3
0
    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)
Example #4
0
 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)
Example #5
0
    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))
Example #6
0
    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})
Example #7
0
 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)
Example #8
0
    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))
Example #9
0
    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']]
Example #10
0
    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
            })
Example #11
0
    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
        })
Example #12
0
    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
Example #13
0
    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})
Example #14
0
    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']
Example #15
0
    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']
Example #16
0
    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']
Example #17
0
    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]
Example #18
0
    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]
Example #19
0
    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})
Example #20
0
    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
            })
Example #21
0
    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})
Example #22
0
    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})
Example #23
0
    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
            })
Example #24
0
    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)
Example #25
0
    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})
Example #26
0
    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
            })
Example #27
0
    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
                })
Example #28
0
    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
Example #29
0
    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
Example #30
0
    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
Example #31
0
    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
Example #32
0
    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})
Example #33
0
    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)
Example #34
0
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
Example #35
0
 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']
Example #36
0
    def get_vmifaces(vm, conn):
        dom = VMModel.get_vm(vm, conn)
        xml = dom.XMLDesc(0)
        root = objectify.fromstring(xml)

        return root.devices.findall("interface")
Example #37
0
 def get_list(self, vm_name):
     dom = VMModel.get_vm(vm_name, self.conn)
     return get_vm_disks(dom).keys()
Example #38
0
    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
Example #39
0
    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
Example #40
0
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']
Example #41
0
 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)
Example #42
0
 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)
Example #43
0
 def get_list(self, vm_name):
     dom = VMModel.get_vm(vm_name, self.conn)
     return get_vm_disks(dom).keys()
Example #44
0
 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']
Example #45
0
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']