Revision d57775d4

b/image_creator/__init__.py
1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
__version__ = '0.1'
b/image_creator/disk.py
1
#!/usr/bin/env python
2

  
3
import losetup
4
import stat
5
import os
6
import tempfile
7
import uuid
8
import re
9
from pbs import dmsetup
10
from pbs import blockdev
11
from pbs import dd
12
from pbs import kpartx
13
from pbs import mount
14
from pbs import umount
15

  
16
class Disk(object):
17

  
18
    def __init__(self, source):
19
        self._cleanup_jobs = []
20
        self._devices = []
21
        self.source = source
22

  
23
    def _add_cleanup(self, job, *args):
24
        self._cleanup_jobs.append((job, args))
25

  
26
    def _losetup(self, fname):
27
        loop = losetup.find_unused_loop_device()
28
        loop.mount(fname)
29
        self._add_cleanup(loop.unmount)
30
        return loop.device
31

  
32
    def _dir_to_disk(self):
33
        raise NotImplementedError
34

  
35
    def cleanup(self):
36
        while len(self._cleanup_jobs):
37
            job, args = self._cleanup_jobs.pop()
38
            job(*args)
39

  
40
    def get_device(self):
41
        sourcedev = self.source
42
        mode = os.stat(self.source).st_mode
43
        if stat.S_ISDIR(mode):
44
            return self._losetup(self._dir_to_disk())
45
        elif stat.S_ISREG(mode):
46
            sourcedev = self._losetup(self.source)
47
        elif not stat.S_ISBLK(mode):
48
            raise ValueError("Value for self.source is invalid")
49

  
50
        # Take a snapshot and return it to the user
51
        size = blockdev('--getsize', sourcedev)
52
        cowfd, cow = tempfile.mkstemp()
53
        self._add_cleanup(os.unlink, cow)
54
        dd('if=/dev/zero', 'of=%s' % cow, 'count=%d' % (1024*1024))#(int(size)/4))
55
        cowdev = self._losetup(cow)
56

  
57
        snapshot = uuid.uuid4().hex
58
        tablefd, table = tempfile.mkstemp()
59
        try:
60
            os.write(tablefd, "0 %d snapshot %s %s n 8" % \
61
                                        (int(size), sourcedev, cowdev))
62
            dmsetup('create', snapshot, table)
63
            self._add_cleanup(dmsetup, 'remove', snapshot)
64
        finally:
65
            os.unlink(table)
66

  
67
        new_device = DiskDevice(self, "/dev/mapper/%s" % snapshot)
68
        self._devices.append(new_device)
69
        return new_device
70

  
71
class DiskDevice(object):
72

  
73
    def __init__(self, disk, device):
74
        self.disk = disk
75
        self.dev = device
76
        self.partitions_mapped = False
77
        self.magic_number = uuid.uuid4().hex
78

  
79
    def list_partitions(self):
80
        output = kpartx("-l", "-p", self.magic_number, self.dev)
81
        return [ "/dev/mapper/%s" % x for x in
82
                re.findall('^\S+', str(output), flags=re.MULTILINE)]
83

  
84
    def mount(self, partition):
85
        if not self.partitions_mapped:
86
            kpartx("-a", "-p", self.magic_number, self.dev)
87
            self.disk._cleanup_jobs.append(kpartx, "-d", "-p",
88
                        self.magic_number, self.dev)
89
            self.partitions_mapped = True
90

  
91
        targetfd, target = tempfile.mkdtemp()
92
        try:
93
            mount(dev, partition)
94
        except:
95
            os.rmdir(table)
96
            raise
97
        return target
98

  
99
    def unmount(self, partition):
100
        umount(target)
101

  
102
        mode = os.stat(self.source).st_mode
103
        if stat.S_ISDIR(mode):
104
            os.rmdir(target)
105

  
106
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/image_creator/main.py
1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from image_creator.disk import Disk
35
import sys
36
import os
37

  
38
def main():
39
    if len(sys.argv) != 3:
40
        sys.exit("Usage: %s <source> <output_file>" %
41
                        os.path.basename(sys.argv[0]))
42
    source = sys.argv[1]
43
    dest = sys.argv[2]
44

  
45
    disk = Disk(source)
46
    try:
47
        dev = disk.get_device()
48

  
49
    finally:
50
        disk.cleanup()
51

  
52
if __name__ == '__main__':
53
    main()
54

  
55
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
56

  
b/setup.py
1
#!/usr/bin/env python
2

  
3
# Copyright 2011 GRNET S.A. All rights reserved.
4
#
5
# Redistribution and use in source and binary forms, with or
6
# without modification, are permitted provided that the following
7
# conditions are met:
8
#
9
#   1. Redistributions of source code must retain the above
10
#      copyright notice, this list of conditions and the following
11
#      disclaimer.
12
#
13
#   2. Redistributions in binary form must reproduce the above
14
#      copyright notice, this list of conditions and the following
15
#      disclaimer in the documentation and/or other materials
16
#      provided with the distribution.
17
#
18
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
# POSSIBILITY OF SUCH DAMAGE.
30
#
31
# The views and conclusions contained in the software and
32
# documentation are those of the authors and should not be
33
# interpreted as representing official policies, either expressed
34
# or implied, of GRNET S.A.
35

  
36
import image_creator
37

  
38
from setuptools import setup
39

  
40

  
41
setup(
42
    name='snf_image_creator',
43
    version=image_creator.__version__,
44
    description='Command line tool for creating images',
45
#    long_description=open('README.rst').read(),
46
    url='https://code.grnet.gr/projects/snf-image-creator',
47
    license='BSD',
48
    packages=['image_creator'],
49
    include_package_data=True,
50
    entry_points={
51
        'console_scripts': ['snf-image-creator = image_creator.main:main']
52
    }
53
)

Also available in: Unified diff