Commit 86c42bba authored by Nikos Skalkotos's avatar Nikos Skalkotos

Merge branch 'release-0.7' into debian-release-0.7

parents bef488b5 a413acff
2014-09-12, v0.7rc2
* Support all QEMU supported disk image formats as input media
* Detect if a Windows input media is sysprepped
* Support VirtIO driver installation in Windows
* Do a major code cleanup
* Fix bugs
2014-06-10, v0.6.2 2014-06-10, v0.6.2
* Add support for Ubuntu 14.04 * Add support for Ubuntu 14.04
* Fix a bug in Windows image creation * Fix a bug in Windows image creation
......
...@@ -704,11 +704,12 @@ def virtio(session): ...@@ -704,11 +704,12 @@ def virtio(session):
(code, choice) = d.menu( (code, choice) = d.menu(
"In this menu you can see details about the installed VirtIO " "In this menu you can see details about the installed VirtIO "
"drivers on the input media. Press <OK> to see more information " "drivers on the input media. Press <Info> to see more information "
"about a specific installed driver or <Update> to install one or " "about a specific installed driver or <Update> to install one or "
"more new drivers.", height=16, width=WIDTH, choices=choices, "more new drivers.", height=16, width=WIDTH, choices=choices,
menu_height=len(choices), cancel="Back", title="VirtIO Drivers", ok_label="Info", menu_height=len(choices), cancel="Back",
extra_button=1, extra_label="Update", default_item=default_item) title="VirtIO Drivers", extra_button=1, extra_label="Update",
default_item=default_item)
if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
return True return True
...@@ -832,13 +833,12 @@ def sysprep(session): ...@@ -832,13 +833,12 @@ def sysprep(session):
sysprep_help = "%s\n%s\n\n" % (help_title, '=' * len(help_title)) sysprep_help = "%s\n%s\n\n" % (help_title, '=' * len(help_title))
for task in syspreps: for task in syspreps:
name, descr = image.os.sysprep_info(task) name, descr, display = image.os.sysprep_info(task)
display_name = name.replace('-', ' ').capitalize() sysprep_help += "%s\n" % display
sysprep_help += "%s\n" % display_name sysprep_help += "%s\n" % ('-' * len(display))
sysprep_help += "%s\n" % ('-' * len(display_name))
sysprep_help += "%s\n\n" % wrapper.fill(" ".join(descr.split())) sysprep_help += "%s\n\n" % wrapper.fill(" ".join(descr.split()))
enabled = 1 if image.os.sysprep_enabled(task) else 0 enabled = 1 if image.os.sysprep_enabled(task) else 0
choices.append((str(index + 1), display_name, enabled)) choices.append((str(index + 1), display, enabled))
index += 1 index += 1
(code, tags) = d.checklist( (code, tags) = d.checklist(
......
...@@ -156,10 +156,13 @@ class Disk(object): ...@@ -156,10 +156,13 @@ class Disk(object):
self.out.warn("Snapshotting ignored for host bundling mode.") self.out.warn("Snapshotting ignored for host bundling mode.")
return self.file return self.file
# Examine media file
mode = os.stat(self.file).st_mode
self.out.output("Snapshotting media source ...", False) self.out.output("Snapshotting media source ...", False)
# Create a qcow2 snapshot for image files # Create a qcow2 snapshot for image files
if not stat.S_ISBLK(os.stat(self.file).st_mode): if not stat.S_ISBLK(mode):
snapshot = create_snapshot(self.file, self.tmp) snapshot = create_snapshot(self.file, self.tmp)
self._add_cleanup(os.unlink, snapshot) self._add_cleanup(os.unlink, snapshot)
self.out.success('done') self.out.success('done')
......
...@@ -292,12 +292,14 @@ class OSBase(object): ...@@ -292,12 +292,14 @@ class OSBase(object):
"""Returns information about a sysprep object""" """Returns information about a sysprep object"""
assert hasattr(obj, '_sysprep'), "Object is not a sysprep" assert hasattr(obj, '_sysprep'), "Object is not a sysprep"
SysprepInfo = namedtuple("SysprepInfo", "name description") SysprepInfo = namedtuple("SysprepInfo", "name description display")
name = obj.__name__.replace('_', '-')[1:] name = obj.__name__.replace('_', '-')[1:]
description = textwrap.dedent(obj.__doc__) description = textwrap.dedent(obj.__doc__)
display = getattr(obj, '_sysprep_display',
name.replace('-', ' ').capitalize())
return SysprepInfo(name, description) return SysprepInfo(name, description, display)
def get_sysprep_by_name(self, name): def get_sysprep_by_name(self, name):
"""Returns the sysprep object with the given name""" """Returns the sysprep object with the given name"""
......
...@@ -240,7 +240,7 @@ class Windows(OSBase): ...@@ -240,7 +240,7 @@ class Windows(OSBase):
@add_sysprep_param('virtio', 'dir', "", DESCR['virtio'], @add_sysprep_param('virtio', 'dir', "", DESCR['virtio'],
check=virtio_dir_check, hidden=True) check=virtio_dir_check, hidden=True)
@add_sysprep_param( @add_sysprep_param(
'virtio_timeout', 'posint', 300, DESCR['virtio_timeout']) 'virtio_timeout', 'posint', 900, DESCR['virtio_timeout'])
def __init__(self, image, **kargs): def __init__(self, image, **kargs):
super(Windows, self).__init__(image, **kargs) super(Windows, self).__init__(image, **kargs)
...@@ -313,20 +313,21 @@ class Windows(OSBase): ...@@ -313,20 +313,21 @@ class Windows(OSBase):
self.image.device, self.sysprep_params, self.image.device, self.sysprep_params,
namedtuple('User', 'rid name')(admin, self.usernames[admin])) namedtuple('User', 'rid name')(admin, self.usernames[admin]))
@sysprep('Disabling IPv6 privacy extensions') @sysprep('Disabling IPv6 privacy extensions',
display="Disable IPv6 privacy extensions")
def _disable_ipv6_privacy_extensions(self): def _disable_ipv6_privacy_extensions(self):
"""Disable IPv6 privacy extensions""" """Disable IPv6 privacy extensions"""
self.vm.rexec('netsh interface ipv6 set global ' self.vm.rexec('netsh interface ipv6 set global '
'randomizeidentifiers=disabled store=persistent') 'randomizeidentifiers=disabled store=persistent')
@sysprep('Disabling Teredo interface') @sysprep('Disabling Teredo interface', display="Disable Teredo")
def _disable_teredo(self): def _disable_teredo(self):
"""Disable Teredo interface""" """Disable Teredo interface"""
self.vm.rexec('netsh interface teredo set state disabled') self.vm.rexec('netsh interface teredo set state disabled')
@sysprep('Disabling ISATAP Adapters') @sysprep('Disabling ISATAP Adapters', display="Disable ISATAP")
def _disable_isatap(self): def _disable_isatap(self):
"""Disable ISATAP Adapters""" """Disable ISATAP Adapters"""
...@@ -338,7 +339,7 @@ class Windows(OSBase): ...@@ -338,7 +339,7 @@ class Windows(OSBase):
self.vm.rexec('netsh firewall set icmpsetting 8') self.vm.rexec('netsh firewall set icmpsetting 8')
@sysprep('Setting the system clock to UTC') @sysprep('Setting the system clock to UTC', display="UTC")
def _utc(self): def _utc(self):
"""Set the hardware clock to UTC""" """Set the hardware clock to UTC"""
...@@ -354,7 +355,8 @@ class Windows(OSBase): ...@@ -354,7 +355,8 @@ class Windows(OSBase):
"cmd /q /c for /f \"tokens=*\" %l in ('wevtutil el') do " "cmd /q /c for /f \"tokens=*\" %l in ('wevtutil el') do "
"wevtutil cl \"%l\"") "wevtutil cl \"%l\"")
@sysprep('Executing Sysprep on the image (may take more that 10 min)') @sysprep('Executing Sysprep on the image (may take more that 10 min)',
display="Microsoft Sysprep")
def _microsoft_sysprep(self): def _microsoft_sysprep(self):
"""Run the Microsoft System Preparation Tool. This will remove """Run the Microsoft System Preparation Tool. This will remove
system-specific data and will make the image ready to be deployed. system-specific data and will make the image ready to be deployed.
...@@ -365,7 +367,8 @@ class Windows(OSBase): ...@@ -365,7 +367,8 @@ class Windows(OSBase):
r'/quiet /generalize /oobe /shutdown', uninstall=True) r'/quiet /generalize /oobe /shutdown', uninstall=True)
self.sysprepped = True self.sysprepped = True
@sysprep('Converting the image into a KMS client', enabled=False) @sysprep('Converting the image into a KMS client', enabled=False,
display="KMS client setup")
def _kms_client_setup(self): def _kms_client_setup(self):
"""Install the appropriate KMS client setup key to the image to convert """Install the appropriate KMS client setup key to the image to convert
it to a KMS client. Computers that are running volume licensing it to a KMS client. Computers that are running volume licensing
...@@ -701,7 +704,7 @@ class Windows(OSBase): ...@@ -701,7 +704,7 @@ class Windows(OSBase):
"""Check if winexe works on the Windows VM""" """Check if winexe works on the Windows VM"""
retries = self.sysprep_params['connection_retries'].value retries = self.sysprep_params['connection_retries'].value
timeout = [2] timeout = [5]
for i in xrange(1, retries - 1): for i in xrange(1, retries - 1):
timeout.insert(0, timeout[0] * 2) timeout.insert(0, timeout[0] * 2)
...@@ -724,8 +727,9 @@ class Windows(OSBase): ...@@ -724,8 +727,9 @@ class Windows(OSBase):
log.close() log.close()
self.out.output("failed! See: `%s' for the full output" % log.name) self.out.output("failed! See: `%s' for the full output" % log.name)
if i < retries - 1: if i < retries - 1:
self.out.output("retrying ...", False) wait = timeout.pop()
time.sleep(timeout.pop()) self.out.output("retrying in %d seconds ..." % wait, False)
time.sleep(wait)
raise FatalError("Connection to the Windows VM failed after %d retries" raise FatalError("Connection to the Windows VM failed after %d retries"
% retries) % retries)
......
...@@ -23,6 +23,7 @@ import signal ...@@ -23,6 +23,7 @@ import signal
import tempfile import tempfile
import os import os
import time import time
import errno
from string import lowercase, uppercase, digits from string import lowercase, uppercase, digits
from image_creator.util import FatalError, get_kvm_binary from image_creator.util import FatalError, get_kvm_binary
...@@ -168,8 +169,9 @@ class VM(object): ...@@ -168,8 +169,9 @@ class VM(object):
if self.serial is not None: if self.serial is not None:
try: try:
os.unlink(self.serial) os.unlink(self.serial)
except FileNotFoundError: except OSError as e:
pass if errno.errorcode[e.errno] != 'ENOENT': # File not found
raise
def wait_on_serial(self, timeout): def wait_on_serial(self, timeout):
"""Wait until the random token appears on the VM's serial port""" """Wait until the random token appears on the VM's serial port"""
......
...@@ -25,9 +25,6 @@ import os ...@@ -25,9 +25,6 @@ import os
import re import re
import json import json
import tempfile import tempfile
from sh import qemu_img
from sh import qemu_nbd
from sh import modprobe
class FatalError(Exception): class FatalError(Exception):
...@@ -35,8 +32,25 @@ class FatalError(Exception): ...@@ -35,8 +32,25 @@ class FatalError(Exception):
pass pass
def get_command(command):
"""Return a file system binary command"""
def find_sbin_command(command, exception):
search_paths = ['/usr/local/sbin', '/usr/sbin', '/sbin']
for fullpath in map(lambda x: "%s/%s" % (x, command), search_paths):
if os.path.exists(fullpath) and os.access(fullpath, os.X_OK):
return sh.Command(fullpath)
raise exception
try:
return sh.__getattr__(command)
except sh.CommandNotFound as e:
return find_sbin_command(command, e)
def image_info(image): def image_info(image):
"""Returns information about an image file""" """Returns information about an image file"""
qemu_img = get_command('qemu-img')
info = qemu_img('info', '--output', 'json', image) info = qemu_img('info', '--output', 'json', image)
return json.loads(str(info)) return json.loads(str(info))
...@@ -44,6 +58,7 @@ def image_info(image): ...@@ -44,6 +58,7 @@ def image_info(image):
def create_snapshot(source, target_dir): def create_snapshot(source, target_dir):
"""Returns a qcow2 snapshot of an image file""" """Returns a qcow2 snapshot of an image file"""
qemu_img = get_command('qemu-img')
snapfd, snap = tempfile.mkstemp(prefix='snapshot-', dir=target_dir) snapfd, snap = tempfile.mkstemp(prefix='snapshot-', dir=target_dir)
os.close(snapfd) os.close(snapfd)
qemu_img('create', '-f', 'qcow2', '-o', qemu_img('create', '-f', 'qcow2', '-o',
...@@ -51,21 +66,6 @@ def create_snapshot(source, target_dir): ...@@ -51,21 +66,6 @@ def create_snapshot(source, target_dir):
return snap return snap
def get_command(command):
"""Return a file system binary command"""
def find_sbin_command(command, exception):
search_paths = ['/usr/local/sbin', '/usr/sbin', '/sbin']
for fullpath in map(lambda x: "%s/%s" % (x, command), search_paths):
if os.path.exists(fullpath) and os.access(fullpath, os.X_OK):
return sh.Command(fullpath)
raise exception
try:
return sh.__getattr__(command)
except sh.CommandNotFound as e:
return find_sbin_command(command, e)
def get_kvm_binary(): def get_kvm_binary():
"""Returns the path to the kvm binary and some extra arguments if needed""" """Returns the path to the kvm binary and some extra arguments if needed"""
...@@ -131,6 +131,8 @@ class QemuNBD(object): ...@@ -131,6 +131,8 @@ class QemuNBD(object):
self.image = image self.image = image
self.device = None self.device = None
self.pattern = re.compile('^nbd\d+$') self.pattern = re.compile('^nbd\d+$')
self.modprobe = get_command('modprobe')
self.qemu_nbd = get_command('qemu-nbd')
def _list_devices(self): def _list_devices(self):
"""Returns all the NBD block devices""" """Returns all the NBD block devices"""
...@@ -141,7 +143,7 @@ class QemuNBD(object): ...@@ -141,7 +143,7 @@ class QemuNBD(object):
devs = self._list_devices() devs = self._list_devices()
if len(devs) == 0: # Is nbd module loaded? if len(devs) == 0: # Is nbd module loaded?
modprobe('nbd', 'max_part=16') self.modprobe('nbd', 'max_part=16')
# Wait a second for /dev to be populated # Wait a second for /dev to be populated
time.sleep(1) time.sleep(1)
devs = self._list_devices() devs = self._list_devices()
...@@ -166,7 +168,7 @@ class QemuNBD(object): ...@@ -166,7 +168,7 @@ class QemuNBD(object):
args.append('-r') args.append('-r')
args.append(self.image) args.append(self.image)
qemu_nbd(*args) self.qemu_nbd(*args)
self.device = device self.device = device
return device return device
...@@ -174,7 +176,7 @@ class QemuNBD(object): ...@@ -174,7 +176,7 @@ class QemuNBD(object):
"""Disconnect the image from the connected device""" """Disconnect the image from the connected device"""
assert self.device is not None, "No device connected" assert self.device is not None, "No device connected"
qemu_nbd('-d', self.device) self.qemu_nbd('-d', self.device)
self.device = None self.device = None
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
__version__ = "0.7rc1" __version__ = "0.7rc2"
__version_vcs_info__ = { __version_vcs_info__ = {
'branch': 'release-0.7', 'branch': 'release-0.7',
'revid': '96d50cb', 'revid': '9813162',
'revno': 555} 'revno': 564}
__version_user_email__ = "skalkoto@grnet.gr" __version_user_email__ = "skalkoto@grnet.gr"
__version_user_name__ = "Nikos Skalkotos" __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