Source code for virtualbox.pool
"""Virtual Machine pool
=======================
The :py:class:`MachinePool` manages a pool of linked clones against a defined
"root machine". This module works with multiple processes running on the
host machine at a time. It manages a resource lock over the root virtual
machine to ensure consistency.
In this example the machine *win7* has a current version of guest editions
installed and is in a powered off state.
Create multiple clones::
pool = MachinePool('win7')
sessions = []
for i in range(3):
sessions.append(pool.acquire("Mick", "password"))
# You now have three running machines.
for session in sessions:
with session.guest.create_session("Mick", "password") as gs:
_, out, _ = gs.execute("ipconfig")
print(out)
for session in sessions:
pool.release(session)
A reliable version of the above code would look like this::
pool = MachinePool('win7')
sessions = []
try:
for i in range(3):
sessions.append(pool.acquire("Mick", "password"))
# You now have three running machines.
for session in sessions:
with session.guest.create_session("Mick", "password") as gs:
_, out, _ = gs.execute("ipconfig")
print(out)
finally:
for session in sessions:
try:
pool.release(session)
except Exception as err:
print("Error raised on release: %s" % err)
"""
from __future__ import absolute_import
from contextlib import contextmanager
import time
from virtualbox import VirtualBox
from virtualbox import Session
from virtualbox.library import LockType
from virtualbox.library import SessionState
from virtualbox.library import DeviceType
from virtualbox.library import DeviceActivity
from virtualbox.library import OleErrorUnexpected
[docs]class MachinePool(object):
"""MachinePool manages a pool of resources and enable cross process
coordination of a linked machine clone. """
def __init__(self, machine_name):
"""Create a MachinePool instance.
:param machine_name: Name of the root virtual machine.
:type machine_name: str
"""
self.machine_name = machine_name
with self._lock() as session:
machine = session.machine
if not machine.current_snapshot:
p, id_p = machine.take_snapshot('initialised',
'root machine',
False)
p.wait_for_completion(60 * 1000)
@contextmanager
def _lock(self, timeout_ms=-1):
"""Exclusive lock over root machine"""
vbox = VirtualBox()
machine = vbox.find_machine(self.machine_name)
wait_time = 0
while True:
session = Session()
try:
machine.lock_machine(session, LockType.write)
except Exception as exc:
if timeout_ms != -1 and wait_time > timeout_ms:
raise ValueError("Failed to acquire lock - %s" % exc)
time.sleep(1)
wait_time += 1000
else:
try:
yield session
finally:
session.unlock_machine()
break
@property
def _clones(self):
"""Yield all machines under this pool"""
vbox = VirtualBox()
machines = []
for machine in vbox.machines:
if machine.name == self.machine_name:
continue
if machine.name.startswith(self.machine_name):
machines.append(machine)
return machines
def _power_down(self, session):
vbox = VirtualBox()
clone = vbox.find_machine(session.machine.name)
try:
p = session.console.power_down()
p.wait_for_completion(60 * 1000)
try:
session.unlock_machine()
except OleErrorUnexpected:
# session seems to become unlocked automatically after
# wait_for_completion is called after the power_down?
pass
session = clone.create_session()
p = session.machine.restore_snapshot()
p.wait_for_completion(60 * 1000)
return clone
finally:
if session.state == SessionState.locked:
session.unlock_machine()
[docs] def acquire(self, username, password, frontend='headless'):
"""Acquire a Machine resource."""
with self._lock() as root_session:
for clone in self._clones:
# Search for a free clone
session = Session()
try:
clone.lock_machine(session, LockType.write)
except:
continue
else:
try:
p = session.machine.restore_snapshot()
p.wait_for_completion(60 * 1000)
except:
pass
session.unlock_machine()
break
else:
# Build a new clone
machine = root_session.machine
clone = machine.clone(name="%s Pool" % self.machine_name)
p = clone.launch_vm_process(type_p=frontend)
p.wait_for_completion(60 * 1000)
session = clone.create_session()
console = session.console
guest = console.guest
try:
guest_session = guest.create_session(username, password,
timeout_ms=300 * 1000)
idle_count = 0
timeout = 60
while idle_count < 5 and timeout > 0:
act = console.get_device_activity([DeviceType.hard_disk])
if act[0] == DeviceActivity.idle:
idle_count += 1
time.sleep(0.5)
timeout -= 0.5
guest_session.close()
console.pause()
p, id_p = console.machine.take_snapshot('initialised',
'machine pool',
True)
p.wait_for_completion(60 * 1000)
self._power_down(session)
finally:
if session.state == SessionState.locked:
session.unlock_machine()
# Launch our clone
p = clone.launch_vm_process(type_p=frontend)
p.wait_for_completion(60 * 1000)
session = clone.create_session()
return session
[docs] def release(self, session):
"""Release a machine session resource."""
if session.state != SessionState.locked:
return
with self._lock():
return self._power_down(session)