diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/virtBootstrap/progress.py | 67 | ||||
-rw-r--r-- | src/virtBootstrap/sources.py | 52 | ||||
-rwxr-xr-x | src/virtBootstrap/virt_bootstrap.py | 10 |
3 files changed, 108 insertions, 21 deletions
diff --git a/src/virtBootstrap/progress.py b/src/virtBootstrap/progress.py new file mode 100644 index 0000000..3463168 --- /dev/null +++ b/src/virtBootstrap/progress.py @@ -0,0 +1,67 @@ +# Authors: +# Cedric Bosdonnat <cbosdonnat@suse.com> +# Radostin Stoyanov <rstoyanov1@gmail.com> +# +# Copyright (c) 2017 Radostin Stoyanov +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +""" +Store the progress of virt-bootstrap +""" + + +class Progress(object): + """ + Store progress status and value of virt-bootstrap and + inform callback method about change. + """ + + def __init__(self, callback=None): + """ + If callback method is passed it will be called when the progress + value has been changed. + """ + self.progress = {'status': '', 'value': 0} + self.callback = callback + + def get_progress(self): + """ + Return "state" and "value" of the progress in python dictionary. + """ + return self.progress.copy() + + def update_progress(self, status=None, value=None, logger=None): + """ + Set status/value and call the callback method if available. + Log information message if logger instance was passed. + + @param status: String representing the current state of virt-bootstrap. + @param value: The new progress value of virt-bootstrap. + @param logger: Reference to logger. If passed info message with + including the status will be logged. + """ + # Note: We do not validate the values stored in progress + if isinstance(status, str): + self.progress['status'] = status + if isinstance(value, (float, int)): + self.progress['value'] = value + + try: + if logger is not None: + logger.info(status) + if self.callback is not None: + self.callback(self.get_progress()) + except Exception as err: + raise ValueError("Progress update has failed.", err) diff --git a/src/virtBootstrap/sources.py b/src/virtBootstrap/sources.py index b503846..04266e4 100644 --- a/src/virtBootstrap/sources.py +++ b/src/virtBootstrap/sources.py @@ -122,28 +122,31 @@ def format_number(number): return(fmt % (number or 0, symbols[depth])) -def log_layer_extract(layer, current, total): +def log_layer_extract(layer, current, total, progress): """ Create log message on layer extract. """ sum_type, sum_value, layer_file, layer_size = layer - logger.info("Extracting layer (%s/%s) with size: %s", - current, total, format_number(layer_size)) + progress("Extracting layer (%s/%s) with size: %s" + % (current, total, format_number(layer_size)), logger=logger) logger.debug('Untar layer: (%s:%s) %s', sum_type, sum_value, layer_file) -def untar_layers(layers_list, dest_dir): +def untar_layers(layers_list, dest_dir, progress): """ Untar each of layers from container image. """ nlayers = len(layers_list) for index, layer in enumerate(layers_list): - log_layer_extract(layer, index + 1, nlayers) + log_layer_extract(layer, index + 1, nlayers, progress) layer_file = layer[2] # Extract layer tarball into destination directory safe_untar(layer_file, dest_dir) + # Update progress value + progress(value=(float(index + 1) / nlayers * 50) + 50) + def get_mime_type(path): """ @@ -203,7 +206,7 @@ def create_qcow2(tar_file, layer_file, backing_file=None, size=DEF_QCOW2_SIZE): execute(tar_in_cmd) -def extract_layers_in_qcow2(layers_list, dest_dir): +def extract_layers_in_qcow2(layers_list, dest_dir, progress): """ Extract docker layers in qcow2 images with backing chains. """ @@ -211,7 +214,7 @@ def extract_layers_in_qcow2(layers_list, dest_dir): nlayers = len(layers_list) for index, layer in enumerate(layers_list): - log_layer_extract(layer, index + 1, nlayers) + log_layer_extract(layer, index + 1, nlayers, progress) tar_file = layer[2] # Name format for the qcow2 image @@ -221,6 +224,9 @@ def extract_layers_in_qcow2(layers_list, dest_dir): # Keep the file path for the next layer qcow2_backing_file = qcow2_layer_file + # Update progress value + progress(value=(float(index + 1) / nlayers * 50) + 50) + def get_image_dir(no_cache=False): """ @@ -258,6 +264,7 @@ class FileSource(object): def __init__(self, **kwargs): self.path = kwargs['uri'].path self.output_format = kwargs['fmt'] + self.progress = kwargs['progress'].update_progress def unpack(self, dest): """ @@ -270,7 +277,8 @@ class FileSource(object): raise Exception('Invalid file source "%s"' % self.path) if self.output_format == 'dir': - logger.info("Extracting files into destination directory") + self.progress("Extracting files into destination directory", + value=0, logger=logger) safe_untar(self.path, dest) elif self.output_format == 'qcow2': @@ -279,12 +287,14 @@ class FileSource(object): qcow2_file = os.path.realpath('{}/{}.qcow2'.format(dest, file_name)) - logger.info("Extracting files into qcow2 image") + self.progress("Extracting files into qcow2 image", value=0, + logger=logger) create_qcow2(self.path, qcow2_file) else: raise Exception("Unknown format:" + self.output_format) - logger.info("Extraction completed successfully!") + self.progress("Extraction completed successfully!", value=100, + logger=logger) logger.info("Files are stored in: " + dest) @@ -304,6 +314,7 @@ class DockerSource(object): @param fmt: Format used to store image [dir, qcow2] @param insecure: Do not require HTTPS and certificate verification @param no_cache: Whether to store downloaded images or not + @param progress: Instance of the progress module """ self.username = kwargs['username'] @@ -311,6 +322,7 @@ class DockerSource(object): self.output_format = kwargs['fmt'] self.insecure = kwargs['not_secure'] self.no_cache = kwargs['no_cache'] + self.progress = kwargs['progress'].update_progress registry = kwargs['uri'].netloc image = kwargs['uri'].path @@ -352,7 +364,7 @@ class DockerSource(object): self.password = getpass.getpass() skopeo_copy.append('--src-creds={}:{}'.format(self.username, self.password)) - logger.info("Downloading container image") + self.progress("Downloading container image", value=0, logger=logger) # Run "skopeo copy" command execute(skopeo_copy) # Remove the manifest file as it is not needed @@ -363,6 +375,7 @@ class DockerSource(object): Check if layers of container image exist in image_dir and have valid hash sum. """ + self.progress("Checking cached layers", value=0, logger=logger) for sum_type, sum_expected, path, _ignore in self.layers: logger.debug("Checking layer: %s", path) if not (os.path.exists(path) @@ -375,9 +388,7 @@ class DockerSource(object): Retrieve layers of container image. """ # Check if layers have been downloaded - if self.validate_image_layers(): - logger.info("Using cached image layers") - else: + if not self.validate_image_layers(): self.download_image() def unpack(self, dest): @@ -394,11 +405,13 @@ class DockerSource(object): # Unpack to destination directory if self.output_format == 'dir': - logger.info("Extracting container layers") - untar_layers(self.layers, dest) + self.progress("Extracting container layers", value=50, + logger=logger) + untar_layers(self.layers, dest, self.progress) elif self.output_format == 'qcow2': - logger.info("Extracting container layers into qcow2 images") - extract_layers_in_qcow2(self.layers, dest) + self.progress("Extracting container layers into qcow2 images", + value=50, logger=logger) + extract_layers_in_qcow2(self.layers, dest, self.progress) else: raise Exception("Unknown format:" + self.output_format) @@ -406,7 +419,8 @@ class DockerSource(object): raise else: - logger.info("Download and extract completed!") + self.progress("Download and extract completed!", value=100, + logger=logger) logger.info("Files are stored in: " + dest) finally: diff --git a/src/virtBootstrap/virt_bootstrap.py b/src/virtBootstrap/virt_bootstrap.py index 460d0e9..f03f29b 100755 --- a/src/virtBootstrap/virt_bootstrap.py +++ b/src/virtBootstrap/virt_bootstrap.py @@ -34,6 +34,7 @@ except ImportError: from urllib.parse import urlparse from virtBootstrap import sources +from virtBootstrap import progress gettext.bindtextdomain("virt-bootstrap", "/usr/share/locale") @@ -87,10 +88,14 @@ def bootstrap(uri, dest, password=None, root_password=None, not_secure=False, - no_cache=False): + no_cache=False, + progress_cb=None): """ Get source object and call unpack method """ + # Get instance of progress storing module + prog = progress.Progress(progress_cb) + uri = urlparse(uri) source = get_source(uri.scheme or 'file') @@ -108,7 +113,8 @@ def bootstrap(uri, dest, username=username, password=password, not_secure=not_secure, - no_cache=no_cache).unpack(dest) + no_cache=no_cache, + progress=prog).unpack(dest) if root_password is not None: set_root_password(dest, root_password) |