示例#1
0
    def _vm_get_graphics(self, name):
        dom = self.get_vm(name, self.conn)
        xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE)

        expr = "/domain/devices/graphics/@type"
        res = xpath_get_text(xml, expr)
        graphics_type = res[0] if res else None

        expr = "/domain/devices/graphics/@listen"
        res = xpath_get_text(xml, expr)
        graphics_listen = res[0] if res else None

        graphics_port = graphics_passwd = graphics_passwdValidTo = None
        if graphics_type:
            expr = "/domain/devices/graphics[@type='%s']/@port"
            res = xpath_get_text(xml, expr % graphics_type)
            graphics_port = int(res[0]) if res else None

            expr = "/domain/devices/graphics[@type='%s']/@passwd"
            res = xpath_get_text(xml, expr % graphics_type)
            graphics_passwd = res[0] if res else None

            expr = "/domain/devices/graphics[@type='%s']/@passwdValidTo"
            res = xpath_get_text(xml, expr % graphics_type)
            if res:
                to = time.mktime(time.strptime(res[0], '%Y-%m-%dT%H:%M:%S'))
                graphics_passwdValidTo = to - time.mktime(time.gmtime())

        return (graphics_type, graphics_listen, graphics_port,
                graphics_passwd, graphics_passwdValidTo)
示例#2
0
    def test_ip_xml(self):
        """
        Test network ip xml
        """
        dhcp_range = {"start": "192.168.122.100", "end": "192.168.122.254"}
        params = {}

        dhcp = nxml._get_dhcp_elem(**params)
        self.assertEquals(None, dhcp)

        params["net"] = "192.168.122.0/255.255.255.0"
        params["dhcp"] = {'range': dhcp_range}
        xml = ET.tostring(nxml._get_ip_elem(**params))
        start = xpath_get_text(xml, "/ip/dhcp/range/@start")[0]
        end = xpath_get_text(xml, "/ip/dhcp/range/@end")[0]
        self.assertEquals(dhcp_range['start'], start)
        self.assertEquals(dhcp_range['end'], end)

        address = xpath_get_text(xml, "/ip/@address")[0]
        netmask = xpath_get_text(xml, "/ip/@netmask")[0]
        self.assertEquals(address, params["net"].split("/")[0])
        self.assertEquals(netmask, params["net"].split("/")[1])

        # test _get_ip_xml can accepts strings: '192.168.122.0/24',
        # which is same as "192.168.122.0/255.255.255.0"
        params["net"] = "192.168.122.0/24"
        xml = ET.tostring(nxml._get_ip_elem(**params))
        netmask = xpath_get_text(xml, "/ip/@netmask")[0]
        self.assertEquals(netmask,
                          str(ipaddr.IPNetwork(params["net"]).netmask))
示例#3
0
文件: vms.py 项目: gouzongmei/kimchi
    def _vm_get_graphics(self, name):
        dom = self.get_vm(name, self.conn)
        xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE)

        expr = "/domain/devices/graphics/@type"
        res = xpath_get_text(xml, expr)
        graphics_type = res[0] if res else None

        expr = "/domain/devices/graphics/@listen"
        res = xpath_get_text(xml, expr)
        graphics_listen = res[0] if res else None

        graphics_port = graphics_passwd = graphics_passwdValidTo = None
        if graphics_type:
            expr = "/domain/devices/graphics[@type='%s']/@port"
            res = xpath_get_text(xml, expr % graphics_type)
            graphics_port = int(res[0]) if res else None

            expr = "/domain/devices/graphics[@type='%s']/@passwd"
            res = xpath_get_text(xml, expr % graphics_type)
            graphics_passwd = res[0] if res else None

            expr = "/domain/devices/graphics[@type='%s']/@passwdValidTo"
            res = xpath_get_text(xml, expr % graphics_type)
            if res:
                to = time.mktime(time.strptime(res[0], '%Y-%m-%dT%H:%M:%S'))
                graphics_passwdValidTo = to - time.mktime(time.gmtime())

        return (graphics_type, graphics_listen, graphics_port,
                graphics_passwd, graphics_passwdValidTo)
示例#4
0
文件: vms.py 项目: cbosdo/kimchi
    def _vm_update_access_metadata(self, dom, params):
        users = groups = None
        if "users" in params:
            users = params["users"]
            invalid_users = set(users) - set(self.users.get_list())
            if len(invalid_users) != 0:
                raise InvalidParameter("KCHVM0027E",
                                       {'users': ", ".join(invalid_users)})
        if "groups" in params:
            groups = params["groups"]
            invalid_groups = set(groups) - set(self.groups.get_list())
            if len(invalid_groups) != 0:
                raise InvalidParameter("KCHVM0028E",
                                       {'groups': ", ".join(invalid_groups)})

        if users is None and groups is None:
            return

        access_xml = (get_metadata_node(dom, "access") or
                      """<access></access>""")
        old_users = xpath_get_text(access_xml, "/access/user")
        old_groups = xpath_get_text(access_xml, "/access/group")
        users = old_users if users is None else users
        groups = old_groups if groups is None else groups

        node = self._build_access_elem(users, groups)
        set_metadata_node(dom, node)
示例#5
0
    def test_dhcp_xml(self):
        """
        Test network dhcp xml
        """
        dhcp_range = {"start": "192.168.122.100", "end": "192.168.122.254"}
        host1 = {"mac": "00:16:3e:77:e2:ed",
                 "name": "foo.example.com",
                 "ip": "192.168.122.10"}
        host2 = {"mac": "00:16:3e:3e:a9:1a",
                 "name": "bar.example.com",
                 "ip": "192.168.122.11"}
        params = {}

        dhcp = nxml._get_dhcp_elem(**params)
        self.assertEquals(None, dhcp)

        params["range"] = dhcp_range
        xml = ET.tostring(nxml._get_dhcp_elem(**params))
        start = xpath_get_text(xml, "/dhcp/range/@start")
        end = xpath_get_text(xml, "/dhcp/range/@end")
        self.assertEquals(dhcp_range['start'], start[0])
        self.assertEquals(dhcp_range['end'], end[0])

        params["hosts"] = [host1, host2]
        xml = ET.tostring(nxml._get_dhcp_elem(**params))
        ip = xpath_get_text(xml, "/dhcp/host/@ip")
        self.assertEquals(ip, [host1['ip'], host2['ip']])
示例#6
0
文件: vms.py 项目: cbosdo/kimchi
    def lookup(self, name):
        dom = self.get_vm(name, self.conn)
        info = dom.info()
        state = DOM_STATE_MAP[info[0]]
        screenshot = None
        # (type, listen, port, passwd, passwdValidTo)
        graphics = self._vm_get_graphics(name)
        graphics_port = graphics[2]
        graphics_port = graphics_port if state == 'running' else None
        try:
            if state == 'running' and self._has_video(dom):
                screenshot = self.vmscreenshot.lookup(name)
            elif state == 'shutoff':
                # reset vm stats when it is powered off to avoid sending
                # incorrect (old) data
                stats[dom.UUIDString()] = {}
        except NotFoundError:
            pass

        with self.objstore as session:
            try:
                extra_info = session.get('vm', dom.UUIDString())
            except NotFoundError:
                extra_info = {}
        icon = extra_info.get('icon')

        vm_stats = stats.get(dom.UUIDString(), {})
        res = {}
        res['cpu_utilization'] = vm_stats.get('cpu', 0)
        res['net_throughput'] = vm_stats.get('net_io', 0)
        res['net_throughput_peak'] = vm_stats.get('max_net_io', 100)
        res['io_throughput'] = vm_stats.get('disk_io', 0)
        res['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)

        access_xml = (get_metadata_node(dom, "access") or
                      """<access></access>""")
        users = xpath_get_text(access_xml, "/access/user")
        groups = xpath_get_text(access_xml, "/access/group")

        return {'name': name,
                'state': state,
                'stats': res,
                'uuid': dom.UUIDString(),
                'memory': info[2] >> 10,
                'cpus': info[3],
                'screenshot': screenshot,
                'icon': icon,
                # (type, listen, port, passwd, passwdValidTo)
                'graphics': {"type": graphics[0],
                             "listen": graphics[1],
                             "port": graphics_port,
                             "passwd": graphics[3],
                             "passwdValidTo": graphics[4]},
                'users': users,
                'groups': groups,
                'access': 'full',
                'persistent': True if dom.isPersistent() else False
                }
示例#7
0
 def test_to_xml(self):
     graphics = {'type': 'spice', 'listen': '127.0.0.1'}
     vm_uuid = str(uuid.uuid4()).replace('-', '')
     t = VMTemplate({'name': 'test-template', 'cdrom': self.iso})
     xml = t.to_vm_xml('test-vm', vm_uuid, graphics=graphics)
     self.assertEquals(vm_uuid, xpath_get_text(xml, "/domain/uuid")[0])
     self.assertEquals('test-vm', xpath_get_text(xml, "/domain/name")[0])
     expr = "/domain/devices/graphics/@type"
     self.assertEquals(graphics['type'], xpath_get_text(xml, expr)[0])
     expr = "/domain/devices/graphics/@listen"
     self.assertEquals(graphics['listen'], xpath_get_text(xml, expr)[0])
示例#8
0
 def test_to_xml(self):
     graphics = {'type': 'spice', 'listen': '127.0.0.1'}
     vm_uuid = str(uuid.uuid4()).replace('-', '')
     t = VMTemplate({'name': 'test-template', 'cdrom': self.iso})
     xml = t.to_vm_xml('test-vm', vm_uuid, graphics=graphics)
     self.assertEquals(vm_uuid, xpath_get_text(xml, "/domain/uuid")[0])
     self.assertEquals('test-vm', xpath_get_text(xml, "/domain/name")[0])
     expr = "/domain/devices/graphics/@type"
     self.assertEquals(graphics['type'], xpath_get_text(xml, expr)[0])
     expr = "/domain/devices/graphics/@listen"
     self.assertEquals(graphics['listen'], xpath_get_text(xml, expr)[0])
示例#9
0
    def get_network_from_xml(xml):
        address = xpath_get_text(xml, "/network/ip/@address")
        address = address and address[0] or ''
        netmask = xpath_get_text(xml, "/network/ip/@netmask")
        netmask = netmask and netmask[0] or ''
        net = address and netmask and "/".join([address, netmask]) or ''

        dhcp_start = xpath_get_text(xml, "/network/ip/dhcp/range/@start")
        dhcp_start = dhcp_start and dhcp_start[0] or ''
        dhcp_end = xpath_get_text(xml, "/network/ip/dhcp/range/@end")
        dhcp_end = dhcp_end and dhcp_end[0] or ''
        dhcp = {'start': dhcp_start, 'end': dhcp_end}

        forward_mode = xpath_get_text(xml, "/network/forward/@mode")
        forward_mode = forward_mode and forward_mode[0] or ''
        forward_if = xpath_get_text(xml, "/network/forward/interface/@dev")
        forward_pf = xpath_get_text(xml, "/network/forward/pf/@dev")
        bridge = xpath_get_text(xml, "/network/bridge/@name")
        bridge = bridge and bridge[0] or ''
        return {
            'subnet': net,
            'dhcp': dhcp,
            'bridge': bridge,
            'forward': {
                'mode': forward_mode,
                'interface': forward_if,
                'pf': forward_pf
            }
        }
示例#10
0
文件: vms.py 项目: cbosdo/kimchi
    def delete(self, name):
        conn = self.conn.get()
        dom = self.get_vm(name, self.conn)
        self._vmscreenshot_delete(dom.UUIDString())
        paths = self._vm_get_disk_paths(dom)
        info = self.lookup(name)

        if info['state'] == 'running':
            self.poweroff(name)

        try:
            dom.undefine()
        except libvirt.libvirtError as e:
            raise OperationFailed("KCHVM0021E",
                                  {'name': name, 'err': e.get_error_message()})

        for path in paths:
            vol = conn.storageVolLookupByPath(path)
            pool = vol.storagePoolLookupByVolume()
            xml = pool.XMLDesc(0)
            pool_type = xpath_get_text(xml, "/pool/@type")[0]
            if pool_type not in READONLY_POOL_TYPE:
                vol.delete(0)
        try:
            with self.objstore as session:
                session.delete('vm', dom.UUIDString(), ignore_missing=True)
        except Exception as e:
            # It is possible to delete vm without delete its database info
            kimchi_log.error('Error deleting vm information from database: '
                             '%s', e.message)

        vnc.remove_proxy_token(name)
示例#11
0
    def test_forward_xml(self):
        """
        Test network forward xml
        """
        params = {"mode": None}

        forward = nxml._get_forward_elem(**params)
        self.assertEquals(None, forward)

        params["mode"] = 'nat'
        params["dev"] = 'eth0'
        xml = ET.tostring(nxml._get_forward_elem(**params))
        mode = xpath_get_text(xml, "/forward/@mode")[0]
        dev = xpath_get_text(xml, "/forward/@dev")[0]
        self.assertEquals(params['mode'], mode)
        self.assertEquals(params['dev'], dev)
示例#12
0
 def lookup(self, pool, name):
     vol = StorageVolumeModel.get_storagevolume(pool, name, self.conn)
     path = vol.path()
     info = vol.info()
     xml = vol.XMLDesc(0)
     try:
         fmt = xpath_get_text(xml, "/volume/target/format/@type")[0]
     except IndexError:
         # Not all types of libvirt storage can provide volume format
         # infomation. When there is no format information, we assume
         # it's 'raw'.
         fmt = 'raw'
     ref_cnt = get_disk_ref_cnt(self.objstore, self.conn, path)
     res = dict(type=VOLUME_TYPE_MAP[info[0]],
                capacity=info[1],
                allocation=info[2],
                path=path,
                ref_cnt=ref_cnt,
                format=fmt)
     if fmt == 'iso':
         if os.path.islink(path):
             path = os.path.join(os.path.dirname(path), os.readlink(path))
         os_distro = os_version = 'unknown'
         try:
             iso_img = IsoImage(path)
             os_distro, os_version = iso_img.probe()
             bootable = True
         except IsoFormatError:
             bootable = False
         res.update(
             dict(os_distro=os_distro,
                  os_version=os_version,
                  path=path,
                  bootable=bootable))
     return res
示例#13
0
 def lookup(self, pool, name):
     vol = self._get_storagevolume(pool, name)
     path = vol.path()
     info = vol.info()
     xml = vol.XMLDesc(0)
     try:
         fmt = xpath_get_text(xml, "/volume/target/format/@type")[0]
     except IndexError:
         # Not all types of libvirt storage can provide volume format
         # infomation. When there is no format information, we assume
         # it's 'raw'.
         fmt = 'raw'
     ref_cnt = get_disk_ref_cnt(self.objstore, self.conn, path)
     res = dict(type=VOLUME_TYPE_MAP[info[0]],
                capacity=info[1],
                allocation=info[2],
                path=path,
                ref_cnt=ref_cnt,
                format=fmt)
     if fmt == 'iso':
         if os.path.islink(path):
             path = os.path.join(os.path.dirname(path), os.readlink(path))
         os_distro = os_version = 'unknown'
         try:
             iso_img = IsoImage(path)
             os_distro, os_version = iso_img.probe()
             bootable = True
         except IsoFormatError:
             bootable = False
         res.update(
             dict(os_distro=os_distro, os_version=os_version, path=path,
                  bootable=bootable))
     return res
示例#14
0
    def deactivate(self, name):
        if self._pool_used_by_template(name):
            raise InvalidOperation('KCHPOOL0034E', {'name': name})

        pool = self.get_storagepool(name, self.conn)
        # FIXME: nfs workaround - do not try to deactivate a NFS pool
        # if the NFS server is not reachable.
        xml = pool.XMLDesc(0)
        pool_type = xpath_get_text(xml, "/pool/@type")[0]
        if pool_type == 'netfs' and not self._nfs_status_online(pool):
            # block the user from dactivating the pool.
            source = self._get_storage_source(pool_type, xml)
            raise OperationFailed("KCHPOOL0033E",
                                  {'name': name, 'server': source['addr']})
            return
        try:
            persistent = pool.isPersistent()
            pool.destroy()
        except libvirt.libvirtError as e:
            raise OperationFailed("KCHPOOL0010E",
                                  {'name': name, 'err': e.get_error_message()})
        # If pool was not persistent, then it was erased by destroy() and
        # must return nothing here, to trigger _redirect() and avoid errors
        if not persistent:
            return ""
示例#15
0
    def _clone_update_mac_addresses(xml):
        """Update the MAC addresses with new values in the XML descriptor of a
        cloning domain.

        The new MAC addresses will be generated randomly, and their values are
        guaranteed to be distinct from the ones in the original VM.

        Arguments:
        xml -- The XML descriptor of the original domain.

        Return:
        The XML descriptor <xml> with the new MAC addresses instead of the
        old ones.
        """
        old_macs = xpath_get_text(xml, XPATH_DOMAIN_MAC)
        new_macs = []

        for mac in old_macs:
            while True:
                new_mac = model.vmifaces.VMIfacesModel.random_mac()
                # make sure the new MAC doesn't conflict with the original VM
                # and with the new values on the new VM.
                if new_mac not in (old_macs + new_macs):
                    new_macs.append(new_mac)
                    break

            xml = xml_item_update(xml, XPATH_DOMAIN_MAC_BY_ADDRESS % mac,
                                  new_mac, 'address')

        return xml
示例#16
0
文件: vms.py 项目: gouzongmei/kimchi
    def _clone_update_mac_addresses(xml):
        """Update the MAC addresses with new values in the XML descriptor of a
        cloning domain.

        The new MAC addresses will be generated randomly, and their values are
        guaranteed to be distinct from the ones in the original VM.

        Arguments:
        xml -- The XML descriptor of the original domain.

        Return:
        The XML descriptor <xml> with the new MAC addresses instead of the
        old ones.
        """
        old_macs = xpath_get_text(xml, XPATH_DOMAIN_MAC)
        new_macs = []

        for mac in old_macs:
            while True:
                new_mac = model.vmifaces.VMIfacesModel.random_mac()
                # make sure the new MAC doesn't conflict with the original VM
                # and with the new values on the new VM.
                if new_mac not in (old_macs + new_macs):
                    new_macs.append(new_mac)
                    break

            xml = xml_item_update(xml, XPATH_DOMAIN_MAC_BY_ADDRESS % mac,
                                  new_mac, 'address')

        return xml
示例#17
0
    def deactivate(self, name):
        if self._pool_used_by_template(name):
            raise InvalidOperation('KCHPOOL0034E', {'name': name})

        pool = self.get_storagepool(name, self.conn)
        # FIXME: nfs workaround - do not try to deactivate a NFS pool
        # if the NFS server is not reachable.
        xml = pool.XMLDesc(0)
        pool_type = xpath_get_text(xml, "/pool/@type")[0]
        if pool_type == 'netfs' and not self._nfs_status_online(pool):
            # block the user from dactivating the pool.
            source = self._get_storage_source(pool_type, xml)
            raise OperationFailed("KCHPOOL0033E", {
                'name': name,
                'server': source['addr']
            })
            return
        try:
            persistent = pool.isPersistent()
            pool.destroy()
        except libvirt.libvirtError as e:
            raise OperationFailed("KCHPOOL0010E", {
                'name': name,
                'err': e.get_error_message()
            })
        # If pool was not persistent, then it was erased by destroy() and
        # must return nothing here, to trigger _redirect() and avoid errors
        if not persistent:
            return ""
示例#18
0
 def test_to_xml(self):
     graphics = {"type": "spice", "listen": "127.0.0.1"}
     vm_uuid = str(uuid.uuid4()).replace("-", "")
     if os.uname()[4] in ["ppc", "ppc64", "ppc64le"]:
         maxmem = 3328
     else:
         maxmem = 3072
     t = VMTemplate({"name": "test-template", "cdrom": self.iso, "max_memory": maxmem << 10})
     xml = t.to_vm_xml("test-vm", vm_uuid, graphics=graphics)
     self.assertEquals(vm_uuid, xpath_get_text(xml, "/domain/uuid")[0])
     self.assertEquals("test-vm", xpath_get_text(xml, "/domain/name")[0])
     expr = "/domain/devices/graphics/@type"
     self.assertEquals(graphics["type"], xpath_get_text(xml, expr)[0])
     expr = "/domain/devices/graphics/@listen"
     self.assertEquals(graphics["listen"], xpath_get_text(xml, expr)[0])
     expr = "/domain/maxMemory/@slots"
     self.assertEquals("2", xpath_get_text(xml, expr)[0])
示例#19
0
    def lookup(self, name):
        pool = self.get_storagepool(name, self.conn)
        info = pool.info()
        autostart = True if pool.autostart() else False
        persistent = True if pool.isPersistent() else False
        xml = pool.XMLDesc(0)
        path = xpath_get_text(xml, "/pool/target/path")[0]
        pool_type = xpath_get_text(xml, "/pool/@type")[0]
        source = self._get_storage_source(pool_type, xml)
        # FIXME: nfs workaround - prevent any libvirt operation
        # for a nfs if the corresponding NFS server is down.
        if pool_type == 'netfs' and not self._nfs_status_online(pool):
            kimchi_log.debug(
                "NFS pool %s is offline, reason: NFS "
                "server %s is unreachable.", name, source['addr'])
            # Mark state as '4' => inaccessible.
            info[0] = 4
            # skip calculating volumes
            nr_volumes = 0
        else:
            nr_volumes = self._get_storagepool_vols_num(pool)

        res = {
            'state': POOL_STATE_MAP[info[0]],
            'path': path,
            'source': source,
            'type': pool_type,
            'autostart': autostart,
            'capacity': info[1],
            'allocated': info[2],
            'available': info[3],
            'nr_volumes': nr_volumes,
            'persistent': persistent
        }

        if not pool.isPersistent():
            # Deal with deep scan generated pool
            try:
                with self.objstore as session:
                    task_id = session.get('scanning', name)
                res['task_id'] = str(task_id)
                res['type'] = 'kimchi-iso'
            except NotFoundError:
                # User created normal pool
                pass
        return res
示例#20
0
        def _get_ipaddr_info(libvirt_interface_xml):
            search_ip = \
                xpath_get_text(libvirt_interface_xml,
                               "/interface/protocol[@family='ipv4']"
                               "/ip/@address")

            if not len(search_ip):
                return '', ''

            search_prefix = \
                xpath_get_text(libvirt_interface_xml,
                               "/interface/protocol[@family='ipv4']"
                               "/ip/@prefix")

            ip_obj = ipaddr.IPv4Network('%s/%s' % (search_ip[0],
                                                   search_prefix[0]))
            return str(ip_obj.ip), str(ip_obj.netmask)
示例#21
0
        def _get_ipaddr_info(libvirt_interface_xml):
            search_ip = \
                xpath_get_text(libvirt_interface_xml,
                               "/interface/protocol[@family='ipv4']"
                               "/ip/@address")

            if not len(search_ip):
                return '', ''

            search_prefix = \
                xpath_get_text(libvirt_interface_xml,
                               "/interface/protocol[@family='ipv4']"
                               "/ip/@prefix")

            ip_obj = ipaddr.IPv4Network('%s/%s' % (search_ip[0],
                                                   search_prefix[0]))
            return str(ip_obj.ip), str(ip_obj.netmask)
示例#22
0
 def test_to_xml(self):
     graphics = {'type': 'spice', 'listen': '127.0.0.1'}
     vm_uuid = str(uuid.uuid4()).replace('-', '')
     if os.uname()[4] in ['ppc', 'ppc64', 'ppc64le']:
         maxmem = 3328
     else:
         maxmem = 3072
     t = VMTemplate({'name': 'test-template', 'cdrom': self.iso,
                    'max_memory': maxmem << 10})
     xml = t.to_vm_xml('test-vm', vm_uuid, graphics=graphics)
     self.assertEquals(vm_uuid, xpath_get_text(xml, "/domain/uuid")[0])
     self.assertEquals('test-vm', xpath_get_text(xml, "/domain/name")[0])
     expr = "/domain/devices/graphics/@type"
     self.assertEquals(graphics['type'], xpath_get_text(xml, expr)[0])
     expr = "/domain/devices/graphics/@listen"
     self.assertEquals(graphics['listen'], xpath_get_text(xml, expr)[0])
     expr = "/domain/maxMemory/@slots"
     self.assertEquals('2', xpath_get_text(xml, expr)[0])
示例#23
0
    def lookup(self, name):
        pool = self.get_storagepool(name, self.conn)
        info = pool.info()
        autostart = True if pool.autostart() else False
        persistent = True if pool.isPersistent() else False
        xml = pool.XMLDesc(0)
        path = xpath_get_text(xml, "/pool/target/path")[0]
        pool_type = xpath_get_text(xml, "/pool/@type")[0]
        source = self._get_storage_source(pool_type, xml)
        # FIXME: nfs workaround - prevent any libvirt operation
        # for a nfs if the corresponding NFS server is down.
        if pool_type == 'netfs' and not self._nfs_status_online(pool):
            kimchi_log.debug("NFS pool %s is offline, reason: NFS "
                             "server %s is unreachable.", name,
                             source['addr'])
            # Mark state as '4' => inaccessible.
            info[0] = 4
            # skip calculating volumes
            nr_volumes = 0
        else:
            nr_volumes = self._get_storagepool_vols_num(pool)

        res = {'state': POOL_STATE_MAP[info[0]],
               'path': path,
               'source': source,
               'type': pool_type,
               'autostart': autostart,
               'capacity': info[1],
               'allocated': info[2],
               'available': info[3],
               'nr_volumes': nr_volumes,
               'persistent': persistent}

        if not pool.isPersistent():
            # Deal with deep scan generated pool
            try:
                with self.objstore as session:
                    task_id = session.get('scanning', name)
                res['task_id'] = str(task_id)
                res['type'] = 'kimchi-iso'
            except NotFoundError:
                # User created normal pool
                pass
        return res
示例#24
0
文件: vms.py 项目: gouzongmei/kimchi
 def _get_access_info(self, dom):
     users = groups = list()
     access_xml = (get_metadata_node(dom, "access",
                                     self.caps.metadata_support) or
                   """<access></access>""")
     access_info = dictize(access_xml)
     auth = config.get("authentication", "method")
     if ('auth' in access_info['access'] and
             ('type' in access_info['access']['auth'] or
              len(access_info['access']['auth']) > 1)):
         users = xpath_get_text(access_xml,
                                "/access/auth[@type='%s']/user" % auth)
         groups = xpath_get_text(access_xml,
                                 "/access/auth[@type='%s']/group" % auth)
     elif auth == 'pam':
         # Compatible to old permission tagging
         users = xpath_get_text(access_xml, "/access/user")
         groups = xpath_get_text(access_xml, "/access/group")
     return users, groups
示例#25
0
 def _get_access_info(self, dom):
     users = groups = list()
     access_xml = (get_metadata_node(dom, "access",
                                     self.caps.metadata_support) or
                   """<access></access>""")
     access_info = dictize(access_xml)
     auth = config.get("authentication", "method")
     if ('auth' in access_info['access'] and
             ('type' in access_info['access']['auth'] or
              len(access_info['access']['auth']) > 1)):
         users = xpath_get_text(access_xml,
                                "/access/auth[@type='%s']/user" % auth)
         groups = xpath_get_text(access_xml,
                                 "/access/auth[@type='%s']/group" % auth)
     elif auth == 'pam':
         # Compatible to old permission tagging
         users = xpath_get_text(access_xml, "/access/user")
         groups = xpath_get_text(access_xml, "/access/group")
     return users, groups
示例#26
0
    def lookup(self, pool, name):
        vol = StorageVolumeModel.get_storagevolume(pool, name, self.conn)
        path = vol.path()
        info = vol.info()
        xml = vol.XMLDesc(0)
        try:
            fmt = xpath_get_text(xml, "/volume/target/format/@type")[0]
        except IndexError:
            # Not all types of libvirt storage can provide volume format
            # infomation. When there is no format information, we assume
            # it's 'raw'.
            fmt = 'raw'

        iso_img = None

        # 'raw' volumes from 'logical' pools may actually be 'iso';
        # libvirt always reports them as 'raw'
        pool_info = self.storagepool.lookup(pool)
        if pool_info['type'] == 'logical' and fmt == 'raw':
            try:
                iso_img = IsoImage(path)
            except IsoFormatError:
                # not 'iso' afterall
                pass
            else:
                fmt = 'iso'

        used_by = get_disk_used_by(self.objstore, self.conn, path)
        res = dict(type=VOLUME_TYPE_MAP[info[0]],
                   capacity=info[1],
                   allocation=info[2],
                   path=path,
                   used_by=used_by,
                   format=fmt)
        if fmt == 'iso':
            if os.path.islink(path):
                path = os.path.join(os.path.dirname(path), os.readlink(path))
            os_distro = os_version = 'unknown'
            try:
                if iso_img is None:
                    iso_img = IsoImage(path)
                os_distro, os_version = iso_img.probe()
                bootable = True
            except IsoFormatError:
                bootable = False
            res.update(
                dict(os_distro=os_distro,
                     os_version=os_version,
                     path=path,
                     bootable=bootable))
        return res
示例#27
0
    def _get_storage_source(self, pool_type, pool_xml):
        source = {}
        if pool_type not in STORAGE_SOURCES:
            return source

        for key, val in STORAGE_SOURCES[pool_type].items():
            res = xpath_get_text(pool_xml, val)
            if len(res) == 1:
                source[key] = res[0]
            elif len(res) == 0:
                source[key] = ""
            else:
                source[key] = res
        return source
示例#28
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})
示例#29
0
    def _get_storage_source(self, pool_type, pool_xml):
        source = {}
        if pool_type not in STORAGE_SOURCES:
            return source

        for key, val in STORAGE_SOURCES[pool_type].items():
            res = xpath_get_text(pool_xml, val)
            if len(res) == 1:
                source[key] = res[0]
            elif len(res) == 0:
                source[key] = ""
            else:
                source[key] = res
        return source
示例#30
0
    def lookup(self, pool, name):
        vol = StorageVolumeModel.get_storagevolume(pool, name, self.conn)
        path = vol.path()
        info = vol.info()
        xml = vol.XMLDesc(0)
        try:
            fmt = xpath_get_text(xml, "/volume/target/format/@type")[0]
        except IndexError:
            # Not all types of libvirt storage can provide volume format
            # infomation. When there is no format information, we assume
            # it's 'raw'.
            fmt = 'raw'

        iso_img = None

        # 'raw' volumes from 'logical' pools may actually be 'iso';
        # libvirt always reports them as 'raw'
        pool_info = self.storagepool.lookup(pool)
        if pool_info['type'] == 'logical' and fmt == 'raw':
            try:
                iso_img = IsoImage(path)
            except IsoFormatError:
                # not 'iso' afterall
                pass
            else:
                fmt = 'iso'

        used_by = get_disk_used_by(self.objstore, self.conn, path)
        res = dict(type=VOLUME_TYPE_MAP[info[0]],
                   capacity=info[1],
                   allocation=info[2],
                   path=path,
                   used_by=used_by,
                   format=fmt)
        if fmt == 'iso':
            if os.path.islink(path):
                path = os.path.join(os.path.dirname(path), os.readlink(path))
            os_distro = os_version = 'unknown'
            try:
                if iso_img is None:
                    iso_img = IsoImage(path)
                os_distro, os_version = iso_img.probe()
                bootable = True
            except IsoFormatError:
                bootable = False
            res.update(
                dict(os_distro=os_distro, os_version=os_version, path=path,
                     bootable=bootable))
        return res
示例#31
0
    def start(self, name):
        # make sure the ISO file has read permission
        dom = self.get_vm(name, self.conn)
        xml = dom.XMLDesc(0)
        xpath = "/domain/devices/disk[@device='cdrom']/source/@file"
        isofiles = xpath_get_text(xml, xpath)
        for iso in isofiles:
            run_setfacl_set_attr(iso)

        dom = self.get_vm(name, self.conn)
        try:
            dom.create()
        except libvirt.libvirtError as e:
            raise OperationFailed("KCHVM0019E",
                                  {'name': name, 'err': e.get_error_message()})
示例#32
0
文件: vms.py 项目: gouzongmei/kimchi
    def start(self, name):
        # make sure the ISO file has read permission
        dom = self.get_vm(name, self.conn)
        xml = dom.XMLDesc(0)
        xpath = "/domain/devices/disk[@device='cdrom']/source/@file"
        isofiles = xpath_get_text(xml, xpath)
        for iso in isofiles:
            run_setfacl_set_attr(iso)

        dom = self.get_vm(name, self.conn)
        try:
            dom.create()
        except libvirt.libvirtError as e:
            raise OperationFailed("KCHVM0019E",
                                  {'name': name, 'err': e.get_error_message()})
示例#33
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
            })
示例#34
0
    def update(self, name, params):
        pool = self.get_storagepool(name, self.conn)
        if 'autostart' in params:
            if params['autostart']:
                pool.setAutostart(1)
            else:
                pool.setAutostart(0)

        if 'disks' in params:
            # check if pool is type 'logical'
            xml = pool.XMLDesc(0)
            pool_type = xpath_get_text(xml, "/pool/@type")[0]
            if pool_type != 'logical':
                raise InvalidOperation('KCHPOOL0029E')
            self._update_lvm_disks(name, params['disks'])
        ident = pool.name()
        return ident.decode('utf-8')
示例#35
0
 def activate(self, name):
     pool = self.get_storagepool(name, self.conn)
     # FIXME: nfs workaround - do not activate a NFS pool
     # if the NFS server is not reachable.
     xml = pool.XMLDesc(0)
     pool_type = xpath_get_text(xml, "/pool/@type")[0]
     if pool_type == 'netfs' and not self._nfs_status_online(pool):
         # block the user from activating the pool.
         source = self._get_storage_source(pool_type, xml)
         raise OperationFailed("KCHPOOL0032E",
                               {'name': name, 'server': source['addr']})
         return
     try:
         pool.create(0)
     except libvirt.libvirtError as e:
         raise OperationFailed("KCHPOOL0009E",
                               {'name': name, 'err': e.get_error_message()})
示例#36
0
 def _nfs_status_online(self, pool, poolArgs=None):
     if not poolArgs:
         xml = pool.XMLDesc(0)
         pool_type = xpath_get_text(xml, "/pool/@type")[0]
         source = self._get_storage_source(pool_type, xml)
         poolArgs = {}
         poolArgs['name'] = pool.name()
         poolArgs['type'] = pool_type
         poolArgs['source'] = {'path': source['path'],
                               'host': source['addr']}
     conn = self.conn.get()
     poolDef = StoragePoolDef.create(poolArgs)
     try:
         poolDef.prepare(conn)
         return True
     except Exception:
         return False
示例#37
0
    def update(self, name, params):
        pool = self.get_storagepool(name, self.conn)
        if 'autostart' in params:
            if params['autostart']:
                pool.setAutostart(1)
            else:
                pool.setAutostart(0)

        if 'disks' in params:
            # check if pool is type 'logical'
            xml = pool.XMLDesc(0)
            pool_type = xpath_get_text(xml, "/pool/@type")[0]
            if pool_type != 'logical':
                raise InvalidOperation('KCHPOOL0029E')
            self._update_lvm_disks(name, params['disks'])
        ident = pool.name()
        return ident
示例#38
0
 def _nfs_status_online(self, pool, poolArgs=None):
     if not poolArgs:
         xml = pool.XMLDesc(0)
         pool_type = xpath_get_text(xml, "/pool/@type")[0]
         source = self._get_storage_source(pool_type, xml)
         poolArgs = {}
         poolArgs['name'] = pool.name()
         poolArgs['type'] = pool_type
         poolArgs['source'] = {
             'path': source['path'],
             'host': source['addr']
         }
     conn = self.conn.get()
     poolDef = StoragePoolDef.create(poolArgs)
     try:
         poolDef.prepare(conn)
         return True
     except Exception:
         return False
示例#39
0
 def _get_devices_fc_host(self):
     conn = self.conn.get()
     # Libvirt < 1.0.5 does not support fc_host capability
     if not self.caps.fc_host_support:
         ret = []
         scsi_hosts = self._get_devices_with_capability('scsi_host')
         for host in scsi_hosts:
             xml = conn.nodeDeviceLookupByName(host).XMLDesc(0)
             path = '/device/capability/capability/@type'
             if 'fc_host' in xpath_get_text(xml, path):
                 ret.append(host)
         return ret
     # Double verification to catch the case where the libvirt
     # supports fc_host but does not, for some reason, recognize
     # the libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_FC_HOST
     # attribute.
     if not self.cap_map['fc_host']:
         return conn.listDevices('fc_host', 0)
     return self._get_devices_with_capability('fc_host')
示例#40
0
 def activate(self, name):
     pool = self.get_storagepool(name, self.conn)
     # FIXME: nfs workaround - do not activate a NFS pool
     # if the NFS server is not reachable.
     xml = pool.XMLDesc(0)
     pool_type = xpath_get_text(xml, "/pool/@type")[0]
     if pool_type == 'netfs' and not self._nfs_status_online(pool):
         # block the user from activating the pool.
         source = self._get_storage_source(pool_type, xml)
         raise OperationFailed("KCHPOOL0032E", {
             'name': name,
             'server': source['addr']
         })
         return
     try:
         pool.create(0)
     except libvirt.libvirtError as e:
         raise OperationFailed("KCHPOOL0009E", {
             'name': name,
             'err': e.get_error_message()
         })
示例#41
0
    def template_volume_validate(self, tmp_volumes, pool):
        kwargs = {'conn': self.conn, 'objstore': self.objstore}
        pool_type = xpath_get_text(pool.XMLDesc(0), "/pool/@type")[0]
        pool_name = pool.name()

        # as we discussion, we do not mix disks from 2 different types of
        # storage pools, for instance: we do not create a template with 2
        # disks, where one comes from a SCSI pool and other is a .img in
        # a DIR pool.
        if pool_type in ['iscsi', 'scsi']:
            if not tmp_volumes:
                raise InvalidParameter("KCHTMPL0018E")

            storagevolumes = __import__("kimchi.model.storagevolumes",
                                        fromlist=[''])
            pool_volumes = storagevolumes.StorageVolumesModel(
                **kwargs).get_list(pool_name)
            vols = set(tmp_volumes) - set(pool_volumes)
            if vols:
                raise InvalidParameter("KCHTMPL0019E", {'pool': pool_name,
                                                        'volume': vols})
示例#42
0
    def get_network_from_xml(xml):
        address = xpath_get_text(xml, "/network/ip/@address")
        address = address and address[0] or ''
        netmask = xpath_get_text(xml, "/network/ip/@netmask")
        netmask = netmask and netmask[0] or ''
        net = address and netmask and "/".join([address, netmask]) or ''

        dhcp_start = xpath_get_text(xml, "/network/ip/dhcp/range/@start")
        dhcp_start = dhcp_start and dhcp_start[0] or ''
        dhcp_end = xpath_get_text(xml, "/network/ip/dhcp/range/@end")
        dhcp_end = dhcp_end and dhcp_end[0] or ''
        dhcp = {'start': dhcp_start, 'end': dhcp_end}

        forward_mode = xpath_get_text(xml, "/network/forward/@mode")
        forward_mode = forward_mode and forward_mode[0] or ''
        forward_if = xpath_get_text(xml, "/network/forward/interface/@dev")
        forward_pf = xpath_get_text(xml, "/network/forward/pf/@dev")
        bridge = xpath_get_text(xml, "/network/bridge/@name")
        bridge = bridge and bridge[0] or ''
        return {'subnet': net, 'dhcp': dhcp, 'bridge': bridge,
                'forward': {'mode': forward_mode,
                            'interface': forward_if,
                            'pf': forward_pf}}
示例#43
0
    def template_volume_validate(self, tmp_volumes, pool):
        kwargs = {'conn': self.conn, 'objstore': self.objstore}
        pool_type = xpath_get_text(pool.XMLDesc(0), "/pool/@type")[0]
        pool_name = unicode(pool.name(), 'utf-8')

        # as we discussion, we do not mix disks from 2 different types of
        # storage pools, for instance: we do not create a template with 2
        # disks, where one comes from a SCSI pool and other is a .img in
        # a DIR pool.
        if pool_type in ['iscsi', 'scsi']:
            if not tmp_volumes:
                raise InvalidParameter("KCHTMPL0018E")

            storagevolumes = __import__("kimchi.model.storagevolumes",
                                        fromlist=[''])
            pool_volumes = storagevolumes.StorageVolumesModel(
                **kwargs).get_list(pool_name)
            vols = set(tmp_volumes) - set(pool_volumes)
            if vols:
                raise InvalidParameter("KCHTMPL0019E", {
                    'pool': pool_name,
                    'volume': vols
                })
示例#44
0
    def _clone_update_disks(self, xml, rollback):
        """Clone disks from a virtual machine. The disks are copied as new
        volumes and the new VM's XML is updated accordingly.

        Arguments:
        xml -- The XML descriptor of the original VM + new value for
            "/domain/uuid".
        rollback -- A rollback context so the new volumes can be removed if an
            error occurs during the cloning operation.

        Return:
        The XML descriptor <xml> with the new disk paths instead of the
        old ones.
        """
        # the UUID will be used to create the disk paths
        uuid = xpath_get_text(xml, XPATH_DOMAIN_UUID)[0]
        all_paths = xpath_get_text(xml, XPATH_DOMAIN_DISK)

        vir_conn = self.conn.get()

        for i, path in enumerate(all_paths):
            try:
                vir_orig_vol = vir_conn.storageVolLookupByPath(path)
                vir_pool = vir_orig_vol.storagePoolLookupByVolume()

                orig_pool_name = vir_pool.name().decode('utf-8')
                orig_vol_name = vir_orig_vol.name().decode('utf-8')
            except libvirt.libvirtError, e:
                domain_name = xpath_get_text(xml, XPATH_DOMAIN_NAME)[0]
                raise OperationFailed('KCHVM0035E', {'name': domain_name,
                                                     'err': e.message})

            orig_pool = self.storagepool.lookup(orig_pool_name)
            orig_vol = self.storagevolume.lookup(orig_pool_name, orig_vol_name)

            new_pool_name = orig_pool_name
            new_pool = orig_pool

            if orig_pool['type'] in ['dir', 'netfs', 'logical']:
                # if a volume in a pool 'dir', 'netfs' or 'logical' cannot hold
                # a new volume with the same size, the pool 'default' should
                # be used
                if orig_vol['capacity'] > orig_pool['available']:
                    kimchi_log.warning('storage pool \'%s\' doesn\'t have '
                                       'enough free space to store image '
                                       '\'%s\'; falling back to \'default\'',
                                       orig_pool_name, path)
                    new_pool_name = u'default'
                    new_pool = self.storagepool.lookup(u'default')

                    # ...and if even the pool 'default' cannot hold a new
                    # volume, raise an exception
                    if orig_vol['capacity'] > new_pool['available']:
                        domain_name = xpath_get_text(xml, XPATH_DOMAIN_NAME)[0]
                        raise InvalidOperation('KCHVM0034E',
                                               {'name': domain_name})

            elif orig_pool['type'] in ['scsi', 'iscsi']:
                # SCSI and iSCSI always fall back to the storage pool 'default'
                kimchi_log.warning('cannot create new volume for clone in '
                                   'storage pool \'%s\'; falling back to '
                                   '\'default\'', orig_pool_name)
                new_pool_name = u'default'
                new_pool = self.storagepool.lookup(u'default')

                # if the pool 'default' cannot hold a new volume, raise
                # an exception
                if orig_vol['capacity'] > new_pool['available']:
                    domain_name = xpath_get_text(xml, XPATH_DOMAIN_NAME)[0]
                    raise InvalidOperation('KCHVM0034E', {'name': domain_name})

            else:
                # unexpected storage pool type
                raise InvalidOperation('KCHPOOL0014E',
                                       {'type': orig_pool['type']})

            # new volume name: <UUID>-<loop-index>.<original extension>
            # e.g. 1234-5678-9012-3456-0.img
            ext = os.path.splitext(path)[1]
            new_vol_name = u'%s-%d%s' % (uuid, i, ext)
            task = self.storagevolume.clone(orig_pool_name, orig_vol_name,
                                            new_name=new_vol_name)
            self.task.wait(task['id'], 3600)  # 1 h

            # get the new volume path and update the XML descriptor
            new_vol = self.storagevolume.lookup(new_pool_name, new_vol_name)
            xml = xml_item_update(xml, XPATH_DOMAIN_DISK_BY_FILE % path,
                                  new_vol['path'], 'file')

            # remove the new volume should an error occur later
            rollback.prependDefer(self.storagevolume.delete, new_pool_name,
                                  new_vol_name)
示例#45
0
    def lookup(self, name):
        dom = self.get_vm(name, self.conn)
        info = dom.info()
        state = DOM_STATE_MAP[info[0]]
        screenshot = None
        # (type, listen, port, passwd, passwdValidTo)
        graphics = self._vm_get_graphics(name)
        graphics_port = graphics[2]
        graphics_port = graphics_port if state == 'running' else None
        try:
            if state == 'running' and self._has_video(dom):
                screenshot = self.vmscreenshot.lookup(name)
            elif state == 'shutoff':
                # reset vm stats when it is powered off to avoid sending
                # incorrect (old) data
                stats[dom.UUIDString()] = {}
        except NotFoundError:
            pass

        with self.objstore as session:
            try:
                extra_info = session.get('vm', dom.UUIDString())
            except NotFoundError:
                extra_info = {}
        icon = extra_info.get('icon')

        vm_stats = stats.get(dom.UUIDString(), {})
        res = {}
        res['cpu_utilization'] = vm_stats.get('cpu', 0)
        res['net_throughput'] = vm_stats.get('net_io', 0)
        res['net_throughput_peak'] = vm_stats.get('max_net_io', 100)
        res['io_throughput'] = vm_stats.get('disk_io', 0)
        res['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)
        users, groups = self._get_access_info(dom)

        if state == 'shutoff':
            xml = dom.XMLDesc(0)
            val = xpath_get_text(xml, XPATH_DOMAIN_MEMORY)[0]
            unit_list = xpath_get_text(xml, XPATH_DOMAIN_MEMORY_UNIT)
            if len(unit_list) > 0:
                unit = unit_list[0]
            else:
                unit = 'KiB'
            memory = convert_data_size(val, unit, 'MiB')
        else:
            memory = info[2] >> 10

        return {
            'name': name,
            'state': state,
            'stats': res,
            'uuid': dom.UUIDString(),
            'memory': memory,
            'cpus': info[3],
            'screenshot': screenshot,
            'icon': icon,
            # (type, listen, port, passwd, passwdValidTo)
            'graphics': {
                "type": graphics[0],
                "listen": graphics[1],
                "port": graphics_port,
                "passwd": graphics[3],
                "passwdValidTo": graphics[4]
            },
            'users': users,
            'groups': groups,
            'access': 'full',
            'persistent': True if dom.isPersistent() else False
        }
示例#46
0
 def _vm_get_networks(self, dom):
     xml = dom.XMLDesc(0)
     xpath = "/domain/devices/interface[@type='network']/source/@network"
     return xpath_get_text(xml, xpath)
示例#47
0
 def _get_storage_type(self):
     pool = self._storage_validate()
     xml = pool.XMLDesc(0)
     return xpath_get_text(xml, "/pool/@type")[0]
示例#48
0
 def _get_storage_path(self):
     pool = self._storage_validate()
     xml = pool.XMLDesc(0)
     return xpath_get_text(xml, "/pool/target/path")[0]
示例#49
0
 def _get_storage_path(self):
     pool = self._storage_validate()
     xml = pool.XMLDesc(0)
     return xpath_get_text(xml, "/pool/target/path")[0]
示例#50
0
 def _get_storage_type(self):
     pool = self._storage_validate()
     xml = pool.XMLDesc(0)
     return xpath_get_text(xml, "/pool/@type")[0]
示例#51
0
        else:
            for s in snapshot_names:
                self.vmsnapshot.delete(name, s)

        try:
            dom.undefine()
        except libvirt.libvirtError as e:
            raise OperationFailed("KCHVM0021E",
                                  {'name': name, 'err': e.get_error_message()})

        for path in paths:
            try:
                vol = conn.storageVolLookupByPath(path)
                pool = vol.storagePoolLookupByVolume()
                xml = pool.XMLDesc(0)
                pool_type = xpath_get_text(xml, "/pool/@type")[0]
                if pool_type not in READONLY_POOL_TYPE:
                    vol.delete(0)
                    # Update objstore to remove the volume
                    with self.objstore as session:
                        session.delete('storagevolume', path,
                                       ignore_missing=True)
            except libvirt.libvirtError as e:
                kimchi_log.error('Unable to get storage volume by path: %s' %
                                 e.message)
            except Exception as e:
                raise OperationFailed('KCHVOL0017E', {'err': e.message})

            try:
                with self.objstore as session:
                    if path in session.get_list('storagevolume'):
示例#52
0
文件: vms.py 项目: gouzongmei/kimchi
 def _vm_get_disk_paths(self, dom):
     xml = dom.XMLDesc(0)
     xpath = "/domain/devices/disk[@device='disk']/source/@file"
     return xpath_get_text(xml, xpath)
示例#53
0
class VMModel(object):
    def __init__(self, **kargs):
        self.conn = kargs['conn']
        self.objstore = kargs['objstore']
        self.caps = CapabilitiesModel(**kargs)
        self.vmscreenshot = VMScreenshotModel(**kargs)
        self.users = import_class('kimchi.model.users.UsersModel')(**kargs)
        self.groups = import_class('kimchi.model.groups.GroupsModel')(**kargs)
        self.vms = VMsModel(**kargs)
        self.task = TaskModel(**kargs)
        self.storagepool = model.storagepools.StoragePoolModel(**kargs)
        self.storagevolume = model.storagevolumes.StorageVolumeModel(**kargs)
        self.storagevolumes = model.storagevolumes.StorageVolumesModel(**kargs)
        cls = import_class('kimchi.model.vmsnapshots.VMSnapshotModel')
        self.vmsnapshot = cls(**kargs)
        cls = import_class('kimchi.model.vmsnapshots.VMSnapshotsModel')
        self.vmsnapshots = cls(**kargs)

    def update(self, name, params):
        dom = self.get_vm(name, self.conn)
        dom = self._static_vm_update(dom, params)
        self._live_vm_update(dom, params)
        return dom.name().decode('utf-8')

    def clone(self, name):
        """Clone a virtual machine based on an existing one.

        The new virtual machine will have the exact same configuration as the
        original VM, except for the name, UUID, MAC addresses and disks. The
        name will have the form "<name>-clone-<number>", with <number> starting
        at 1; the UUID will be generated randomly; the MAC addresses will be
        generated randomly with no conflicts within the original and the new
        VM; and the disks will be new volumes [mostly] on the same storage
        pool, with the same content as the original disks. The storage pool
        'default' will always be used when cloning SCSI and iSCSI disks and
        when the original storage pool cannot hold the new volume.

        An exception will be raised if the virtual machine <name> is not
        shutoff, if there is no available space to copy a new volume to the
        storage pool 'default' (when there was also no space to copy it to the
        original storage pool) and if one of the virtual machine's disks belong
        to a storage pool not supported by Kimchi.

        Parameters:
        name -- The name of the existing virtual machine to be cloned.

        Return:
        A Task running the clone operation.
        """
        name = name.decode('utf-8')

        # VM must be shutoff in order to clone it
        info = self.lookup(name)
        if info['state'] != u'shutoff':
            raise InvalidParameter('KCHVM0033E', {'name': name})

        # the new VM's name will be used as the Task's 'target_uri' so it needs
        # to be defined now.

        vms_being_created = []

        # lookup names of VMs being created right now
        with self.objstore as session:
            task_names = session.get_list('task')
            for tn in task_names:
                t = session.get('task', tn)
                if t['target_uri'].startswith('/vms/'):
                    uri_name = t['target_uri'][5:]  # 5 = len('/vms/')
                    vms_being_created.append(uri_name)

        current_vm_names = self.vms.get_list() + vms_being_created
        new_name = get_next_clone_name(current_vm_names, name)

        # create a task with the actual clone function
        taskid = add_task(u'/vms/%s' % new_name, self._clone_task,
                          self.objstore,
                          {'name': name, 'new_name': new_name})

        return self.task.lookup(taskid)

    def _clone_task(self, cb, params):
        """Asynchronous function which performs the clone operation.

        Parameters:
        cb -- A callback function to signal the Task's progress.
        params -- A dict with the following values:
            "name": the name of the original VM.
            "new_name": the name of the new VM.
        """
        name = params['name']
        new_name = params['new_name']
        vir_conn = self.conn.get()

        # fetch base XML
        cb('reading source VM XML')
        try:
            vir_dom = vir_conn.lookupByName(name)
            flags = libvirt.VIR_DOMAIN_XML_SECURE
            xml = vir_dom.XMLDesc(flags).decode('utf-8')
        except libvirt.libvirtError, e:
            raise OperationFailed('KCHVM0035E', {'name': name,
                                                 'err': e.message})

        # update UUID
        cb('updating VM UUID')
        old_uuid = xpath_get_text(xml, XPATH_DOMAIN_UUID)[0]
        new_uuid = unicode(uuid.uuid4())
        xml = xml_item_update(xml, './uuid', new_uuid)

        # update MAC addresses
        cb('updating VM MAC addresses')
        xml = self._clone_update_mac_addresses(xml)

        with RollbackContext() as rollback:
            # copy disks
            cb('copying VM disks')
            xml = self._clone_update_disks(xml, rollback)

            # update objstore entry
            cb('updating object store')
            self._clone_update_objstore(old_uuid, new_uuid, rollback)

            # update name
            cb('updating VM name')
            xml = xml_item_update(xml, './name', new_name)

            # create new guest
            cb('defining new VM')
            try:
                vir_conn.defineXML(xml)
            except libvirt.libvirtError, e:
                raise OperationFailed('KCHVM0035E', {'name': name,
                                                     'err': e.message})

            rollback.commitAll()
示例#54
0
文件: vms.py 项目: gouzongmei/kimchi
    def _clone_update_disks(self, xml, rollback):
        """Clone disks from a virtual machine. The disks are copied as new
        volumes and the new VM's XML is updated accordingly.

        Arguments:
        xml -- The XML descriptor of the original VM + new value for
            "/domain/uuid".
        rollback -- A rollback context so the new volumes can be removed if an
            error occurs during the cloning operation.

        Return:
        The XML descriptor <xml> with the new disk paths instead of the
        old ones.
        """
        # the UUID will be used to create the disk paths
        uuid = xpath_get_text(xml, XPATH_DOMAIN_UUID)[0]
        all_paths = xpath_get_text(xml, XPATH_DOMAIN_DISK)

        vir_conn = self.conn.get()

        for i, path in enumerate(all_paths):
            try:
                vir_orig_vol = vir_conn.storageVolLookupByPath(path)
                vir_pool = vir_orig_vol.storagePoolLookupByVolume()

                orig_pool_name = vir_pool.name().decode('utf-8')
                orig_vol_name = vir_orig_vol.name().decode('utf-8')
            except libvirt.libvirtError, e:
                domain_name = xpath_get_text(xml, XPATH_DOMAIN_NAME)[0]
                raise OperationFailed('KCHVM0035E', {'name': domain_name,
                                                     'err': e.message})

            orig_pool = self.storagepool.lookup(orig_pool_name)
            orig_vol = self.storagevolume.lookup(orig_pool_name, orig_vol_name)

            new_pool_name = orig_pool_name
            new_pool = orig_pool

            if orig_pool['type'] in ['dir', 'netfs', 'logical']:
                # if a volume in a pool 'dir', 'netfs' or 'logical' cannot hold
                # a new volume with the same size, the pool 'default' should
                # be used
                if orig_vol['capacity'] > orig_pool['available']:
                    kimchi_log.warning('storage pool \'%s\' doesn\'t have '
                                       'enough free space to store image '
                                       '\'%s\'; falling back to \'default\'',
                                       orig_pool_name, path)
                    new_pool_name = u'default'
                    new_pool = self.storagepool.lookup(u'default')

                    # ...and if even the pool 'default' cannot hold a new
                    # volume, raise an exception
                    if orig_vol['capacity'] > new_pool['available']:
                        domain_name = xpath_get_text(xml, XPATH_DOMAIN_NAME)[0]
                        raise InvalidOperation('KCHVM0034E',
                                               {'name': domain_name})

            elif orig_pool['type'] in ['scsi', 'iscsi']:
                # SCSI and iSCSI always fall back to the storage pool 'default'
                kimchi_log.warning('cannot create new volume for clone in '
                                   'storage pool \'%s\'; falling back to '
                                   '\'default\'', orig_pool_name)
                new_pool_name = u'default'
                new_pool = self.storagepool.lookup(u'default')

                # if the pool 'default' cannot hold a new volume, raise
                # an exception
                if orig_vol['capacity'] > new_pool['available']:
                    domain_name = xpath_get_text(xml, XPATH_DOMAIN_NAME)[0]
                    raise InvalidOperation('KCHVM0034E', {'name': domain_name})

            else:
                # unexpected storage pool type
                raise InvalidOperation('KCHPOOL0014E',
                                       {'type': orig_pool['type']})

            # new volume name: <UUID>-<loop-index>.<original extension>
            # e.g. 1234-5678-9012-3456-0.img
            ext = os.path.splitext(path)[1]
            new_vol_name = u'%s-%d%s' % (uuid, i, ext)
            task = self.storagevolume.clone(orig_pool_name, orig_vol_name,
                                            new_name=new_vol_name)
            self.task.wait(task['id'], 3600)  # 1 h

            # get the new volume path and update the XML descriptor
            new_vol = self.storagevolume.lookup(new_pool_name, new_vol_name)
            xml = xml_item_update(xml, XPATH_DOMAIN_DISK_BY_FILE % path,
                                  new_vol['path'], 'file')

            # remove the new volume should an error occur later
            rollback.prependDefer(self.storagevolume.delete, new_pool_name,
                                  new_vol_name)
示例#55
0
 def _vm_get_disk_paths(self, dom):
     xml = dom.XMLDesc(0)
     xpath = "/domain/devices/disk[@device='disk']/source/@file"
     return xpath_get_text(xml, xpath)
示例#56
0
文件: vms.py 项目: gouzongmei/kimchi
        else:
            for s in snapshot_names:
                self.vmsnapshot.delete(name, s)

        try:
            dom.undefine()
        except libvirt.libvirtError as e:
            raise OperationFailed("KCHVM0021E",
                                  {'name': name, 'err': e.get_error_message()})

        for path in paths:
            try:
                vol = conn.storageVolLookupByPath(path)
                pool = vol.storagePoolLookupByVolume()
                xml = pool.XMLDesc(0)
                pool_type = xpath_get_text(xml, "/pool/@type")[0]
                if pool_type not in READONLY_POOL_TYPE:
                    vol.delete(0)
                    # Update objstore to remove the volume
                    with self.objstore as session:
                        session.delete('storagevolume', path,
                                       ignore_missing=True)
            except libvirt.libvirtError as e:
                kimchi_log.error('Unable to get storage volume by path: %s' %
                                 e.message)
            except Exception as e:
                raise OperationFailed('KCHVOL0017E', {'err': e.message})

            try:
                with self.objstore as session:
                    if path in session.get_list('storagevolume'):