summaryrefslogtreecommitdiff
path: root/virtinst/kernelupload.py
blob: e4e2a83a923f2dfd6e7eeb1ced48a12d01a972a0 (plain)
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
152
153
154
155
156
157
158
159
160
161
162
#
# 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 .logger import log
from .devices import DeviceDisk
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
        meter.start(size=size,
                    text=_("Transferring %s") % os.path.basename(src))
        while True:
            # blocksize = (1024 ** 2)
            blocksize = 1024
            data = fileobj.read(blocksize)
            if not data:
                break

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

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

    return vol


def upload_kernel_initrd(conn, scratchdir, system_scratchdir,
                         meter, kernel, initrd):
    """
    Upload kernel/initrd media to remote connection if necessary
    """
    tmpvols = []

    if (not conn.is_remote() and
        (conn.is_session_uri() or scratchdir == system_scratchdir)):
        # We have access to system scratchdir, don't jump through hoops
        log.debug("Have access to preferred scratchdir so"
                    " nothing to upload")  # pragma: no cover
        return kernel, initrd, tmpvols  # pragma: no cover

    if not conn.support_remote_url_install():
        # Needed for the test_urls suite
        log.debug("Media upload not supported")  # pragma: no cover
        return kernel, initrd, tmpvols  # pragma: no cover

    # Build pool
    log.debug("Uploading kernel/initrd media")
    pool = _build_pool(conn, meter, system_scratchdir)

    kvol = _upload_file(conn, meter, pool, kernel)
    newkernel = kvol.path()
    tmpvols.append(kvol)

    ivol = _upload_file(conn, meter, pool, initrd)
    newinitrd = ivol.path()
    tmpvols.append(ivol)

    return newkernel, newinitrd, tmpvols