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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
#
# 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 urldetect
from . import urlfetcher
from .installerinject import perform_initrd_injections
from .. import progress
from ..devices import DeviceDisk
from ..logger import log
from ..osdict import OSDB
# Enum of the various install media types we can have
(MEDIA_DIR,
MEDIA_ISO,
MEDIA_URL,
MEDIA_KERNEL) = range(1, 5)
def _is_url(url):
return (url.startswith("http://") or
url.startswith("https://") or
url.startswith("ftp://"))
class _LocationData(object):
def __init__(self, os_variant, kernel_pairs, os_media, os_tree):
self.os_variant = os_variant
self.kernel_pairs = kernel_pairs
self.os_media = os_media
self.os_tree = os_tree
self.kernel_url_arg = None
if self.os_variant:
osobj = OSDB.lookup_os(self.os_variant)
self.kernel_url_arg = osobj.get_kernel_url_arg()
class InstallerTreeMedia(object):
"""
Class representing --location Tree media. Can be one of
- A network URL: http://dl.fedoraproject.org/...
- A local directory
- A local .iso file, which will be accessed with isoinfo
"""
@staticmethod
def validate_path(conn, path):
try:
dev = DeviceDisk(conn)
dev.device = dev.DEVICE_CDROM
dev.set_source_path(path)
dev.validate()
return dev.get_source_path()
except Exception as e:
log.debug("Error validating install location", exc_info=True)
if path.startswith("nfs:"):
log.warning("NFS URL installs are no longer supported. "
"Access your install media over an alternate transport "
"like HTTP, or manually mount the NFS share and install "
"from the local directory mount point.")
msg = (_("Validating install media '%(media)s' failed: %(error)s") %
{"media": str(path), "error": str(e)})
raise ValueError(msg) from None
@staticmethod
def get_system_scratchdir(guest):
"""
Return the tmpdir that's accessible by VMs on system libvirt URIs
"""
if guest.conn.is_xen():
return "/var/lib/xen"
return "/var/lib/libvirt/boot"
@staticmethod
def make_scratchdir(guest):
"""
Determine the scratchdir for this URI, create it if necessary.
scratchdir is the directory that's accessible by VMs
"""
user_scratchdir = os.path.join(
guest.conn.get_app_cache_dir(), "boot")
system_scratchdir = InstallerTreeMedia.get_system_scratchdir(guest)
# If we are a session URI, or we don't have access to the system
# scratchdir, make sure the session scratchdir exists and use that.
if (guest.conn.is_unprivileged() or
not os.path.exists(system_scratchdir) or
not os.access(system_scratchdir, os.W_OK)):
os.makedirs(user_scratchdir, 0o751, exist_ok=True)
return user_scratchdir
return system_scratchdir # pragma: no cover
def __init__(self, conn, location, location_kernel, location_initrd,
install_kernel, install_initrd, install_kernel_args):
self.conn = conn
self.location = location
self._location_kernel = location_kernel
self._location_initrd = location_initrd
self._install_kernel = install_kernel
self._install_initrd = install_initrd
self._install_kernel_args = install_kernel_args
self._initrd_injections = []
self._extra_args = []
if location_kernel or location_initrd:
if not location:
raise ValueError(_("location kernel/initrd may only "
"be specified with a location URL/path"))
if not (location_kernel and location_initrd):
raise ValueError(_("location kernel/initrd must be "
"be specified as a pair"))
self._cached_fetcher = None
self._cached_data = None
self._tmpfiles = []
if self._install_kernel or self._install_initrd:
self._media_type = MEDIA_KERNEL
elif (not self.conn.is_remote() and
os.path.exists(self.location) and
os.path.isdir(self.location)):
self.location = os.path.abspath(self.location)
self._media_type = MEDIA_DIR
elif _is_url(self.location):
self._media_type = MEDIA_URL
else:
self._media_type = MEDIA_ISO
if (self.conn.is_remote() and
not self._media_type == MEDIA_URL and
not self._media_type == MEDIA_KERNEL):
raise ValueError(_("Cannot access install tree on remote "
"connection: %s") % self.location)
if self._media_type == MEDIA_ISO:
InstallerTreeMedia.validate_path(self.conn, self.location)
########################
# Install preparations #
########################
def _get_fetcher(self, guest, meter):
meter = progress.ensure_meter(meter)
if not self._cached_fetcher:
scratchdir = InstallerTreeMedia.make_scratchdir(guest)
if self._media_type == MEDIA_KERNEL:
self._cached_fetcher = urlfetcher.DirectFetcher(
None, scratchdir, meter)
else:
self._cached_fetcher = urlfetcher.fetcherForURI(
self.location, scratchdir, meter)
self._cached_fetcher.meter = meter
return self._cached_fetcher
def _get_cached_data(self, guest, fetcher):
if self._cached_data:
return self._cached_data
store = None
os_variant = None
os_media = None
os_tree = None
kernel_paths = []
has_location_kernel = bool(
self._location_kernel and self._location_initrd)
if self._media_type == MEDIA_KERNEL:
kernel_paths = [
(self._install_kernel, self._install_initrd)]
else:
store = urldetect.getDistroStore(guest, fetcher,
skip_error=has_location_kernel)
if store:
kernel_paths = store.get_kernel_paths()
os_variant = store.get_osdict_info()
os_media = store.get_os_media()
os_tree = store.get_os_tree()
if has_location_kernel:
kernel_paths = [
(self._location_kernel, self._location_initrd)]
self._cached_data = _LocationData(os_variant, kernel_paths,
os_media, os_tree)
return self._cached_data
def _prepare_kernel_url(self, guest, cache, fetcher):
ignore = guest
def _check_kernel_pairs():
for kpath, ipath in cache.kernel_pairs:
if fetcher.hasFile(kpath) and fetcher.hasFile(ipath):
return kpath, ipath
raise RuntimeError( # pragma: no cover
_("Couldn't find kernel for install tree."))
kernelpath, initrdpath = _check_kernel_pairs()
kernel = fetcher.acquireFile(kernelpath)
self._tmpfiles.append(kernel)
initrd = fetcher.acquireFile(initrdpath)
self._tmpfiles.append(initrd)
perform_initrd_injections(initrd,
self._initrd_injections,
fetcher.scratchdir)
return kernel, initrd
##############
# Public API #
##############
def _prepare_unattended_data(self, scripts):
if not scripts:
return
for script in scripts:
expected_filename = script.get_expected_filename()
scriptpath = script.write()
self._tmpfiles.append(scriptpath)
self._initrd_injections.append((scriptpath, expected_filename))
def _prepare_kernel_url_arg(self, guest, cache):
os_variant = cache.os_variant or guest.osinfo.name
osobj = OSDB.lookup_os(os_variant)
return osobj.get_kernel_url_arg()
def _prepare_kernel_args(self, guest, cache, unattended_scripts):
install_args = None
if unattended_scripts:
args = []
for unattended_script in unattended_scripts:
cmdline = unattended_script.generate_cmdline()
if cmdline:
args.append(cmdline)
install_args = (" ").join(args)
log.debug("Generated unattended cmdline: %s", install_args)
elif self.is_network_url():
kernel_url_arg = self._prepare_kernel_url_arg(guest, cache)
if kernel_url_arg:
install_args = "%s=%s" % (kernel_url_arg, self.location)
if install_args:
self._extra_args.append(install_args)
if self._install_kernel_args:
ret = self._install_kernel_args
else:
ret = " ".join(self._extra_args)
if self._media_type == MEDIA_DIR and not ret:
log.warning(_("Directory tree installs typically do not work "
"unless extra kernel args are passed to point the "
"installer at a network accessible install tree."))
return ret
def prepare(self, guest, meter, unattended_scripts):
fetcher = self._get_fetcher(guest, meter)
cache = self._get_cached_data(guest, fetcher)
self._prepare_unattended_data(unattended_scripts)
kernel_args = self._prepare_kernel_args(guest, cache, unattended_scripts)
kernel, initrd = self._prepare_kernel_url(guest, cache, fetcher)
return kernel, initrd, kernel_args
def cleanup(self, guest):
ignore = guest
for f in self._tmpfiles:
log.debug("Removing %s", str(f))
os.unlink(f)
self._tmpfiles = []
def set_initrd_injections(self, initrd_injections):
self._initrd_injections = initrd_injections
def set_extra_args(self, extra_args):
self._extra_args = extra_args
def cdrom_path(self):
if self._media_type in [MEDIA_ISO]:
return self.location
def is_network_url(self):
if self._media_type in [MEDIA_URL]:
return self.location
def detect_distro(self, guest):
fetcher = self._get_fetcher(guest, None)
cache = self._get_cached_data(guest, fetcher)
return cache.os_variant
def get_os_media(self, guest, meter):
fetcher = self._get_fetcher(guest, meter)
cache = self._get_cached_data(guest, fetcher)
return cache.os_media
def get_os_tree(self, guest, meter):
fetcher = self._get_fetcher(guest, meter)
cache = self._get_cached_data(guest, fetcher)
return cache.os_tree
def requires_internet(self, guest, meter):
if self._media_type in [MEDIA_URL, MEDIA_DIR]:
return True
os_media = self.get_os_media(guest, meter)
if os_media:
return os_media.is_netinst()
return False
|