Source code for lago.guestfs_tools
#
# Copyright 2017 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Refer to the README and COPYING files for full details of the license
#
import os
import guestfs
import logging
import time
from lago.plugins.vm import ExtractPathError, ExtractPathNoPathError
LOGGER = logging.getLogger(__name__)
[docs]def _copy_path(guestfs_conn, guest_path, host_path):
if guestfs_conn.is_file(guest_path):
with open(host_path, 'w') as dest_fd:
dest_fd.write(guestfs_conn.read_file(guest_path))
elif guestfs_conn.is_dir(guest_path):
os.mkdir(host_path)
for path in guestfs_conn.ls(guest_path):
_copy_path(
guestfs_conn,
os.path.join(
guest_path,
path,
),
os.path.join(host_path, os.path.basename(path)),
)
else:
raise ExtractPathNoPathError(
('unable to extract {0}: path does not '
'exist.').format(guest_path)
)
[docs]def extract_paths(disk_path, disk_root, paths, ignore_nopath):
"""
Extract paths from a disk using guestfs
Args:
disk_path(str): path to the disk
disk_root(str): root partition
paths(list of tuples): files to extract in
`[(src1, dst1), (src2, dst2)...]` format.
ignore_nopath(bool): If set to True, ignore source paths that do
not exist
Returns:
None
Raises:
:exc:`~lago.plugins.vm.ExtractPathNoPathError`: if a none existing
path was found on the VM, and `ignore_nopath` is False.
:exc:`~lago.plugins.vm.ExtractPathError`: on all other failures.
"""
gfs_cli = guestfs.GuestFS(python_return_dict=True)
disk_path = os.path.expandvars(disk_path)
try:
gfs_cli.add_drive_ro(disk_path)
gfs_cli.set_backend(os.environ.get('LIBGUESTFS_BACKEND', 'direct'))
gfs_cli.launch()
rootfs = [
filesystem for filesystem in gfs_cli.list_filesystems()
if disk_root in filesystem
]
if not rootfs:
raise ExtractPathError(
'No root fs (%s) could be found for %s from list %s' %
(disk_root, disk_path, str(gfs_cli.list_filesystems()))
)
else:
rootfs = rootfs[0]
max_attempts = 5
for attempt in range(max_attempts):
try:
gfs_cli.mount_ro(rootfs, '/')
break
except RuntimeError as err:
if attempt <= max_attempts:
LOGGER.debug(err)
LOGGER.debug(
(
'failed mounting %s:%s using guestfs, '
'attempt %s/%s'
), disk_path, rootfs, attempt + 1, max_attempts
)
time.sleep(1)
else:
LOGGER.debug(
(
'failed mounting %s:%s using guestfs', disk_path,
rootfs
)
)
raise
for (guest_path, host_path) in paths:
msg = ('Extracting guestfs://{0} to {1}').format(
guest_path, host_path
)
LOGGER.debug(msg)
try:
_copy_path(gfs_cli, guest_path, host_path)
except ExtractPathNoPathError as err:
if ignore_nopath:
LOGGER.debug('%s: ignoring', err)
else:
raise
finally:
gfs_cli.shutdown()
gfs_cli.close()