summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRadostin Stoyanov <rstoyanov1@gmail.com>2017-07-04 16:24:23 +0100
committerCédric Bosdonnat <cbosdonnat@suse.com>2017-07-05 13:29:45 +0200
commitb8b69f2f41802bf87e119845a6f532d4970d2481 (patch)
treeeb1cf2d3ccb436379c069ae311e7f575efeeeb3f /src
parent9d1bc151c7eb72c1281d6a22476a9082fef8374e (diff)
downloadvirt-bootstrap.git-b8b69f2f41802bf87e119845a6f532d4970d2481.tar.gz
virt-bootstrap.git-b8b69f2f41802bf87e119845a6f532d4970d2481.tar.xz
virt-bootstrap.git-b8b69f2f41802bf87e119845a6f532d4970d2481.zip
Add new module to store the progress
This module is used to store the progress of the bootstrap process and could be used by other applications to get status of virt-bootstrap as well as percentage of completion. Convert some info messages to use this new system for a clean reporting in client applications. Example usage: import virtBootstrap def show_progress(data): print("Status: %s, Progress: %.2f" % (data['status'], data['value'])) virtBootstrap.bootstrap(uri='docker://ubuntu', dest="/tmp/test1", progress_cb=show_progress)
Diffstat (limited to 'src')
-rw-r--r--src/virtBootstrap/progress.py67
-rw-r--r--src/virtBootstrap/sources.py52
-rwxr-xr-xsrc/virtBootstrap/virt_bootstrap.py10
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)