Unify output by creating a seperated output module
authorNikos Skalkotos <skalkoto@grnet.gr>
Tue, 5 Jun 2012 15:36:17 +0000 (18:36 +0300)
committerNikos Skalkotos <skalkoto@grnet.gr>
Tue, 5 Jun 2012 16:08:04 +0000 (19:08 +0300)
All image-creator components now output messages using an instance
of one of the output classes.

image_creator/disk.py
image_creator/kamaki_wrapper.py
image_creator/main.py
image_creator/os_type/__init__.py
image_creator/os_type/linux.py
image_creator/os_type/ubuntu.py
image_creator/os_type/unix.py
image_creator/output.py [new file with mode: 0644]
image_creator/util.py

index 6632b3e..20ce9f5 100644 (file)
@@ -32,7 +32,7 @@
 # or implied, of GRNET S.A.
 
 from image_creator.util import get_command
 # or implied, of GRNET S.A.
 
 from image_creator.util import get_command
-from image_creator.util import warn, progress, success, output, FatalError
+from image_creator.util import FatalError
 from image_creator.gpt import GPTPartitionTable
 import stat
 import os
 from image_creator.gpt import GPTPartitionTable
 import stat
 import os
@@ -62,12 +62,13 @@ class Disk(object):
     the Linux kernel.
     """
 
     the Linux kernel.
     """
 
-    def __init__(self, source):
+    def __init__(self, source, output):
         """Create a new Disk instance out of a source media. The source
         media can be an image file, a block device or a directory."""
         self._cleanup_jobs = []
         self._devices = []
         self.source = source
         """Create a new Disk instance out of a source media. The source
         media can be an image file, a block device or a directory."""
         self._cleanup_jobs = []
         self._devices = []
         self.source = source
+        self.out = output
 
     def _add_cleanup(self, job, *args):
         self._cleanup_jobs.append((job, args))
 
     def _add_cleanup(self, job, *args):
         self._cleanup_jobs.append((job, args))
@@ -98,7 +99,7 @@ class Disk(object):
         instance.
         """
 
         instance.
         """
 
-        output("Examining source media `%s'..." % self.source, False)
+        self.out.output("Examining source media `%s'..." % self.source, False)
         sourcedev = self.source
         mode = os.stat(self.source).st_mode
         if stat.S_ISDIR(mode):
         sourcedev = self.source
         mode = os.stat(self.source).st_mode
         if stat.S_ISDIR(mode):
@@ -111,10 +112,10 @@ class Disk(object):
             raise ValueError("Invalid media source. Only block devices, "
                             "regular files and directories are supported.")
         else:
             raise ValueError("Invalid media source. Only block devices, "
                             "regular files and directories are supported.")
         else:
-            success('looks like a block device')
+            self.out.success('looks like a block device')
 
         # Take a snapshot and return it to the user
 
         # Take a snapshot and return it to the user
-        output("Snapshotting media source...", False)
+        self.out.output("Snapshotting media source...", False)
         size = blockdev('--getsize', sourcedev)
         cowfd, cow = tempfile.mkstemp()
         os.close(cowfd)
         size = blockdev('--getsize', sourcedev)
         cowfd, cow = tempfile.mkstemp()
         os.close(cowfd)
@@ -137,13 +138,13 @@ class Disk(object):
 
         finally:
             os.unlink(table)
 
         finally:
             os.unlink(table)
-        success('done')
+        self.out.success('done')
         return "/dev/mapper/%s" % snapshot
 
     def get_device(self, media):
         """Returns a newly created DiskDevice instance."""
 
         return "/dev/mapper/%s" % snapshot
 
     def get_device(self, media):
         """Returns a newly created DiskDevice instance."""
 
-        new_device = DiskDevice(media)
+        new_device = DiskDevice(media, self.out)
         self._devices.append(new_device)
         new_device.enable()
         return new_device
         self._devices.append(new_device)
         new_device.enable()
         return new_device
@@ -161,10 +162,11 @@ class DiskDevice(object):
     as created by the device-mapper.
     """
 
     as created by the device-mapper.
     """
 
-    def __init__(self, device, bootable=True):
+    def __init__(self, device, output, bootable=True):
         """Create a new DiskDevice."""
 
         self.real_device = device
         """Create a new DiskDevice."""
 
         self.real_device = device
+        self.out = output
         self.bootable = bootable
         self.progress_bar = None
         self.guestfs_device = None
         self.bootable = bootable
         self.progress_bar = None
         self.guestfs_device = None
@@ -180,7 +182,7 @@ class DiskDevice(object):
 
     def enable(self):
         """Enable a newly created DiskDevice"""
 
     def enable(self):
         """Enable a newly created DiskDevice"""
-        self.progressbar = progress("Launching helper VM: ", "percent")
+        self.progressbar = self.out.Progress("Launching helper VM", "percent")
         self.progressbar.max = 100
         self.progressbar.goto(1)
         eh = self.g.set_event_callback(self.progress_callback,
         self.progressbar.max = 100
         self.progressbar.goto(1)
         eh = self.g.set_event_callback(self.progress_callback,
@@ -188,12 +190,10 @@ class DiskDevice(object):
         self.g.launch()
         self.guestfs_enabled = True
         self.g.delete_event_callback(eh)
         self.g.launch()
         self.guestfs_enabled = True
         self.g.delete_event_callback(eh)
-        if self.progressbar is not None:
-            output("\rLaunching helper VM...\033[K", False)
-            success("done")
-            self.progressbar = None
+        self.progressbar.success('done')
+        self.progressbar = None
 
 
-        output('Inspecting Operating System...', False)
+        self.out.output('Inspecting Operating System...', False)
         roots = self.g.inspect_os()
         if len(roots) == 0:
             raise FatalError("No operating system found")
         roots = self.g.inspect_os()
         if len(roots) == 0:
             raise FatalError("No operating system found")
@@ -208,7 +208,7 @@ class DiskDevice(object):
 
         self.ostype = self.g.inspect_get_type(self.root)
         self.distro = self.g.inspect_get_distro(self.root)
 
         self.ostype = self.g.inspect_get_type(self.root)
         self.distro = self.g.inspect_get_distro(self.root)
-        success('found a(n) %s system' % self.distro)
+        self.out.success('found a(n) %s system' % self.distro)
 
     def destroy(self):
         """Destroy this DiskDevice instance."""
 
     def destroy(self):
         """Destroy this DiskDevice instance."""
@@ -229,7 +229,7 @@ class DiskDevice(object):
     def mount(self):
         """Mount all disk partitions in a correct order."""
 
     def mount(self):
         """Mount all disk partitions in a correct order."""
 
-        output("Mounting image...", False)
+        self.out.output("Mounting image...", False)
         mps = self.g.inspect_get_mountpoints(self.root)
 
         # Sort the keys to mount the fs in a correct order.
         mps = self.g.inspect_get_mountpoints(self.root)
 
         # Sort the keys to mount the fs in a correct order.
@@ -246,8 +246,8 @@ class DiskDevice(object):
             try:
                 self.g.mount(dev, mp)
             except RuntimeError as msg:
             try:
                 self.g.mount(dev, mp)
             except RuntimeError as msg:
-                warn("%s (ignored)" % msg)
-        success("done")
+                self.out.warn("%s (ignored)" % msg)
+        self.out.success("done")
 
     def umount(self):
         """Umount all mounted filesystems."""
 
     def umount(self):
         """Umount all mounted filesystems."""
@@ -307,7 +307,7 @@ class DiskDevice(object):
 
         MB = 2 ** 20
 
 
         MB = 2 ** 20
 
-        output("Shrinking image (this may take a while)...", False)
+        self.out.output("Shrinking image (this may take a while)...", False)
 
         last_part = None
         fstype = None
 
         last_part = None
         fstype = None
@@ -329,7 +329,7 @@ class DiskDevice(object):
             break
 
         if not re.match("ext[234]", fstype):
             break
 
         if not re.match("ext[234]", fstype):
-            warn("Don't know how to resize %s partitions." % fstype)
+            self.out.warn("Don't know how to resize %s partitions." % fstype)
             return self.meta['SIZE']
 
         part_dev = "%s%d" % (self.guestfs_device, last_part['part_num'])
             return self.meta['SIZE']
 
         part_dev = "%s%d" % (self.guestfs_device, last_part['part_num'])
@@ -387,7 +387,7 @@ class DiskDevice(object):
                 part_set_id(last_part['part_num'], last_part['id'])
 
         new_size = (end + 1) * sector_size
                 part_set_id(last_part['part_num'], last_part['id'])
 
         new_size = (end + 1) * sector_size
-        success("new size is %dMB" % ((new_size + MB - 1) // MB))
+        self.out.success("new size is %dMB" % ((new_size + MB - 1) // MB))
 
         if self.meta['PARTITION_TABLE'] == 'gpt':
             ptable = GPTPartitionTable(self.real_device)
 
         if self.meta['PARTITION_TABLE'] == 'gpt':
             ptable = GPTPartitionTable(self.real_device)
@@ -407,7 +407,7 @@ class DiskDevice(object):
         blocksize = 4 * MB  # 4MB
         size = self.meta['SIZE']
         progress_size = (size + MB - 1) // MB  # in MB
         blocksize = 4 * MB  # 4MB
         size = self.meta['SIZE']
         progress_size = (size + MB - 1) // MB  # in MB
-        progressbar = progress("Dumping image file: ", 'mb')
+        progressbar = self.out.Progress("Dumping image file", 'mb')
         progressbar.max = progress_size
 
         with open(self.real_device, 'r') as src:
         progressbar.max = progress_size
 
         with open(self.real_device, 'r') as src:
@@ -421,7 +421,6 @@ class DiskDevice(object):
                     offset += sent
                     left -= sent
                     progressbar.goto((size - left) // MB)
                     offset += sent
                     left -= sent
                     progressbar.goto((size - left) // MB)
-        output("\rDumping image file...\033[K", False)
-        success('image file %s was successfully created' % outfile)
+        progressbar.success('image file %s was successfully created' % outfile)
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index e3c249e..e1f3a02 100644 (file)
@@ -39,37 +39,17 @@ from kamaki.clients.image import ImageClient
 from kamaki.clients.pithos import PithosClient
 from progress.bar import Bar
 
 from kamaki.clients.pithos import PithosClient
 from progress.bar import Bar
 
-from image_creator.util import FatalError, output, success
+from image_creator.util import FatalError
+from image_creator.output import output, warn
 
 CONTAINER = "images"
 
 
 
 CONTAINER = "images"
 
 
-def progress(message):
-
-    MSG_LENGTH = 30
-
-    def progress_gen(n):
-        msg = "%s:" % message
-
-        progressbar = Bar(msg.ljust(MSG_LENGTH))
-        progressbar.max = n
-        progressbar.fill = '#'
-        progressbar.bar_prefix = ' ['
-        progressbar.bar_suffix = '] '
-
-        for _ in range(n):
-            yield
-            progressbar.next()
-        output("\r%s...\033[K" % message, False)
-        success("done")
-        yield
-    return progress_gen
-
-
-class Kamaki:
-    def __init__(self, account, token):
+class Kamaki(object):
+    def __init__(self, account, token, output):
         self.account = account
         self.token = token
         self.account = account
         self.token = token
+        self.out = output
 
         config = Config()
 
 
         config = Config()
 
@@ -95,8 +75,8 @@ class Kamaki:
                 raise FatalError("Pithos client: %d %s" % \
                                                     (e.status, e.message))
         try:
                 raise FatalError("Pithos client: %d %s" % \
                                                     (e.status, e.message))
         try:
-            hash_cb = progress(hp) if hp is not None else None
-            upload_cb = progress(up) if up is not None else None
+            hash_cb = self.out.progress_gen(hp) if hp is not None else None
+            upload_cb = self.out.progress_gen(up) if up is not None else None
             self.pithos_client.create_object(remote_path, file_obj, size,
                                                             hash_cb, upload_cb)
             return "pithos://%s/%s/%s" % \
             self.pithos_client.create_object(remote_path, file_obj, size,
                                                             hash_cb, upload_cb)
             return "pithos://%s/%s/%s" % \
index afcdfcd..30fa7d6 100644 (file)
@@ -36,8 +36,8 @@
 from image_creator import __version__ as version
 from image_creator import util
 from image_creator.disk import Disk
 from image_creator import __version__ as version
 from image_creator import util
 from image_creator.disk import Disk
-from image_creator.util import get_command, error, success, output, \
-                                                    FatalError, progress, md5
+from image_creator.util import get_command, FatalError, MD5
+from image_creator.output import Output, Output_with_progress, Silent, error
 from image_creator.os_type import get_os_class
 from image_creator.kamaki_wrapper import Kamaki
 import sys
 from image_creator.os_type import get_os_class
 from image_creator.kamaki_wrapper import Kamaki
 import sys
@@ -153,17 +153,19 @@ def parse_options(input_args):
 def image_creator():
     options = parse_options(sys.argv[1:])
 
 def image_creator():
     options = parse_options(sys.argv[1:])
 
-    if options.silent:
-        util.silent = True
-
     if options.outfile is None and not options.upload \
                                             and not options.print_sysprep:
         raise FatalError("At least one of `-o', `-u' or `--print-sysprep' " \
                                                                 "must be set")
 
     if options.outfile is None and not options.upload \
                                             and not options.print_sysprep:
         raise FatalError("At least one of `-o', `-u' or `--print-sysprep' " \
                                                                 "must be set")
 
+    if options.silent:
+        out = Silent()
+    else:
+        out = Output_with_progress()
+
     title = 'snf-image-creator %s' % version
     title = 'snf-image-creator %s' % version
-    output(title)
-    output('=' * len(title))
+    out.output(title)
+    out.output('=' * len(title))
 
     if os.geteuid() != 0:
         raise FatalError("You must run %s as root" \
 
     if os.geteuid() != 0:
         raise FatalError("You must run %s as root" \
@@ -176,7 +178,7 @@ def image_creator():
                 raise FatalError("Output file %s exists "
                     "(use --force to overwrite it)." % filename)
 
                 raise FatalError("Output file %s exists "
                     "(use --force to overwrite it)." % filename)
 
-    disk = Disk(options.source)
+    disk = Disk(options.source, out)
     try:
         snapshot = disk.snapshot()
 
     try:
         snapshot = disk.snapshot()
 
@@ -184,8 +186,8 @@ def image_creator():
         dev.mount()
 
         osclass = get_os_class(dev.distro, dev.ostype)
         dev.mount()
 
         osclass = get_os_class(dev.distro, dev.ostype)
-        image_os = osclass(dev.root, dev.g)
-        output()
+        image_os = osclass(dev.root, dev.g, out)
+        out.output()
 
         for sysprep in options.disabled_syspreps:
             image_os.disable_sysprep(sysprep)
 
         for sysprep in options.disabled_syspreps:
             image_os.disable_sysprep(sysprep)
@@ -195,7 +197,7 @@ def image_creator():
 
         if options.print_sysprep:
             image_os.print_syspreps()
 
         if options.print_sysprep:
             image_os.print_syspreps()
-            output()
+            out.output()
 
         if options.outfile is None and not options.upload:
             return 0
 
         if options.outfile is None and not options.upload:
             return 0
@@ -206,13 +208,14 @@ def image_creator():
         metadata = image_os.meta
         dev.umount()
 
         metadata = image_os.meta
         dev.umount()
 
-        size = options.shrink and dev.shrink() or dev.size
+        size = options.shrink and dev.shrink() or dev.meta['SIZE']
         metadata.update(dev.meta)
 
         # Add command line metadata to the collected ones...
         metadata.update(options.metadata)
 
         metadata.update(dev.meta)
 
         # Add command line metadata to the collected ones...
         metadata.update(options.metadata)
 
-        checksum = md5(snapshot, size)
+        md5 = MD5(out)
+        checksum = md5.compute(snapshot, size)
 
         metastring = '\n'.join(
                 ['%s=%s' % (key, value) for (key, value) in metadata.items()])
 
         metastring = '\n'.join(
                 ['%s=%s' % (key, value) for (key, value) in metadata.items()])
@@ -221,54 +224,54 @@ def image_creator():
         if options.outfile is not None:
             dev.dump(options.outfile)
 
         if options.outfile is not None:
             dev.dump(options.outfile)
 
-            output('Dumping metadata file...', False)
+            out.output('Dumping metadata file...', False)
             with open('%s.%s' % (options.outfile, 'meta'), 'w') as f:
                 f.write(metastring)
             with open('%s.%s' % (options.outfile, 'meta'), 'w') as f:
                 f.write(metastring)
-            success('done')
+            out.success('done')
 
 
-            output('Dumping md5sum file...', False)
+            out.output('Dumping md5sum file...', False)
             with open('%s.%s' % (options.outfile, 'md5sum'), 'w') as f:
                 f.write('%s %s\n' % (checksum, \
                                             os.path.basename(options.outfile)))
             with open('%s.%s' % (options.outfile, 'md5sum'), 'w') as f:
                 f.write('%s %s\n' % (checksum, \
                                             os.path.basename(options.outfile)))
-            success('done')
+            out.success('done')
 
         # Destroy the device. We only need the snapshot from now on
         disk.destroy_device(dev)
 
 
         # Destroy the device. We only need the snapshot from now on
         disk.destroy_device(dev)
 
-        output()
+        out.output()
 
         uploaded_obj = ""
         if options.upload:
 
         uploaded_obj = ""
         if options.upload:
-            output("Uploading image to pithos:")
-            kamaki = Kamaki(options.account, options.token)
+            out.output("Uploading image to pithos:")
+            kamaki = Kamaki(options.account, options.token, out)
             with open(snapshot) as f:
                 uploaded_obj = kamaki.upload(f, size, options.upload,
                                 "(1/4)  Calculating block hashes",
                                 "(2/4)  Uploading missing blocks")
 
             with open(snapshot) as f:
                 uploaded_obj = kamaki.upload(f, size, options.upload,
                                 "(1/4)  Calculating block hashes",
                                 "(2/4)  Uploading missing blocks")
 
-            output("(3/4)  Uploading metadata file...", False)
+            out.output("(3/4)  Uploading metadata file...", False)
             kamaki.upload(StringIO.StringIO(metastring), size=len(metastring),
                                 remote_path="%s.%s" % (options.upload, 'meta'))
             kamaki.upload(StringIO.StringIO(metastring), size=len(metastring),
                                 remote_path="%s.%s" % (options.upload, 'meta'))
-            success('done')
-            output("(4/4)  Uploading md5sum file...", False)
+            out.success('done')
+            out.output("(4/4)  Uploading md5sum file...", False)
             md5sumstr = '%s %s\n' % (
                 checksum, os.path.basename(options.upload))
             kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr),
                             remote_path="%s.%s" % (options.upload, 'md5sum'))
             md5sumstr = '%s %s\n' % (
                 checksum, os.path.basename(options.upload))
             kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr),
                             remote_path="%s.%s" % (options.upload, 'md5sum'))
-            success('done')
-            output()
+            out.success('done')
+            out.output()
 
         if options.register:
 
         if options.register:
-            output('Registring image to ~okeanos...', False)
+            out.output('Registring image to ~okeanos...', False)
             kamaki.register(options.register, uploaded_obj, metadata)
             kamaki.register(options.register, uploaded_obj, metadata)
-            success('done')
-            output()
+            out.success('done')
+            out.output()
 
     finally:
 
     finally:
-        output('cleaning up...')
+        out.output('cleaning up...')
         disk.cleanup()
 
         disk.cleanup()
 
-    success("snf-image-creator exited without errors")
+    out.success("snf-image-creator exited without errors")
 
     return 0
 
 
     return 0
 
index 8a39846..98d570d 100644 (file)
@@ -31,7 +31,7 @@
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
-from image_creator.util import output, FatalError
+from image_creator.util import FatalError
 
 import textwrap
 import re
 
 import textwrap
 import re
@@ -70,9 +70,10 @@ def sysprep(enabled=True):
 class OSBase(object):
     """Basic operating system class"""
 
 class OSBase(object):
     """Basic operating system class"""
 
-    def __init__(self, rootdev, ghandler):
+    def __init__(self, rootdev, ghandler, output):
         self.root = rootdev
         self.g = ghandler
         self.root = rootdev
         self.g = ghandler
+        self.out = output
 
         # Collect metadata about the OS
         self.meta = {}
 
         # Collect metadata about the OS
         self.meta = {}
@@ -129,23 +130,23 @@ class OSBase(object):
         wrapper.initial_indent = '\t'
         wrapper.width = 72
 
         wrapper.initial_indent = '\t'
         wrapper.width = 72
 
-        output("Enabled system preperation operations:")
+        self.out.output("Enabled system preperation operations:")
         if len(enabled) == 0:
         if len(enabled) == 0:
-            output("(none)")
+            self.out.output("(none)")
         else:
             for sysprep in enabled:
                 name = sysprep.__name__.replace('_', '-')
                 descr = wrapper.fill(textwrap.dedent(sysprep.__doc__))
         else:
             for sysprep in enabled:
                 name = sysprep.__name__.replace('_', '-')
                 descr = wrapper.fill(textwrap.dedent(sysprep.__doc__))
-                output('    %s:\n%s\n' % (name, descr))
+                self.out.output('    %s:\n%s\n' % (name, descr))
 
 
-        output("Disabled system preperation operations:")
+        self.out.output("Disabled system preperation operations:")
         if len(disabled) == 0:
         if len(disabled) == 0:
-            output("(none)")
+            self.out.output("(none)")
         else:
             for sysprep in disabled:
                 name = sysprep.__name__.replace('_', '-')
                 descr = wrapper.fill(textwrap.dedent(sysprep.__doc__))
         else:
             for sysprep in disabled:
                 name = sysprep.__name__.replace('_', '-')
                 descr = wrapper.fill(textwrap.dedent(sysprep.__doc__))
-                output('    %s:\n%s\n' % (name, descr))
+                self.out.output('    %s:\n%s\n' % (name, descr))
 
     @add_prefix
     def ls(self, directory):
 
     @add_prefix
     def ls(self, directory):
@@ -202,15 +203,15 @@ class OSBase(object):
     def do_sysprep(self):
         """Prepere system for image creation."""
 
     def do_sysprep(self):
         """Prepere system for image creation."""
 
-        output('Preparing system for image creation:')
+        self.out.output('Preparing system for image creation:')
 
         tasks, _ = self.list_syspreps()
         size = len(tasks)
         cnt = 0
         for task in tasks:
             cnt += 1
 
         tasks, _ = self.list_syspreps()
         size = len(tasks)
         cnt = 0
         for task in tasks:
             cnt += 1
-            output(('(%d/%d)' % (cnt, size)).ljust(7), False)
+            self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
             task()
             task()
-        output()
+        self.out.output()
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index e128396..9a54eeb 100644 (file)
 # or implied, of GRNET S.A.
 
 from image_creator.os_type.unix import Unix, sysprep
 # or implied, of GRNET S.A.
 
 from image_creator.os_type.unix import Unix, sysprep
-from image_creator.util import warn, output
 
 import re
 import time
 
 
 class Linux(Unix):
 
 import re
 import time
 
 
 class Linux(Unix):
-    def __init__(self, rootdev, ghandler):
-        super(Linux, self).__init__(rootdev, ghandler)
+    def __init__(self, rootdev, ghandler, output):
+        super(Linux, self).__init__(rootdev, ghandler, output)
         self._uuid = dict()
         self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
 
         self._uuid = dict()
         self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
 
@@ -63,14 +62,14 @@ class Linux(Unix):
         """
 
         if print_header:
         """
 
         if print_header:
-            output('Fixing acpid powerdown action')
+            self.out.output('Fixing acpid powerdown action')
 
         powerbtn_action = '#!/bin/sh\n\nPATH=/sbin:/bin:/usr/bin\n' \
                                 'shutdown -h now \"Power button pressed\"\n'
 
         events_dir = '/etc/acpi/events'
         if not self.g.is_dir(events_dir):
 
         powerbtn_action = '#!/bin/sh\n\nPATH=/sbin:/bin:/usr/bin\n' \
                                 'shutdown -h now \"Power button pressed\"\n'
 
         events_dir = '/etc/acpi/events'
         if not self.g.is_dir(events_dir):
-            warn("No acpid event directory found")
+            self.out.warn("No acpid event directory found")
             return
 
         event_exp = re.compile('event=(.+)', re.I)
             return
 
         event_exp = re.compile('event=(.+)', re.I)
@@ -95,18 +94,21 @@ class Linux(Unix):
             if event.strip() == "button[ /]power":
                 if action:
                     if not self.g.is_file(action):
             if event.strip() == "button[ /]power":
                 if action:
                     if not self.g.is_file(action):
-                        warn("Acpid action file: %s does not exist" % action)
+                        self.out.warn("Acpid action file: %s does not exist" %
+                                                                        action)
                         return
                     self.g.copy_file_to_file(action, \
                       "%s.orig.snf-image-creator-%d" % (action, time.time()))
                     self.g.write(action, powerbtn_action)
                     return
                 else:
                         return
                     self.g.copy_file_to_file(action, \
                       "%s.orig.snf-image-creator-%d" % (action, time.time()))
                     self.g.write(action, powerbtn_action)
                     return
                 else:
-                    warn("Acpid event file %s does not contain and action")
+                    self.out.warn(
+                            "Acpid event file %s does not contain and action")
                     return
             elif event.strip() == ".*":
                     return
             elif event.strip() == ".*":
-                warn("Found action `.*'. Don't know how to handle this." \
-                    " Please edit \%s' image file manually to make the " \
+                self.out.warn(
+                    "Found action `.*'. Don't know how to handle this. " \
+                    "Please edit \%s' image file manually to make the " \
                     "system immediatelly shutdown when an power button acpi " \
                     "event occures" % action)
                 return
                     "system immediatelly shutdown when an power button acpi " \
                     "event occures" % action)
                 return
@@ -119,7 +121,7 @@ class Linux(Unix):
         """
 
         if print_header:
         """
 
         if print_header:
-            output('Removing persistent network interface names')
+            self.out.output('Removing persistent network interface names')
 
         rule_file = '/etc/udev/rules.d/70-persistent-net.rules'
         if self.g.is_file(rule_file):
 
         rule_file = '/etc/udev/rules.d/70-persistent-net.rules'
         if self.g.is_file(rule_file):
@@ -134,7 +136,7 @@ class Linux(Unix):
         """
 
         if print_header:
         """
 
         if print_header:
-            output('Removing swap entry from fstab')
+            self.out.output('Removing swap entry from fstab')
 
         new_fstab = ""
         fstab = self.g.cat('/etc/fstab')
 
         new_fstab = ""
         fstab = self.g.cat('/etc/fstab')
@@ -155,7 +157,8 @@ class Linux(Unix):
         """
 
         if print_header:
         """
 
         if print_header:
-            output('Replacing fstab & grub non-persistent device appearences')
+            self.out.output(
+                    'Replacing fstab & grub non-persistent device appearences')
 
         # convert all devices in fstab to persistent
         persistent_root = self._persistent_fstab()
 
         # convert all devices in fstab to persistent
         persistent_root = self._persistent_fstab()
@@ -217,7 +220,7 @@ class Linux(Unix):
 
         entry = line.split()
         if len(entry) != 6:
 
         entry = line.split()
         if len(entry) != 6:
-            warn("Detected abnormal entry in fstab")
+            self.out.warn("Detected abnormal entry in fstab")
             return orig, "", ""
 
         dev = entry[0]
             return orig, "", ""
 
         dev = entry[0]
index f992981..e5cb07d 100644 (file)
@@ -35,8 +35,8 @@ from image_creator.os_type.linux import Linux, sysprep
 
 
 class Ubuntu(Linux):
 
 
 class Ubuntu(Linux):
-    def __init__(self, rootdev, ghandler):
-        super(Ubuntu, self).__init__(rootdev, ghandler)
+    def __init__(self, rootdev, ghandler, output):
+        super(Ubuntu, self).__init__(rootdev, ghandler, output)
 
         apps = self.g.inspect_list_applications(self.root)
         for app in apps:
 
         apps = self.g.inspect_list_applications(self.root)
         for app in apps:
index 7eb7f76..06eccbf 100644 (file)
@@ -35,7 +35,6 @@ import re
 import sys
 
 from image_creator.os_type import OSBase, sysprep
 import sys
 
 from image_creator.os_type import OSBase, sysprep
-from image_creator.util import warn, output
 
 
 class Unix(OSBase):
 
 
 class Unix(OSBase):
@@ -48,8 +47,8 @@ class Unix(OSBase):
         '.thunderbird'
     ]
 
         '.thunderbird'
     ]
 
-    def __init__(self, rootdev, ghandler):
-        super(Unix, self).__init__(rootdev, ghandler)
+    def __init__(self, rootdev, ghandler, output):
+        super(Unix, self).__init__(rootdev, ghandler, output)
 
         self.meta["USERS"] = " ".join(self._get_passworded_users())
 
 
         self.meta["USERS"] = " ".join(self._get_passworded_users())
 
@@ -75,7 +74,8 @@ class Unix(OSBase):
         """Remove all user accounts with id greater than 1000"""
 
         if print_header:
         """Remove all user accounts with id greater than 1000"""
 
         if print_header:
-            output('Removing all user accounts with id greater than 1000')
+            self.out.output(
+                    'Removing all user accounts with id greater than 1000')
 
         # Remove users from /etc/passwd
         passwd = []
 
         # Remove users from /etc/passwd
         passwd = []
@@ -123,7 +123,8 @@ class Unix(OSBase):
         """Remove all passwords and lock all user accounts"""
 
         if print_header:
         """Remove all passwords and lock all user accounts"""
 
         if print_header:
-            output('Cleaning up passwords & locking all user accounts')
+            self.out.output(
+                    'Cleaning up passwords & locking all user accounts')
 
         shadow = []
 
 
         shadow = []
 
@@ -141,7 +142,7 @@ class Unix(OSBase):
         """Remove all regular files under /var/cache"""
 
         if print_header:
         """Remove all regular files under /var/cache"""
 
         if print_header:
-            output('Removing files under /var/cache')
+            self.out.output('Removing files under /var/cache')
 
         self.foreach_file('/var/cache', self.g.rm, ftype='r')
 
 
         self.foreach_file('/var/cache', self.g.rm, ftype='r')
 
@@ -150,7 +151,7 @@ class Unix(OSBase):
         """Remove all files under /tmp and /var/tmp"""
 
         if print_header:
         """Remove all files under /tmp and /var/tmp"""
 
         if print_header:
-            output('Removing files under /tmp and /var/tmp')
+            self.out.output('Removing files under /tmp and /var/tmp')
 
         self.foreach_file('/tmp', self.g.rm_rf, maxdepth=1)
         self.foreach_file('/var/tmp', self.g.rm_rf, maxdepth=1)
 
         self.foreach_file('/tmp', self.g.rm_rf, maxdepth=1)
         self.foreach_file('/var/tmp', self.g.rm_rf, maxdepth=1)
@@ -160,7 +161,7 @@ class Unix(OSBase):
         """Empty all files under /var/log"""
 
         if print_header:
         """Empty all files under /var/log"""
 
         if print_header:
-            output('Emptying all files under /var/log')
+            self.out.output('Emptying all files under /var/log')
 
         self.foreach_file('/var/log', self.g.truncate, ftype='r')
 
 
         self.foreach_file('/var/log', self.g.truncate, ftype='r')
 
@@ -169,7 +170,7 @@ class Unix(OSBase):
         """Remove all files under /var/mail and /var/spool/mail"""
 
         if print_header:
         """Remove all files under /var/mail and /var/spool/mail"""
 
         if print_header:
-            output('Removing files under /var/mail and /var/spool/mail')
+            self.out.output('Removing files under /var/mail & /var/spool/mail')
 
         self.foreach_file('/var/spool/mail', self.g.rm_rf, maxdepth=1)
         self.foreach_file('/var/mail', self.g.rm_rf, maxdepth=1)
 
         self.foreach_file('/var/spool/mail', self.g.rm_rf, maxdepth=1)
         self.foreach_file('/var/mail', self.g.rm_rf, maxdepth=1)
@@ -181,7 +182,7 @@ class Unix(OSBase):
         homedirs = ['/root'] + self.ls('/home/')
 
         if print_header:
         homedirs = ['/root'] + self.ls('/home/')
 
         if print_header:
-            output('Removing sensitive user data under %s' % " ".
+            self.out.output('Removing sensitive user data under %s' % " ".
                                                         join(homedirs))
 
         for homedir in homedirs:
                                                         join(homedirs))
 
         for homedir in homedirs:
diff --git a/image_creator/output.py b/image_creator/output.py
new file mode 100644 (file)
index 0000000..0f33f6d
--- /dev/null
@@ -0,0 +1,148 @@
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+#   1. Redistributions of source code must retain the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer in the documentation and/or other materials
+#      provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+import sys
+from progress.bar import Bar
+from colors import red, green, yellow
+
+
+def error(msg, new_line=True):
+    nl = "\n" if new_line else ''
+    sys.stderr.write(red('Error: %s' % msg) + nl)
+
+
+def warn(msg, new_line=True):
+    nl = "\n" if new_line else ''
+    sys.stderr.write(yellow("Warning: %s" % msg) + nl)
+
+
+def success(msg, new_line=True):
+    nl = "\n" if new_line else ''
+    sys.stdout.write(green(msg) + nl)
+    if not nl:
+        sys.stdout.flush()
+
+
+def output(msg='', new_line=True):
+    nl = "\n" if new_line else ''
+    sys.stdout.write(msg + nl)
+    if not nl:
+        sys.stdout.flush()
+
+
+class Output(object):
+    def error(self, msg, new_line=True):
+        error(msg, new_line)
+
+    def warn(self, msg, new_line=True):
+        warn(msg, new_line)
+
+    def success(self, msg, new_line=True):
+        success(msg, new_line)
+
+    def output(self, msg='', new_line=True):
+        output(msg, new_line)
+
+    class Progress(object):
+        def __init__(self, title, bar_type='default'):
+            output("%s..." % title, False)
+
+        def goto(self, dest):
+            pass
+
+        def next(self):
+            pass
+
+        def success(self, result):
+            sucess(result)
+
+    def progress_gen(self, message):
+        
+        progress = getattr(self, 'Progress')
+
+        def generator(n):
+            progressbar = progress(message, 'default')
+
+            for _ in range(n):
+                yield
+                progressbar.next()
+
+            progressbar.success('done')
+            yield
+        return generator
+
+
+class Output_with_progress(Output):
+    class Progress(Bar):
+        MESSAGE_LENGTH = 30
+
+        template = {
+            'default': '%(index)d/%(max)d',
+            'percent': '%(percent)d%%',
+            'b': '%(index)d/%(max)d B',
+            'kb': '%(index)d/%(max)d KB',
+            'mb': '%(index)d/%(max)d MB'
+        }
+
+        def __init__(self, title, bar_type='default'):
+            super(Output_with_progress.Progress, self).__init__()
+            self.title = title
+            self.fill = '#'
+            self.bar_prefix = ' ['
+            self.bar_suffix = '] '
+            self.message = ("%s:" % self.title).ljust(self.MESSAGE_LENGTH)
+            self.suffix = self.template[bar_type]
+
+        def success(self, result):
+            output("\r%s... \033[K" % self.title, False)
+            success(result)
+
+
+class Silent(Output):
+    def warn(self, msg, new_line=True):
+        pass
+
+    def success(self, msg, new_line=True):
+        pass
+
+    def output(self, msg='', new_line=True):
+        pass
+
+    class Progress(Output.Progress):
+        def __init__(self, title, bar_type):
+            pass
+
+        def success(self, result):
+            pass
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index d3d4cc0..161b73d 100644 (file)
 import sys
 import pbs
 import hashlib
 import sys
 import pbs
 import hashlib
-from colors import red, green, yellow
-from progress.bar import Bar
 
 
 class FatalError(Exception):
     pass
 
 
 
 
 class FatalError(Exception):
     pass
 
 
-silent = False
-
-
 def get_command(command):
     def find_sbin_command(command, exception):
         search_paths = ['/usr/local/sbin', '/usr/sbin', '/sbin']
 def get_command(command):
     def find_sbin_command(command, exception):
         search_paths = ['/usr/local/sbin', '/usr/sbin', '/sbin']
@@ -59,75 +54,29 @@ def get_command(command):
         return find_sbin_command(command, e)
 
 
         return find_sbin_command(command, e)
 
 
-def error(msg, new_line=True):
-    nl = "\n" if new_line else ''
-    sys.stderr.write(red('Error: %s' % msg) + nl)
-
-
-def warn(msg, new_line=True):
-    if not silent:
-        nl = "\n" if new_line else ''
-        sys.stderr.write(yellow("Warning: %s" % msg) + nl)
-
-
-def success(msg, new_line=True):
-    if not silent:
-        nl = "\n" if new_line else ''
-        sys.stdout.write(green(msg) + nl)
-        if not nl:
-            sys.stdout.flush()
-
-
-def output(msg="", new_line=True):
-    if not silent:
-        nl = "\n" if new_line else ''
-        sys.stdout.write(msg + nl)
-        if not nl:
-            sys.stdout.flush()
-
-
-def progress(message='', bar_type="default"):
-
-    MESSAGE_LENGTH = 30
-
-    suffix = {
-        'default': '%(index)d/%(max)d',
-        'percent': '%(percent)d%%',
-        'b': '%(index)d/%(max)d B',
-        'kb': '%(index)d/%(max)d KB',
-        'mb': '%(index)d/%(max)d MB'
-    }
-
-    bar = Bar()
-    bar.message = message.ljust(MESSAGE_LENGTH)
-    bar.fill = '#'
-    bar.suffix = suffix[bar_type]
-    bar.bar_prefix = ' ['
-    bar.bar_suffix = '] '
-
-    return bar
-
+class MD5:
+    def __init__(self, output):
+        self.out = output
 
 
-def md5(filename, size):
+    def compute(self, filename, size):
 
 
-    BLOCKSIZE = 2 ** 22  # 4MB
+        BLOCKSIZE = 2 ** 22  # 4MB
 
 
-    progressbar = progress("Calculating md5sum:", 'mb')
-    progressbar.max = ((size + 2 ** 20 - 1) // (2 ** 20))
-    md5 = hashlib.md5()
-    with open(filename, "r") as src:
-        left = size
-        while left > 0:
-            length = min(left, BLOCKSIZE)
-            data = src.read(length)
-            md5.update(data)
-            left -= length
-            progressbar.goto((size - left) // (2 ** 20))
+        progressbar = self.out.Progress("Calculating md5sum:", 'mb')
+        progressbar.max = ((size + 2 ** 20 - 1) // (2 ** 20))
+        md5 = hashlib.md5()
+        with open(filename, "r") as src:
+            left = size
+            while left > 0:
+                length = min(left, BLOCKSIZE)
+                data = src.read(length)
+                md5.update(data)
+                left -= length
+                progressbar.goto((size - left) // (2 ** 20))
 
 
-    checksum = md5.hexdigest()
-    output("\rCalculating md5sum...\033[K", False)
-    success(checksum)
+        checksum = md5.hexdigest()
+        progressbar.success(checksum)
 
 
-    return checksum
+        return checksum
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :