Initial commit
authorNikos Skalkotos <skalkoto@grnet.gr>
Wed, 15 Feb 2012 17:19:46 +0000 (19:19 +0200)
committerNikos Skalkotos <skalkoto@grnet.gr>
Wed, 15 Feb 2012 17:19:46 +0000 (19:19 +0200)
image_creator/__init__.py [new file with mode: 0644]
image_creator/disk.py [new file with mode: 0644]
image_creator/main.py [new file with mode: 0644]
setup.py [new file with mode: 0755]

diff --git a/image_creator/__init__.py b/image_creator/__init__.py
new file mode 100644 (file)
index 0000000..817a9c6
--- /dev/null
@@ -0,0 +1,34 @@
+# Copyright 2011 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.
+
+__version__ = '0.1'
diff --git a/image_creator/disk.py b/image_creator/disk.py
new file mode 100644 (file)
index 0000000..b20ca74
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+
+import losetup
+import stat
+import os
+import tempfile
+import uuid
+import re
+from pbs import dmsetup
+from pbs import blockdev
+from pbs import dd
+from pbs import kpartx
+from pbs import mount
+from pbs import umount
+
+class Disk(object):
+
+    def __init__(self, source):
+        self._cleanup_jobs = []
+        self._devices = []
+        self.source = source
+
+    def _add_cleanup(self, job, *args):
+        self._cleanup_jobs.append((job, args))
+
+    def _losetup(self, fname):
+        loop = losetup.find_unused_loop_device()
+        loop.mount(fname)
+        self._add_cleanup(loop.unmount)
+        return loop.device
+
+    def _dir_to_disk(self):
+        raise NotImplementedError
+
+    def cleanup(self):
+        while len(self._cleanup_jobs):
+            job, args = self._cleanup_jobs.pop()
+            job(*args)
+
+    def get_device(self):
+        sourcedev = self.source
+        mode = os.stat(self.source).st_mode
+        if stat.S_ISDIR(mode):
+            return self._losetup(self._dir_to_disk())
+        elif stat.S_ISREG(mode):
+            sourcedev = self._losetup(self.source)
+        elif not stat.S_ISBLK(mode):
+            raise ValueError("Value for self.source is invalid")
+
+        # Take a snapshot and return it to the user
+        size = blockdev('--getsize', sourcedev)
+        cowfd, cow = tempfile.mkstemp()
+        self._add_cleanup(os.unlink, cow)
+        dd('if=/dev/zero', 'of=%s' % cow, 'count=%d' % (1024*1024))#(int(size)/4))
+        cowdev = self._losetup(cow)
+
+        snapshot = uuid.uuid4().hex
+        tablefd, table = tempfile.mkstemp()
+        try:
+            os.write(tablefd, "0 %d snapshot %s %s n 8" % \
+                                        (int(size), sourcedev, cowdev))
+            dmsetup('create', snapshot, table)
+            self._add_cleanup(dmsetup, 'remove', snapshot)
+        finally:
+            os.unlink(table)
+
+        new_device = DiskDevice(self, "/dev/mapper/%s" % snapshot)
+        self._devices.append(new_device)
+        return new_device
+
+class DiskDevice(object):
+
+    def __init__(self, disk, device):
+        self.disk = disk
+        self.dev = device
+        self.partitions_mapped = False
+        self.magic_number = uuid.uuid4().hex
+
+    def list_partitions(self):
+        output = kpartx("-l", "-p", self.magic_number, self.dev)
+        return [ "/dev/mapper/%s" % x for x in
+                re.findall('^\S+', str(output), flags=re.MULTILINE)]
+
+    def mount(self, partition):
+        if not self.partitions_mapped:
+            kpartx("-a", "-p", self.magic_number, self.dev)
+            self.disk._cleanup_jobs.append(kpartx, "-d", "-p",
+                        self.magic_number, self.dev)
+            self.partitions_mapped = True
+
+        targetfd, target = tempfile.mkdtemp()
+        try:
+            mount(dev, partition)
+        except:
+            os.rmdir(table)
+            raise
+        return target
+
+    def unmount(self, partition):
+        umount(target)
+
+        mode = os.stat(self.source).st_mode
+        if stat.S_ISDIR(mode):
+            os.rmdir(target)
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
diff --git a/image_creator/main.py b/image_creator/main.py
new file mode 100644 (file)
index 0000000..554cdd3
--- /dev/null
@@ -0,0 +1,56 @@
+# Copyright 2011 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.
+
+from image_creator.disk import Disk
+import sys
+import os
+
+def main():
+    if len(sys.argv) != 3:
+        sys.exit("Usage: %s <source> <output_file>" %
+                        os.path.basename(sys.argv[0]))
+    source = sys.argv[1]
+    dest = sys.argv[2]
+
+    disk = Disk(source)
+    try:
+        dev = disk.get_device()
+
+    finally:
+        disk.cleanup()
+
+if __name__ == '__main__':
+    main()
+
+# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
+
diff --git a/setup.py b/setup.py
new file mode 100755 (executable)
index 0000000..d962e3f
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+# Copyright 2011 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 image_creator
+
+from setuptools import setup
+
+
+setup(
+    name='snf_image_creator',
+    version=image_creator.__version__,
+    description='Command line tool for creating images',
+#    long_description=open('README.rst').read(),
+    url='https://code.grnet.gr/projects/snf-image-creator',
+    license='BSD',
+    packages=['image_creator'],
+    include_package_data=True,
+    entry_points={
+        'console_scripts': ['snf-image-creator = image_creator.main:main']
+    }
+)