File: volumeupload.py

package info (click to toggle)
virt-manager 1%3A5.0.0-5
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 20,200 kB
  • sloc: python: 44,538; xml: 28,397; makefile: 17; sh: 6
file content (151 lines) | stat: -rw-r--r-- 4,178 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#
# Copyright 2006-2009, 2013, 2014 Red Hat, Inc.
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.

import os

from .. import progress
from ..devices import DeviceDisk
from ..logger import log
from ..storage import StoragePool, StorageVolume


def _build_pool(conn, meter, path):
    """
    Helper function for building a pool on demand. Used for building
    a scratchdir pool for volume upload
    """
    pool = StoragePool.lookup_pool_by_path(conn, path)
    if pool:  # pragma: no cover
        log.debug("Existing pool '%s' found for %s", pool.name(), path)
        StoragePool.ensure_pool_is_running(pool, refresh=True)
        return pool

    name = StoragePool.find_free_name(conn, "boot-scratch")
    log.debug("Building storage pool: path=%s name=%s", path, name)
    poolbuild = StoragePool(conn)
    poolbuild.type = poolbuild.TYPE_DIR
    poolbuild.name = name
    poolbuild.target_path = path

    # Explicitly don't build? since if we are creating this directory
    # we probably don't have correct perms
    ret = poolbuild.install(meter=meter, create=True, build=False,
                            autostart=True)
    return ret


class _MockStream:
    _data_size = None

    def send(self, data):
        if self._data_size is None:
            self._data_size = len(data)

        block_size = 128
        ret = min(self._data_size, block_size)
        self._data_size = max(0, self._data_size - block_size)
        return ret

    def finish(self):
        pass


def _upload_file(conn, meter, destpool, src):
    """
    Helper for uploading a file to a pool, via libvirt. Used for
    kernel/initrd upload when we can't access the system scratchdir
    """
    # Build stream object
    if conn.in_testsuite():
        stream = _MockStream()
    else:
        stream = conn.newStream(0)  # pragma: no cover

    def safe_send(data):
        while True:
            ret = stream.send(data)
            if ret == 0 or ret == len(data):
                break
            data = data[ret:]

    meter = progress.ensure_meter(meter)

    # Build placeholder volume
    size = os.path.getsize(src)
    basename = os.path.basename(src)
    name = StorageVolume.find_free_name(conn, destpool, basename)
    log.debug("Generated volume name %s", name)

    vol_install = DeviceDisk.build_vol_install(conn, name, destpool,
                    (float(size) / 1024.0 / 1024.0 / 1024.0), True)

    disk = DeviceDisk(conn)
    disk.set_vol_install(vol_install)
    disk.validate()

    disk.build_storage(meter)
    vol = disk.get_vol_object()
    if not vol:
        raise RuntimeError(  # pragma: no cover
                "Failed to lookup scratch media volume")

    try:
        # Register upload
        offset = 0
        length = size
        flags = 0
        if not conn.in_testsuite():
            vol.upload(stream, offset, length, flags)  # pragma: no cover

        # Open source file
        fileobj = open(src, "rb")

        # Start transfer
        total = 0
        msg = _("Transferring '%(filename)s'") % {
                "filename": os.path.basename(src)}
        meter.start(msg, size)
        while True:
            blocksize = 1024 * 1024  # 1 MiB
            data = fileobj.read(blocksize)
            if not data:
                break

            safe_send(data)
            total += len(data)
            meter.update(total)

        # Cleanup
        stream.finish()
        meter.end()
    except Exception:  # pragma: no cover
        vol.delete(0)
        raise

    return vol


def upload_paths(conn, system_scratchdir, meter, pathlist):
    """
    Upload passed paths to the connection scratchdir
    """
    # Build pool
    log.debug("Uploading kernel/initrd media")
    pool = _build_pool(conn, meter, system_scratchdir)

    tmpvols = []
    newpaths = []
    try:
        for path in pathlist:
            vol = _upload_file(conn, meter, pool, path)
            newpaths.append(vol.path())
            tmpvols.append(vol)
    except Exception:  # pragma: no cover
        for vol in tmpvols:
            vol.delete(0)
        raise

    return newpaths, tmpvols