Commit 9b892d61 authored by Nikos Skalkotos's avatar Nikos Skalkotos

Merge branch 'hotfix-0.7.4'

parents c36af7a4 532ae517
2014-11-04, v0.7.4
* Handle cases where qemu-nbd command is missing
2014-10-21, v0.7.3
* Instruct kamaki to ignore the ssl certificates (kamaki >= 0.13rc5).
......
......@@ -105,18 +105,19 @@ class WizardPage(object):
NEXT = 1
PREV = -1
def __init__(self, name, text, **kargs):
def __init__(self, name, text, **kwargs):
self.name = name
self.answer = None
self.text = text
self.print_name = kargs['print_name'] if 'print_name' in kargs \
self.print_name = kwargs['print_name'] if 'print_name' in kwargs \
else " ".join(re.findall('[A-Z][^A-Z]*', name))
self.title = kargs['title'] if 'title' in kargs else self.print_name
self.default = kargs['default'] if 'default' in kargs else ""
self.extra = kargs['extra'] if 'extra' in kargs else None
self.title = kwargs['title'] if 'title' in kwargs else self.print_name
self.default = kwargs['default'] if 'default' in kwargs else ""
self.extra = kwargs['extra'] if 'extra' in kwargs else None
self.validate = \
kargs['validate'] if 'validate' in kargs else lambda x: x
self.display = kargs['display'] if 'display' in kargs else lambda x: x
kwargs['validate'] if 'validate' in kwargs else lambda x: x
self.display = \
kwargs['display'] if 'display' in kwargs else lambda x: x
self.dargs = {}
self.dargs['ok_label'] = 'Next'
......@@ -124,10 +125,10 @@ class WizardPage(object):
self.dargs['width'] = PAGE_WIDTH
self.dargs['height'] = PAGE_HEIGHT
if 'extra' in kargs:
if 'extra' in kwargs:
self.dargs['extra_button'] = 1
self.extra_label = kargs['extra_label'] if 'extra_label' in kargs \
self.extra_label = kwargs['extra_label'] if 'extra_label' in kwargs \
else lambda: "extra"
def __str__(self):
......@@ -167,9 +168,9 @@ class WizardInfoPage(WizardPage):
The user-defined information is created by the info function.
"""
def __init__(self, name, text, info, **kargs):
def __init__(self, name, text, info, **kwargs):
"""Initialize the WizardInfoPage instance"""
super(WizardInfoPage, self).__init__(name, text, **kargs)
super(WizardInfoPage, self).__init__(name, text, **kwargs)
self.info = info
def show(self, dialog, title):
......@@ -194,9 +195,9 @@ class WizardInfoPage(WizardPage):
class WizardFormPage(WizardPage):
"""Represents a Form in a wizard"""
def __init__(self, name, text, fields, **kargs):
def __init__(self, name, text, fields, **kwargs):
"""Initialize the WizardFormPage instance"""
super(WizardFormPage, self).__init__(name, text, **kargs)
super(WizardFormPage, self).__init__(name, text, **kwargs)
self.fields = fields
def show(self, dialog, title):
......@@ -227,11 +228,11 @@ class WizardPageWthChoices(WizardPage):
the choices variable. If the choices function returns an empty list, a
fallback function is executed if available.
"""
def __init__(self, name, text, choices, **kargs):
def __init__(self, name, text, choices, **kwargs):
"""Initialize the WizardPageWthChoices instance"""
super(WizardPageWthChoices, self).__init__(name, text, **kargs)
super(WizardPageWthChoices, self).__init__(name, text, **kwargs)
self.choices = choices
self.fallback = kargs['fallback'] if 'fallback' in kargs else None
self.fallback = kwargs['fallback'] if 'fallback' in kwargs else None
class WizardRadioListPage(WizardPageWthChoices):
......
......@@ -18,7 +18,7 @@
"""Module hosting the Disk class."""
from image_creator.util import get_command, try_fail_repeat, free_space, \
FatalError, create_snapshot
FatalError, create_snapshot, image_info
from image_creator.bundle_volume import BundleVolume
from image_creator.image import Image
......@@ -80,7 +80,7 @@ class Disk(object):
self._add_cleanup(shutil.rmtree, self.tmp)
def _add_cleanup(self, job, *args):
"""Add a new job in the cleanup list"""
"""Add a new job in the cleanup list."""
self._cleanup_jobs.append((job, args))
def _losetup(self, fname):
......@@ -93,7 +93,7 @@ class Disk(object):
return loop
def _dir_to_disk(self):
"""Create a disk out of a directory"""
"""Create a disk out of a directory."""
if self.source == '/':
bundle = BundleVolume(self.out, self.meta)
image = '%s/%s.raw' % (self.tmp, uuid.uuid4().hex)
......@@ -125,7 +125,7 @@ class Disk(object):
@property
def file(self):
"""Convert the source media into a file"""
"""Convert the source media into a file."""
if self._file is not None:
return self._file
......@@ -157,19 +157,21 @@ class Disk(object):
return self.file
# Examine media file
mode = os.stat(self.file).st_mode
info = image_info(self.file)
self.out.output("Snapshotting media source ...", False)
# Create a qcow2 snapshot for image files
if not stat.S_ISBLK(mode):
# Create a qcow2 snapshot for image files that are not raw
if info['format'] != 'raw':
snapshot = create_snapshot(self.file, self.tmp)
self._add_cleanup(os.unlink, snapshot)
self.out.success('done')
return snapshot
# Create a device-mapper snapshot for block devices
size = int(blockdev('--getsz', self.file))
# Create a device-mapper snapshot for raw image files and block devices
mode = os.stat(self.file).st_mode
device = self.file if stat.S_ISBLK(mode) else self._losetup(self.file)
size = int(blockdev('--getsz', device))
cowfd, cow = tempfile.mkstemp(dir=self.tmp)
os.close(cowfd)
......@@ -183,7 +185,7 @@ class Disk(object):
try:
try:
os.write(tablefd, "0 %d snapshot %s %s n 8\n" %
(size, self.file, cowdev))
(size, device, cowdev))
finally:
os.close(tablefd)
......@@ -194,17 +196,19 @@ class Disk(object):
self.out.success('done')
return "/dev/mapper/%s" % snapshot
def get_image(self, media, **kargs):
def get_image(self, media, **kwargs):
"""Returns a newly created Image instance."""
image = Image(media, self.out, **kargs)
info = image_info(media)
image = Image(media, self.out, format=info['format'], **kwargs)
self._images.append(image)
image.enable()
return image
def destroy_image(self, image):
"""Destroys an Image instance previously created by get_image method.
"""Destroys an Image instance previously created with the get_image()
method.
"""
self._images.remove(image)
image.destroy()
......
......@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module provides the code for handling GUID partition tables"""
"""This module provides the code for handling GUID partition tables."""
import struct
import sys
......
......@@ -15,7 +15,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from image_creator.util import FatalError, QemuNBD, image_info
"""Module hosting the Image class."""
from image_creator.util import FatalError, QemuNBD
from image_creator.gpt import GPTPartitionTable
from image_creator.os_type import os_cls
......@@ -28,16 +30,16 @@ from sendfile import sendfile
class Image(object):
"""The instances of this class can create images out of block devices."""
def __init__(self, device, output, **kargs):
def __init__(self, device, output, **kwargs):
"""Create a new Image instance"""
self.device = device
self.out = output
self.info = image_info(device)
self.format = kwargs['format'] if 'format' in kwargs else 'raw'
self.meta = kargs['meta'] if 'meta' in kargs else {}
self.meta = kwargs['meta'] if 'meta' in kwargs else {}
self.sysprep_params = \
kargs['sysprep_params'] if 'sysprep_params' in kargs else {}
kwargs['sysprep_params'] if 'sysprep_params' in kwargs else {}
self.progress_bar = None
self.guestfs_device = None
......@@ -50,6 +52,10 @@ class Image(object):
# This is needed if the image format is not raw
self.nbd = QemuNBD(device)
if self.nbd.qemu_nbd is None and self.format != 'raw':
raise FatalError("qemu-nbd command is missing, only raw input "
"media are supported")
def check_guestfs_version(self, major, minor, release):
"""Checks if the version of the used libguestfs is smaller, equal or
greater than the one specified by the major, minor and release triplet
......@@ -222,11 +228,11 @@ class Image(object):
class RawImage:
"""The RawImage context manager"""
def __enter__(self):
return img.device if img.info['format'] == 'raw' else \
return img.device if img.format == 'raw' else \
img.nbd.connect(readonly)
def __exit__(self, exc_type, exc_value, traceback):
if img.info['format'] != 'raw':
if img.format != 'raw':
img.nbd.disconnect()
return RawImage()
......
......@@ -87,7 +87,7 @@ def sysprep(message, enabled=True, **kwargs):
class SysprepParam(object):
"""This class represents a system preparation parameter"""
def __init__(self, type, default, description, **kargs):
def __init__(self, type, default, description, **kwargs):
assert hasattr(self, "_check_%s" % type), "Invalid type: %s" % type
......@@ -96,8 +96,8 @@ class SysprepParam(object):
self.description = description
self.value = default
self.error = None
self.check = kargs['check'] if 'check' in kargs else lambda x: x
self.hidden = kargs['hidden'] if 'hidden' in kargs else False
self.check = kwargs['check'] if 'check' in kwargs else lambda x: x
self.hidden = kwargs['hidden'] if 'hidden' in kwargs else False
def set_value(self, value):
"""Update the value of the parameter"""
......@@ -160,11 +160,11 @@ class SysprepParam(object):
raise ValueError("Invalid dirname")
def add_sysprep_param(name, type, default, descr, **kargs):
def add_sysprep_param(name, type, default, descr, **kwargs):
"""Decorator for __init__ that adds the definition for a system preparation
parameter in an instance of an os_type class
"""
extra = kargs
extra = kwargs
def wrapper(init):
@wraps(init)
......@@ -196,7 +196,7 @@ def del_sysprep_param(name):
class OSBase(object):
"""Basic operating system class"""
def __init__(self, image, **kargs):
def __init__(self, image, **kwargs):
self.image = image
self.root = image.root
......@@ -206,8 +206,8 @@ class OSBase(object):
if not hasattr(self, 'sysprep_params'):
self.sysprep_params = {}
if 'sysprep_params' in kargs:
for key, val in kargs['sysprep_params'].items():
if 'sysprep_params' in kwargs:
for key, val in kwargs['sysprep_params'].items():
if key not in self.sysprep_params:
self.out.warn("Ignoring invalid `%s' parameter." % key)
continue
......@@ -504,7 +504,7 @@ class OSBase(object):
"""List the name of all files recursively under a directory"""
return self.image.g.find(directory)
def _foreach_file(self, directory, action, **kargs):
def _foreach_file(self, directory, action, **kwargs):
"""Perform an action recursively on all files under a directory.
The following options are allowed:
......@@ -522,16 +522,16 @@ class OSBase(object):
self.out.warn("Directory: `%s' does not exist!" % directory)
return
maxdepth = None if 'maxdepth' not in kargs else kargs['maxdepth']
maxdepth = None if 'maxdepth' not in kwargs else kwargs['maxdepth']
if maxdepth == 0:
return
# maxdepth -= 1
maxdepth = None if maxdepth is None else maxdepth - 1
kargs['maxdepth'] = maxdepth
kwargs['maxdepth'] = maxdepth
exclude = None if 'exclude' not in kargs else kargs['exclude']
ftype = None if 'ftype' not in kargs else kargs['ftype']
exclude = None if 'exclude' not in kwargs else kwargs['exclude']
ftype = None if 'ftype' not in kwargs else kwargs['ftype']
has_ftype = lambda x, y: y is None and True or x['ftyp'] == y
for f in self.image.g.readdir(directory):
......@@ -544,7 +544,7 @@ class OSBase(object):
continue
if has_ftype(f, 'd'):
self._foreach_file(full_path, action, **kargs)
self._foreach_file(full_path, action, **kwargs)
if has_ftype(f, ftype):
action(full_path)
......
......@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module hosts OS-specific code for Linux"""
"""This module hosts OS-specific code for Linux."""
from image_creator.os_type.unix import Unix, sysprep
......@@ -25,8 +25,8 @@ import time
class Linux(Unix):
"""OS class for Linux"""
def __init__(self, image, **kargs):
super(Linux, self).__init__(image, **kargs)
def __init__(self, image, **kwargs):
super(Linux, self).__init__(image, **kwargs)
self._uuid = dict()
self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
......
......@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module hosts OS-specific code for Red Hat Enterprise Linux"""
"""This module hosts OS-specific code for Red Hat Enterprise Linux."""
from image_creator.os_type.linux import Linux
......
......@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module hosts OS-specific code for Slackware Linux"""
"""This module hosts OS-specific code for Slackware Linux."""
from image_creator.os_type.linux import Linux, sysprep
......
......@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module hosts OS-specific code for Ubuntu Linux"""
"""This module hosts OS-specific code for Ubuntu Linux."""
import re
......
......@@ -22,8 +22,8 @@ from image_creator.os_type import OSBase
class Unsupported(OSBase):
"""OS class for unsupported OSs"""
def __init__(self, image, **kargs):
super(Unsupported, self).__init__(image, **kargs)
def __init__(self, image, **kwargs):
super(Unsupported, self).__init__(image, **kwargs)
def collect_metadata(self):
"""Collect metadata about the OS"""
......
......@@ -241,8 +241,8 @@ class Windows(OSBase):
check=virtio_dir_check, hidden=True)
@add_sysprep_param(
'virtio_timeout', 'posint', 900, DESCR['virtio_timeout'])
def __init__(self, image, **kargs):
super(Windows, self).__init__(image, **kargs)
def __init__(self, image, **kwargs):
super(Windows, self).__init__(image, **kwargs)
# The commit with the following message was added in
# libguestfs 1.17.18 and was backported in version 1.16.11:
......
......@@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module hosts Windows PowerShell scripts that need to be injected into
the windows image"""
the windows image."""
# Just a random 16 character long token
from image_creator.os_type.windows.vm import RANDOM_TOKEN
......
......@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This package hosts code for accessing the windows registry"""
"""This package hosts code for manipulating the windows registry."""
from image_creator.util import FatalError
......@@ -483,7 +483,7 @@ class Registry(object):
return old
def _foreach_account(self, write=False, **kargs):
def _foreach_account(self, write=False, **kwargs):
"""Performs an action on the RID node of a user or a group in the
registry, for every user/group found in the userlist/grouplist.
If userlist/grouplist is empty, it performs the action on all
......@@ -516,11 +516,12 @@ class Registry(object):
action(hive, name, rid_node)
userlist = kargs['userlist'] if 'userlist' in kargs else None
useraction = kargs['useraction'] if 'useraction' in kargs else None
userlist = kwargs['userlist'] if 'userlist' in kwargs else None
useraction = kwargs['useraction'] if 'useraction' in kwargs else None
grouplist = kargs['grouplist'] if 'grouplist' in kargs else None
groupaction = kargs['groupaction'] if 'groupaction' in kargs else None
grouplist = kwargs['grouplist'] if 'grouplist' in kwargs else None
groupaction = \
kwargs['groupaction'] if 'groupaction' in kwargs else None
if userlist is not None:
assert useraction is not None
......
......@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module provides an interface for launching windows VMs"""
"""This module provides an interface for launching windows VMs."""
import random
import subprocess
......
......@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module provides an interface for the WinEXE utility"""
"""This module provides an interface for the WinEXE utility."""
import subprocess
import time
......
......@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module provides an interface for the rsync utility"""
"""This module provides an interface for the rsync utility."""
import subprocess
import time
......
......@@ -141,7 +141,10 @@ class QemuNBD(object):
self.device = None
self.pattern = re.compile('^nbd\d+$')
self.modprobe = get_command('modprobe')
self.qemu_nbd = get_command('qemu-nbd')
try:
self.qemu_nbd = get_command('qemu-nbd')
except sh.CommandNotFound:
self.qemu_nbd = None
def _list_devices(self):
"""Returns all the NBD block devices"""
......@@ -149,6 +152,9 @@ class QemuNBD(object):
def connect(self, ro=True):
"""Connect the image to a free NBD device"""
assert self.qemu_nbd is not None, "qemu-nbd command not found"
devs = self._list_devices()
if len(devs) == 0: # Is nbd module loaded?
......
__version__ = "0.7.3"
__version__ = "0.7.4"
__version_vcs_info__ = {
'branch': 'hotfix-0.7.3',
'revid': '5de748e',
'revno': 586}
'branch': 'hotfix-0.7.4',
'revid': '430b262',
'revno': 593}
__version_user_email__ = "skalkoto@grnet.gr"
__version_user_name__ = "Nikos Skalkotos"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment