]>
git.siccegge.de Git - forks/vmdebootstrap.git/blob - vmextract.py
2 # -*- coding: utf-8 -*-
4 # Copyright 2015 Neil Williams <codehelp@debian.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 __desc__
= "Helper to mount an image read-only and extract files" + \
31 # pylint: disable=missing-docstring
34 class VmExtract(cliapp
.Application
): # pylint: disable=too-many-public-methods
36 Support for extracting useful content from VM images.
37 For example, to assist in validation.
42 version
=__version__
, description
=__desc__
, epilog
=None):
43 super(VmExtract
, self
).__init
__(
49 def add_settings(self
):
50 self
.settings
.boolean(
51 ['verbose'], 'report what is going on')
53 ['image'], 'image to read', metavar
='FILE')
55 ['directory'], 'directory to extract as a tarball.')
56 self
.settings
.string_list(
57 ['path'], 'path to the filename to extract - can repeat.')
58 self
.settings
.boolean(
59 ['boot'], 'mount the boot partition as well as root')
62 'name of tarball containing the extracted directory',
63 default
='vmextract.tgz',
66 # pylint: disable=too-many-branches,too-many-statements
67 def process_args(self
, args
):
69 if not self
.settings
['image']:
70 raise cliapp
.AppException(
71 'You must give an image to read')
72 if not self
.settings
['directory'] and not self
.settings
['path']:
73 raise cliapp
.AppException(
74 'You must provide either a filename or directory '
80 if self
.settings
['boot']:
82 if self
.settings
['directory']:
83 self
.extract_directory()
84 elif self
.settings
['path']:
85 for path
in self
.settings
['path']:
87 except BaseException
as exc
:
88 self
.message('EEEK! Something bad happened... %s' % exc
)
91 def message(self
, msg
):
93 if self
.settings
['verbose']:
100 self
.message("Preparing %s" % self
.settings
['image'])
101 self
.guest_os
= guestfs
.GuestFS(python_return_dict
=True)
102 self
.guest_os
.add_drive_opts(
103 self
.settings
['image'],
106 # ensure launch is only called once per run
107 self
.guest_os
.launch()
108 drives
= self
.guest_os
.inspect_os()
109 self
.mps
= self
.guest_os
.inspect_get_mountpoints(drives
[0])
111 def download(self
, path
):
113 Copy a single file out of the image
114 If a filename is not specified, use the basename of the original.
116 filename
= os
.path
.basename(path
)
118 "Extracting %s as %s" % (path
, filename
))
119 self
.guest_os
.download(path
, filename
)
120 if not os
.path
.exists(filename
):
121 return RuntimeError("Download failed")
123 def mount_root(self
):
125 Mounts the root partition to /
127 root
= [part
for part
in self
.mps
if part
== '/'][0]
129 raise RuntimeError("Unable to identify root partition")
130 self
.guest_os
.mount_ro(self
.mps
[root
], '/')
132 def mount_boot(self
):
134 Mounts the /boot partition to a new /boot mountpoint
136 boot
= [part
for part
in self
.mps
if part
== '/boot'][0]
138 raise RuntimeError("Unable to identify boot partition")
139 if not self
.guest_os
.is_dir('/boot'):
140 self
.guest_os
.mkmountpoint('/boot')
141 self
.guest_os
.mount_ro(self
.mps
[boot
], '/boot')
143 def extract_directory(self
):
145 Create a tarball of a complete directory existing in the image.
147 if not self
.settings
['filename']:
148 self
.settings
['filename'] = 'vmextract.tgz'
149 self
.guest_os
.tar_out(
150 self
.settings
['directory'],
151 self
.settings
['filename'], compress
='gzip')
152 if not tarfile
.is_tarfile(self
.settings
['filename']):
153 raise RuntimeError("Extraction failed")
157 VmExtract(version
=__version__
).run()
161 if __name__
== '__main__':