~louis/ubuntu/vivid/uvtool/uvtool-lp1317266-doc-backing-img-file

« back to all changes in this revision

Viewing changes to uvtool/libvirt/__init__.py

  • Committer: Package Import Robot
  • Author(s): Robie Basak
  • Date: 2013-09-20 17:33:54 UTC
  • Revision ID: package-import@ubuntu.com-20130920173354-1eeqk3ary5bdafst
Tags: upstream-0~bzr35
ImportĀ upstreamĀ versionĀ 0~bzr35

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2013 Canonical Ltd.
 
2
# Author: Robie Basak <robie.basak@canonical.com>
 
3
#
 
4
# This program is free software: you can redistribute it and/or modify
 
5
# it under the terms of the GNU Affero General Public License as published by
 
6
# the Free Software Foundation, either version 3 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU Affero General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU Affero General Public License
 
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
 
17
from __future__ import absolute_import
 
18
from __future__ import print_function
 
19
from __future__ import unicode_literals
 
20
 
 
21
import contextlib
 
22
import itertools
 
23
import os
 
24
import shutil
 
25
import subprocess
 
26
import tempfile
 
27
 
 
28
import libvirt
 
29
from lxml import etree
 
30
from lxml.builder import E
 
31
 
 
32
 
 
33
def get_libvirt_pool_object(libvirt_conn, pool_name):
 
34
    try:
 
35
        pool = libvirt_conn.storagePoolLookupByName(pool_name)
 
36
    except libvirt.libvirtError:
 
37
        raise RuntimeError("Cannot find pool %s." % repr(pool_name))
 
38
    return pool
 
39
 
 
40
 
 
41
def create_volume_from_fobj(new_volume_name, fobj, image_type='raw',
 
42
        pool_name='default'):
 
43
    """Create a new libvirt volume and populate it from a file-like object."""
 
44
 
 
45
    compressed_fobj = tempfile.NamedTemporaryFile()
 
46
    decompressed_fobj = tempfile.NamedTemporaryFile()
 
47
    with contextlib.closing(compressed_fobj):
 
48
        with contextlib.closing(decompressed_fobj):
 
49
            shutil.copyfileobj(fobj, compressed_fobj)
 
50
            compressed_fobj.flush()
 
51
            compressed_fobj.seek(0)  # is this necessary?
 
52
            subprocess.check_call(
 
53
                [
 
54
                    'qemu-img', 'convert', '-f', image_type, '-O', image_type,
 
55
                    compressed_fobj.name, decompressed_fobj.name
 
56
                ],
 
57
                shell=False, close_fds=False)
 
58
            decompressed_fobj.seek(0)  # is this necessary?
 
59
            return _create_volume_from_fobj_with_size(
 
60
                new_volume_name=new_volume_name,
 
61
                fobj=decompressed_fobj,
 
62
                fobj_size=os.fstat(decompressed_fobj.fileno()).st_size,
 
63
                image_type=image_type,
 
64
                pool_name=pool_name
 
65
            )
 
66
 
 
67
 
 
68
def _create_volume_from_fobj_with_size(new_volume_name, fobj, fobj_size,
 
69
        image_type, pool_name):
 
70
    conn = libvirt.open('qemu:///system')
 
71
    pool = get_libvirt_pool_object(conn, pool_name)
 
72
 
 
73
    if image_type == 'raw':
 
74
        extra = [E.allocation(str(fobj_size)), E.capacity(str(fobj_size))]
 
75
    elif image_type == 'qcow2':
 
76
        extra = [E.capacity('0')]
 
77
    else:
 
78
        raise NotImplementedError("Unknown image type %r." % image_type)
 
79
 
 
80
    new_vol = E.volume(
 
81
        E.name(new_volume_name),
 
82
        E.target(E.format(type=image_type)),
 
83
        *extra
 
84
        )
 
85
    vol = pool.createXML(etree.tostring(new_vol), 0)
 
86
 
 
87
    stream = conn.newStream(0)
 
88
    vol.upload(stream, 0, fobj_size, 0)
 
89
 
 
90
    def handler(stream_ignored, size, opaque_ignored):
 
91
        return fobj.read(size)
 
92
 
 
93
    stream.sendAll(handler, None)
 
94
    stream.finish()
 
95
 
 
96
    return vol
 
97
 
 
98
 
 
99
def volume_names_in_pool(pool_name='default'):
 
100
    conn = libvirt.open('qemu:///system')
 
101
    pool = get_libvirt_pool_object(conn, pool_name)
 
102
    return pool.listVolumes()
 
103
 
 
104
 
 
105
def delete_volume_by_name(volume_name, pool_name='default'):
 
106
    conn = libvirt.open('qemu:///system')
 
107
    pool = get_libvirt_pool_object(conn, pool_name)
 
108
    volume = pool.storageVolLookupByName(volume_name)
 
109
    volume.delete(flags=0)
 
110
 
 
111
 
 
112
def have_volume_by_name(volume_name, pool_name='default'):
 
113
    conn = libvirt.open('qemu:///system')
 
114
    pool = get_libvirt_pool_object(conn, pool_name)
 
115
    try:
 
116
        volume = pool.storageVolLookupByName(volume_name)
 
117
    except libvirt.libvirtError:
 
118
        return False
 
119
    else:
 
120
        return True
 
121
 
 
122
 
 
123
def _get_all_domains(conn=None):
 
124
    if conn is None:
 
125
        conn = libvirt.open('qemu:///system')
 
126
 
 
127
    # libvirt in Precise doesn't seem to have a binding for
 
128
    # virConnectListAllDomains, and it seems that we must enumerate
 
129
    # defined-by-not-running and running instances separately and in different
 
130
    # ways.
 
131
 
 
132
    for domain_id in conn.listDomainsID():
 
133
        yield conn.lookupByID(domain_id)
 
134
 
 
135
    for domain_name in conn.listDefinedDomains():
 
136
        yield conn.lookupByName(domain_name)
 
137
 
 
138
 
 
139
def _domain_element_to_volume_paths(element):
 
140
    assert element.tag == 'domain'
 
141
    return (
 
142
        source.get('file')
 
143
        for source in element.xpath(
 
144
            "/domain/devices/disk[@type='file']/source[@file]"
 
145
        )
 
146
    )
 
147
 
 
148
 
 
149
def _domain_volume_paths(domain):
 
150
    volume_paths = set()
 
151
 
 
152
    for flags in [0, libvirt.VIR_DOMAIN_XML_INACTIVE]:
 
153
        element = etree.fromstring(domain.XMLDesc(flags))
 
154
        volume_paths.update(_domain_element_to_volume_paths(element))
 
155
 
 
156
    return frozenset(volume_paths)
 
157
 
 
158
 
 
159
def _volume_element_to_volume_paths(element):
 
160
    assert element.tag == 'volume'
 
161
    return itertools.chain(
 
162
        (path.text for path in element.xpath('/volume/target/path')),
 
163
        (path.text for path in element.xpath('/volume/backingStore/path')),
 
164
    )
 
165
 
 
166
 
 
167
def _volume_volume_paths(volume):
 
168
    # Volumes can depend on other volumes ("backing stores"), so return all
 
169
    # paths a volume needs to function, including the top level one.
 
170
    volume_paths = set()
 
171
 
 
172
    element = etree.fromstring(volume.XMLDesc(0))
 
173
    volume_paths.update(_volume_element_to_volume_paths(element))
 
174
 
 
175
    return frozenset(volume_paths)
 
176
 
 
177
 
 
178
def _get_all_domain_volume_paths(conn=None):
 
179
    if conn is None:
 
180
        conn = libvirt.open('qemu:///system')
 
181
 
 
182
    all_volume_paths = set()
 
183
    for domain in _get_all_domains(conn):
 
184
        for path in _domain_volume_paths(domain):
 
185
            try:
 
186
                volume = conn.storageVolLookupByKey(path)
 
187
            except libvirt.libvirtError:
 
188
                # ignore a lookup failure, since if a volume doesn't exist,
 
189
                # it isn't reasonable to consider what backing volumes it may
 
190
                # have
 
191
                continue
 
192
            all_volume_paths.update(_volume_volume_paths(volume))
 
193
 
 
194
    return frozenset(all_volume_paths)
 
195
 
 
196
 
 
197
def get_all_domain_volume_names(conn=None, filter_by_dir=None):
 
198
    # Limitation: filter_by_dir must currently end in a '/' and be the
 
199
    # canonical path as libvirt returns it. Ideally I'd filter by pool instead,
 
200
    # but the libvirt API appears to not provide any method to find what pool a
 
201
    # volume is in when looked up by key.
 
202
    if conn is None:
 
203
        conn = libvirt.open('qemu:///system')
 
204
 
 
205
    for path in _get_all_domain_volume_paths(conn=conn):
 
206
        volume = conn.storageVolLookupByKey(path)
 
207
        if filter_by_dir and not volume.path().startswith(filter_by_dir):
 
208
            continue
 
209
        yield volume.name()