Change in vdsm[master]: virt: Distinguish between switching to and entering post-cop...
by mzamazal@redhat.com
Milan Zamazal has uploaded a new change for review.
Change subject: virt: Distinguish between switching to and entering post-copy migration
......................................................................
virt: Distinguish between switching to and entering post-copy migration
Switching to migration post-copy mode has two phases: Asking for
switching to post-copy and actually switching to post-copy. The
transition period should be short (if it is not then it is a
QEMU/libvirt bug), so we needn't care about it much.
But on various checks we should distinguish between the post-copy
request phase and the post-copy switch phase. For instance, we should
block canceling migrations very early to avoid timing issues and
possibly canceling a post-copy migration. On the other hand we should
report a true VM state to Engine, so we can report post-copy only after
the VM actually enters it.
Change-Id: I88dddeab84a609ebd3d5d9139724adff9cdb1352
Signed-off-by: Milan Zamazal <mzamazal(a)redhat.com>
Bug-Url: https://bugzilla.redhat.com/1354343
---
M vdsm/virt/migration.py
M vdsm/virt/vm.py
2 files changed, 29 insertions(+), 13 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/43/64143/7
diff --git a/vdsm/virt/migration.py b/vdsm/virt/migration.py
index df478cd..2c73375 100644
--- a/vdsm/virt/migration.py
+++ b/vdsm/virt/migration.py
@@ -150,9 +150,9 @@
return self._mode == MODE_FILE
@property
- def in_post_copy(self):
+ def post_copy_initiated(self):
return (self._monitorThread is not None and
- self._monitorThread.in_post_copy)
+ self._monitorThread.post_copy_initiated)
def getStat(self):
"""
@@ -644,7 +644,7 @@
self.progress = None
self._conv_schedule = conv_schedule
self._use_conv_schedule = use_conv_schedule
- self._in_post_copy = False
+ self._post_copy_initiated = False
self.downtime_thread = _FakeThreadInterface()
self._thread = concurrent.thread(self.run)
@@ -659,8 +659,8 @@
return MonitorThread._MIGRATION_MONITOR_INTERVAL > 0
@property
- def in_post_copy(self):
- return self._in_post_copy
+ def post_copy_initiated(self):
+ return self._post_copy_initiated
@utils.traceback()
def run(self):
@@ -709,7 +709,7 @@
progress = Progress.from_job_stats(job_stats)
now = time.time()
- if self._in_post_copy:
+ if self._post_copy_initiated:
# Post-copy mode is a final state of a migration -- it either
# completes or fails and stops the VM, there is no way to
# continue with the migration in either case. So we won't
@@ -718,8 +718,15 @@
# abort action after the post-copy action in the schedule, for
# the case when it's not possible to switch to the post-copy
# mode for some reason.
- self._vm.log.debug('Post-copy migration still in progress: %d',
- progress.data_remaining)
+ if self._vm.in_post_copy:
+ # If in_post-copy flag is false then we are in the interim
+ # phase (which should be short) between initiating the
+ # post-copy migration and the actual start of the post-copy
+ # migration. Nothing needs to be done in that case.
+ self._vm.log.debug(
+ 'Post-copy migration still in progress: %d',
+ progress.data_remaining
+ )
elif not self._use_conv_schedule and\
(0 < migrationMaxTime < now - self._startTime):
self._vm.log.warn('The migration took %d seconds which is '
@@ -741,7 +748,7 @@
' Refer to RHBZ#919201.',
progress.data_remaining / Mbytes, lowmark / Mbytes)
- if not self._in_post_copy and\
+ if not self._post_copy_initiated and\
lastDataRemaining is not None and\
lastDataRemaining < progress.data_remaining:
iterationCount += 1
@@ -802,8 +809,8 @@
vm.log.info('Switching to post-copy migration')
ret = vm.switch_migration_to_post_copy()
if ret >= 0:
- self._in_post_copy = True
- if not self._in_post_copy:
+ self._post_copy_initiated = True
+ if not self._post_copy_initiated:
# Do nothing for now; the next action will be invoked after
# a while
vm.log.warn('Failed to switch to post-copy migration')
diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index 0f2ece8..f806c45 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -309,6 +309,7 @@
self._numaInfo = {}
self._vmJobs = None
self._clientPort = ''
+ self._in_post_copy = False
@property
def start_time(self):
@@ -317,6 +318,10 @@
@property
def domain(self):
return self._domain
+
+ @property
+ def in_post_copy(self):
+ return self._in_post_copy
def _get_lastStatus(self):
# note that we don't use _statusLock here. One of the reasons is the
@@ -1199,7 +1204,7 @@
if self.lastStatus == vmstatus.DOWN:
stats.update(self._getDownVmStats())
else:
- if self.isMigrating() and self._migrationSourceThread.in_post_copy:
+ if self.isMigrating() and self._in_post_copy:
# Stats are on the destination during post-copy migration,
# except for migration progress, which is always on the source.
stats['migrationProgress'] = self._get_vm_migration_progress()
@@ -1428,7 +1433,8 @@
def migrateCancel(self):
with self._post_copy_lock:
- if self._migrationSourceThread.in_post_copy:
+ if self._migrationSourceThread.post_copy_initiated or \
+ self._in_post_copy:
return response.error(
'migCancelErr',
message=("Can't cancel migration in post-copy mode")
@@ -4181,6 +4187,9 @@
pass
else:
hooks.after_vm_pause(domxml, self.conf)
+ elif detail == libvirt.VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY:
+ self._in_post_copy = True
+ self.log.debug("Migration entered post-copy mode")
elif detail == libvirt.VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY_FAILED:
# This can happen on both the ends of the migration.
# After a failed post-copy migration, the VM remains in a
--
To view, visit https://gerrit.ovirt.org/64143
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I88dddeab84a609ebd3d5d9139724adff9cdb1352
Gerrit-PatchSet: 7
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Milan Zamazal <mzamazal(a)redhat.com>
Gerrit-Reviewer: Francesco Romani <fromani(a)redhat.com>
Gerrit-Reviewer: gerrit-hooks <automation(a)ovirt.org>
7 years, 5 months
Change in vdsm[master]: virt: Stop guest agent before switching to post-copy
by mzamazal@redhat.com
Milan Zamazal has uploaded a new change for review.
Change subject: virt: Stop guest agent before switching to post-copy
......................................................................
virt: Stop guest agent before switching to post-copy
Post-copy migration pauses the VM on the source. We don't want to get
spurious log messages from the guest agent about not being able to talk
to the VM or being disconnected from the communication socket. So we
stop the guest agent before switching to post-copy mode.
If switching to post-copy fails (which should rarely happen), we start
the agent again. Starting the agent may fail, e.g. in case the VM got
destroyed in the meantime. So we catch the contingent exception.
Change-Id: Ibd855ee36847b293009ac35d8d03b01521aaace8
Signed-off-by: Milan Zamazal <mzamazal(a)redhat.com>
Bug-Url: https://bugzilla.redhat.com/1354343
---
M vdsm/virt/migration.py
1 file changed, 7 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/45/64145/7
diff --git a/vdsm/virt/migration.py b/vdsm/virt/migration.py
index 2c73375..522a4fc 100644
--- a/vdsm/virt/migration.py
+++ b/vdsm/virt/migration.py
@@ -807,6 +807,7 @@
vm._dom.migrateSetMaxDowntime(downtime, 0)
elif action == CONVERGENCE_SCHEDULE_POST_COPY:
vm.log.info('Switching to post-copy migration')
+ vm.guestAgent.stop()
ret = vm.switch_migration_to_post_copy()
if ret >= 0:
self._post_copy_initiated = True
@@ -814,6 +815,12 @@
# Do nothing for now; the next action will be invoked after
# a while
vm.log.warn('Failed to switch to post-copy migration')
+ try:
+ vm.guestAgent.start()
+ except:
+ self.log.exception("Failed to start guest agent after "
+ "unsuccessful switch to post-copy "
+ "migration")
elif action == CONVERGENCE_SCHEDULE_SET_ABORT:
vm.log.warn('Aborting migration')
vm._dom.abortJob()
--
To view, visit https://gerrit.ovirt.org/64145
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibd855ee36847b293009ac35d8d03b01521aaace8
Gerrit-PatchSet: 7
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Milan Zamazal <mzamazal(a)redhat.com>
Gerrit-Reviewer: Francesco Romani <fromani(a)redhat.com>
Gerrit-Reviewer: Milan Zamazal <mzamazal(a)redhat.com>
Gerrit-Reviewer: gerrit-hooks <automation(a)ovirt.org>
7 years, 5 months
Change in vdsm[master]: virt: Use a variable for self._vm in MonitorThread action ha...
by mzamazal@redhat.com
Milan Zamazal has uploaded a new change for review.
Change subject: virt: Use a variable for self._vm in MonitorThread action handling
......................................................................
virt: Use a variable for self._vm in MonitorThread action handling
This is a reformatting patch to prevent further line breaking in a
followup patch.
Change-Id: I3e41f8b3ef27319c51e18ad2c75bbb3c61b61c76
Signed-off-by: Milan Zamazal <mzamazal(a)redhat.com>
---
M vdsm/virt/migration.py
1 file changed, 6 insertions(+), 6 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/53/63553/9
diff --git a/vdsm/virt/migration.py b/vdsm/virt/migration.py
index 4633d47..6cd88f4 100644
--- a/vdsm/virt/migration.py
+++ b/vdsm/virt/migration.py
@@ -783,19 +783,19 @@
def _execute_action_with_params(self, action_with_params):
action = str(action_with_params['name'])
+ vm = self._vm
if action == CONVERGENCE_SCHEDULE_SET_DOWNTIME:
downtime = int(action_with_params['params'][0])
- self._vm.log.debug('Setting downtime to %d',
- downtime)
- self._vm._dom.migrateSetMaxDowntime(downtime, 0)
+ vm.log.debug('Setting downtime to %d', downtime)
+ vm._dom.migrateSetMaxDowntime(downtime, 0)
elif action == CONVERGENCE_SCHEDULE_POST_COPY:
if not self._vm.switch_migration_to_post_copy():
# Do nothing for now; the next action will be invoked after a
# while
- self._vm.log.warn('Failed to switch to post-copy migration')
+ vm.log.warn('Failed to switch to post-copy migration')
elif action == CONVERGENCE_SCHEDULE_SET_ABORT:
- self._vm.log.warn('Aborting migration')
- self._vm._dom.abortJob()
+ vm.log.warn('Aborting migration')
+ vm._dom.abortJob()
self.stop()
--
To view, visit https://gerrit.ovirt.org/63553
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I3e41f8b3ef27319c51e18ad2c75bbb3c61b61c76
Gerrit-PatchSet: 9
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Milan Zamazal <mzamazal(a)redhat.com>
Gerrit-Reviewer: Francesco Romani <fromani(a)redhat.com>
Gerrit-Reviewer: gerrit-hooks <automation(a)ovirt.org>
7 years, 5 months
Change in vdsm[master]: virt: Refuse to cancel post-copy migrations
by mzamazal@redhat.com
Milan Zamazal has uploaded a new change for review.
Change subject: virt: Refuse to cancel post-copy migrations
......................................................................
virt: Refuse to cancel post-copy migrations
A VM in post-copy migration is split between the source and the
destination, with inconsistent memory state on both the ends of the
migration. Stopping the migration in that phase is not a good idea, it
loses the VM and leaves it in the pause state on the source and may lead
to data loss.
Since it's not possible to stop a post-copy migration safely and the
migration is guaranteed to finish sooner or later, we prevent its
cancellation with this patch.
Change-Id: I670ab48ed3fe69582646675d8b683bcdb3cfa038
Signed-off-by: Milan Zamazal <mzamazal(a)redhat.com>
---
M vdsm/virt/vm.py
1 file changed, 25 insertions(+), 17 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/54/63554/10
diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index b5a92cb..11cacc9 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -274,6 +274,7 @@
self._guestEventTime = 0
self._guestCpuRunning = False
self._guestCpuLock = threading.Lock()
+ self._post_copy_lock = threading.Lock()
self._startTime = time.time() - \
float(self.conf.pop('elapsedTimeOffset', 0))
@@ -1447,20 +1448,26 @@
return self._migrationSourceThread.getStat()
def migrateCancel(self):
- self._acquireCpuLockWithTimeout()
- try:
- self._migrationSourceThread.stop()
- self._migrationSourceThread.status['status']['message'] = \
- 'Migration process cancelled'
- return self._migrationSourceThread.status
- except libvirt.libvirtError as e:
- if e.get_error_code() == libvirt.VIR_ERR_OPERATION_INVALID:
+ with self._post_copy_lock:
+ if self.post_copy:
+ return response.error(
+ 'migCancelErr',
+ message=("Can't cancel migration in post-copy mode")
+ )
+ self._acquireCpuLockWithTimeout()
+ try:
+ self._migrationSourceThread.stop()
+ self._migrationSourceThread.status['status']['message'] = \
+ 'Migration process cancelled'
+ return self._migrationSourceThread.status
+ except libvirt.libvirtError as e:
+ if e.get_error_code() == libvirt.VIR_ERR_OPERATION_INVALID:
+ return response.error('migCancelErr')
+ raise
+ except virdomain.NotConnectedError:
return response.error('migCancelErr')
- raise
- except virdomain.NotConnectedError:
- return response.error('migCancelErr')
- finally:
- self._guestCpuLock.release()
+ finally:
+ self._guestCpuLock.release()
def _getSerialConsole(self):
"""
@@ -1512,10 +1519,11 @@
corresponding libvirt event.
"""
self.log.info('Switching to post-copy migration')
- result = self._dom.migrateStartPostCopy(0)
- success = result >= 0
- if success:
- self._post_copy = True
+ with self._post_copy_lock:
+ result = self._dom.migrateStartPostCopy(0)
+ success = result >= 0
+ if success:
+ self._post_copy = True
return success
def _customDevices(self):
--
To view, visit https://gerrit.ovirt.org/63554
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I670ab48ed3fe69582646675d8b683bcdb3cfa038
Gerrit-PatchSet: 10
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Milan Zamazal <mzamazal(a)redhat.com>
Gerrit-Reviewer: Francesco Romani <fromani(a)redhat.com>
Gerrit-Reviewer: Milan Zamazal <mzamazal(a)redhat.com>
Gerrit-Reviewer: gerrit-hooks <automation(a)ovirt.org>
7 years, 5 months
Change in vdsm[master]: virt: Report paused VM status in post-copy
by mzamazal@redhat.com
Milan Zamazal has uploaded a new change for review.
Change subject: virt: Report paused VM status in post-copy
......................................................................
virt: Report paused VM status in post-copy
When a migration enters a post-copy mode, it gets paused. While we are
still in migration source status from the point of view of Vdsm, Engine
wants to know the actual libvirt status, i.e. paused. Thus we report
the VM as paused when it is in post-copy mode.
Change-Id: I4738a7519f8d1af2ee30bfe766f3407853263cf2
Signed-off-by: Milan Zamazal <mzamazal(a)redhat.com>
Bug-Url: https://bugzilla.redhat.com/1354343
---
M vdsm/virt/vm.py
1 file changed, 5 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/47/64147/7
diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index 2fb498a..195453f 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -1347,6 +1347,11 @@
return self._guestEvent
return vmstatus.UP
+ if self.lastStatus == vmstatus.MIGRATION_SOURCE and self.in_post_copy:
+ # We are still in MIGRATION_SOURCE state, but Engine developers
+ # prefer to get the actual libvirt state, which is PAUSED during
+ # post-copy migration (until it switches to DOWN).
+ return vmstatus.PAUSED
statuses = (vmstatus.SAVING_STATE, vmstatus.RESTORING_STATE,
vmstatus.MIGRATION_SOURCE, vmstatus.MIGRATION_DESTINATION,
vmstatus.PAUSED, vmstatus.DOWN)
--
To view, visit https://gerrit.ovirt.org/64147
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4738a7519f8d1af2ee30bfe766f3407853263cf2
Gerrit-PatchSet: 7
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Milan Zamazal <mzamazal(a)redhat.com>
Gerrit-Reviewer: Arik Hadas <ahadas(a)redhat.com>
Gerrit-Reviewer: Francesco Romani <fromani(a)redhat.com>
Gerrit-Reviewer: gerrit-hooks <automation(a)ovirt.org>
7 years, 5 months
Change in vdsm[master]: sos: replace dumpStorageTable with dump_volume_chains
by igoihman@redhat.com
Irit Goihman has uploaded a new change for review.
Change subject: sos: replace dumpStorageTable with dump_volume_chains
......................................................................
sos: replace dumpStorageTable with dump_volume_chains
dumpStorageTable is old and uses vdscli and has been replaced with
dump_volume_chains which uses jsonrpcvdscli
Change-Id: I73a85e6e720b61da1673af7161a21589ade79831
Signed-off-by: Irit Goihman <igoihman(a)redhat.com>
---
M vdsm/sos/vdsm.py.in
1 file changed, 6 insertions(+), 6 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/28/62628/1
diff --git a/vdsm/sos/vdsm.py.in b/vdsm/sos/vdsm.py.in
index 09ebb78..998c5a1 100644
--- a/vdsm/sos/vdsm.py.in
+++ b/vdsm/sos/vdsm.py.in
@@ -60,6 +60,7 @@
config = _importVdsmPylibModule("vdsm.config").config
jsonrpcvdscli = _importVdsmPylibModule("vdsm.jsonrpcvdscli")
+dump_volume_chains = _importVdsmPylibModule("vdsm.tool.dump_volume_chains")
class vdsm(Plugin, RedHatPlugin):
@@ -151,12 +152,11 @@
self.addObjectAsFile(
cli.getSpmStatus(pool), "getSpmStatus " + pool)
- self.collectExtOutput(
- '/bin/su vdsm -s %s %s/dumpStorageTable.pyc' % (
- '@PYTHON@',
- '@VDSMDIR@',
- )
- )
+ sd_uuid, = cli.getStorageDomainsList()["items"]
+
+ self.addObjectAsFile(
+ dump_volume_chains.dump_chains("dump-volume-chains", sd_uuid),
+ "dump_volume_chains")
def _addVdsmRunDir(self):
"""Add everything under /var/run/vdsm except possibly confidential
--
To view, visit https://gerrit.ovirt.org/62628
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I73a85e6e720b61da1673af7161a21589ade79831
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Irit Goihman <igoihman(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: stomp: reroute messages to different process
by Piotr Kliczewski
Piotr Kliczewski has uploaded a new change for review.
Change subject: stomp: reroute messages to different process
......................................................................
stomp: reroute messages to different process
When a client subscribes it can define which messages should be rerouted
to it based on a custom stomp header. Whenever vdsm receives message for
a verb specified it sends it to that client instead of processing it.
Change-Id: I622cb7f3b39a19314b7de4c325a62fa47faeaa4d
Signed-off-by: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
---
M lib/yajsonrpc/stomp.py
M lib/yajsonrpc/stompreactor.py
M tests/stompAdapterTests.py
3 files changed, 161 insertions(+), 16 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/46/65846/1
diff --git a/lib/yajsonrpc/stomp.py b/lib/yajsonrpc/stomp.py
index 558c519..99d09ac 100644
--- a/lib/yajsonrpc/stomp.py
+++ b/lib/yajsonrpc/stomp.py
@@ -563,6 +563,7 @@
self._valid = True
self._message_handler = message_handler
self._destination = destination
+ self._redirects = []
def handle_message(self, frame):
self._message_handler(self, frame)
@@ -576,6 +577,13 @@
def set_message_handler(self, handler):
self._message_handler = handler
+ def set_redirects(self, redirects):
+ self._redirects = redirects
+
+ @property
+ def redirects(self):
+ return self._redirects
+
@property
def id(self):
return self._subid
diff --git a/lib/yajsonrpc/stompreactor.py b/lib/yajsonrpc/stompreactor.py
index 42d7d01..61bb98c 100644
--- a/lib/yajsonrpc/stompreactor.py
+++ b/lib/yajsonrpc/stompreactor.py
@@ -15,7 +15,7 @@
from __future__ import absolute_import
import logging
-from collections import deque
+from collections import deque, defaultdict
from uuid import uuid4
import functools
@@ -68,6 +68,7 @@
self._outbox = deque()
self._sub_dests = sub_map
self._req_dest = req_dest
+ self._redirects = defaultdict(list)
self._sub_ids = {}
request_queues = config.get('addresses', 'request_queues')
self.request_queues = request_queues.split(",")
@@ -128,6 +129,7 @@
self.log.info("Subscribe command received")
destination = frame.headers.get("destination", None)
sub_id = frame.headers.get("id", None)
+ redirect_value = frame.headers.get("redirect", None)
if not destination or not sub_id:
self._send_error("Missing destination or subscription id header",
@@ -145,6 +147,12 @@
self._sub_dests[destination].append(subscription)
self._sub_ids[sub_id] = subscription
+
+ if redirect_value:
+ redirects = redirect_value.split(",")
+ subscription.set_redirects(redirects)
+ for redirect in redirects:
+ self._redirects[redirect].append(subscription)
def _send_error(self, msg, connection):
res = stomp.Frame(
@@ -188,17 +196,26 @@
subs = self._sub_dests[subscription.destination]
if len(subs) == 1:
del self._sub_dests[subscription.destination]
+ self._remove_redirects(subscription)
else:
if subscription in subs:
subs.remove(subscription)
+ self._remove_redirects(subscription)
+
+ def _remove_redirects(self, subscription):
+ if subscription.redirects:
+ for redirect in subscription.redirects:
+ reds = self._redirects[redirect]
+ if len(reds) == 1:
+ del self._redirects[redirect]
+ else:
+ reds.remove(subscription)
def _cmd_send(self, dispatcher, frame):
destination = frame.headers.get(stomp.Headers.DESTINATION, None)
if destination in self.request_queues:
# default subscription
- self._handle_internal(dispatcher,
- frame.headers.get(stomp.Headers.REPLY_TO),
- frame.body)
+ self._handle_internal(dispatcher, frame)
return
else:
try:
@@ -224,31 +241,42 @@
)
subscription.client.send_raw(res)
- def _handle_internal(self, dispatcher, req_dest, request):
+ def _handle_internal(self, dispatcher, frame):
"""
We need to build response dictionary which maps message id
with destination. For legacy mode we use known 3.5 destination
or for standard mode we use 'reply-to' header.
"""
+ requests = []
try:
- self._handle_destination(dispatcher, req_dest, json.loads(request))
+ req_dest = frame.headers.get(stomp.Headers.REPLY_TO)
+ requests = self._handle_destination(dispatcher, req_dest,
+ frame, json.loads(frame.body))
except Exception:
# let json server process issue
pass
- dispatcher.connection.handleMessage(request)
+ for request in requests:
+ dispatcher.connection.handleMessage(request)
- def _handle_destination(self, dispatcher, req_dest, request):
+ def _handle_destination(self, dispatcher, req_dest, frame, request):
"""
We could receive single message or batch of messages. We need
to build response map for each message.
"""
if isinstance(request, list):
- map(functools.partial(self._handle_destination, dispatcher,
- req_dest),
- request)
- return
+ results = map(functools.partial(self._handle_destination,
+ dispatcher, req_dest, frame),
+ request)
+ return results
+
+ if request.get("method") in self._redirects.keys():
+ subscriptions = self._redirects[request.get("method")]
+ for subscription in subscriptions:
+ subscription.client.send_raw(frame)
+ return []
self._req_dest[request.get("id")] = req_dest
+ return [request]
def handle_frame(self, dispatcher, frame):
try:
@@ -318,7 +346,7 @@
"""
Sends message to all subscribes that subscribed to destination.
"""
- def send(self, message, destination):
+ def send(self, message, destination='jms.topic.vdsm_responses'):
resp = json.loads(message)
response_id = resp.get("id")
diff --git a/tests/stompAdapterTests.py b/tests/stompAdapterTests.py
index 610c722..8f98391 100644
--- a/tests/stompAdapterTests.py
+++ b/tests/stompAdapterTests.py
@@ -17,7 +17,7 @@
#
# Refer to the README and COPYING files for full details of the license
#
-from collections import defaultdict
+from collections import defaultdict, deque
from testlib import VdsmTestCase as TestCaseBase
from yajsonrpc import JsonRpcRequest
@@ -36,6 +36,10 @@
def send_raw(self, msg):
self._client.queue_frame(msg)
+
+ @property
+ def queue(self):
+ return self._client._outbox
def handleMessage(self, data):
self._client.queue_frame(data)
@@ -59,6 +63,7 @@
def __init__(self, destination, id):
self._destination = destination
self._id = id
+ self._redirects = []
def set_client(self, client):
self._client = TestConnection(client)
@@ -74,6 +79,29 @@
@property
def client(self):
return self._client
+
+ def set_redirects(self, redirects):
+ self._redirects = redirects
+
+ @property
+ def redirects(self):
+ return self._redirects
+
+
+class TestClient(object):
+
+ def __init__(self):
+ self._outbox = deque()
+
+ def queue_frame(self, frame):
+ self._outbox.append(frame)
+
+ def pop_message(self):
+ return self._outbox.popleft()
+
+ @property
+ def has_outgoing_messages(self):
+ return (len(self._outbox) > 0)
class ConnectFrameTest(TestCaseBase):
@@ -255,7 +283,7 @@
data = adapter.pop_message()
self.assertIsNot(data, None)
- request = JsonRpcRequest.decode(data)
+ request = JsonRpcRequest.fromRawObject(data)
self.assertEquals(request.method, 'Host.getAllVmStats')
self.assertTrue(len(ids) == 1)
@@ -278,7 +306,7 @@
data = adapter.pop_message()
self.assertIsNot(data, None)
- request = JsonRpcRequest.decode(data)
+ request = JsonRpcRequest.fromRawObject(data)
self.assertEquals(request.method, 'Host.getAllVmStats')
self.assertTrue(len(ids) == 1)
@@ -337,3 +365,84 @@
resp_frame = adapter.pop_message()
self.assertEquals(resp_frame.command, Command.MESSAGE)
+
+
+class RedirectTests(TestCaseBase):
+
+ def test_subscription(self):
+ frame = Frame(Command.SUBSCRIBE,
+ {'ack': 'auto',
+ Headers.DESTINATION: 'jms.queue.events',
+ 'id': '1',
+ 'redirect': 'VM.create'})
+ sub_map = defaultdict(list)
+
+ adapter = StompAdapterImpl(Reactor(), sub_map, {})
+ adapter.handle_frame(TestDispatcher(adapter), frame)
+
+ sub = sub_map['jms.queue.events']
+
+ self.assertEquals(len(sub), 1)
+ subscrption = sub[0]
+ self.assertEquals(subscrption.id, '1')
+
+ redirects = subscrption.redirects
+ self.assertEquals(len(redirects), 1)
+ self.assertEquals(redirects, ['VM.create'])
+ self.assertEquals(len(adapter._redirects), 1)
+
+ def test_unsubscribe(self):
+ frame = Frame(Command.UNSUBSCRIBE,
+ {'id': '1'})
+
+ subscription = TestSubscription('jms.queue.events',
+ '1')
+ subscription.set_redirects(['VM.create'])
+ sub_map = defaultdict(list)
+ sub_map['jms.queue.events'].append(subscription)
+
+ adapter = StompAdapterImpl(Reactor(), sub_map, {})
+ adapter._sub_ids['1'] = subscription
+ adapter._redirects['VM.create'].append(subscription)
+
+ adapter.handle_frame(TestDispatcher(adapter), frame)
+
+ self.assertTrue(len(adapter._redirects) == 0)
+ self.assertTrue(len(adapter._sub_ids) == 0)
+ self.assertTrue(len(sub_map) == 0)
+
+ def test_redirect(self):
+ frame = Frame(command=Command.SEND,
+ headers={Headers.DESTINATION: 'jms.topic.vdsm_requests',
+ Headers.REPLY_TO: 'jms.topic.vdsm_responses',
+ Headers.CONTENT_LENGTH: '103'},
+ body=('{"jsonrpc":"2.0","method":"VM.create",'
+ '"params":{},"id":"e8a936a6-d886-4cfa-97b9-2d54209'
+ '053ff"}'
+ )
+ )
+
+ subscription = TestSubscription('jms.topic.vdsm_requests',
+ '1')
+ client = TestClient()
+ subscription.set_client(client)
+
+ subscription.set_redirects(['VM.create'])
+ sub_map = defaultdict(list)
+ sub_map['jms.topic.vdsm_requests'].append(subscription)
+ ids = {}
+
+ adapter = StompAdapterImpl(Reactor(), sub_map, ids)
+ adapter._sub_ids['1'] = subscription
+ adapter._redirects['VM.create'].append(subscription)
+
+ adapter.handle_frame(TestDispatcher(adapter), frame)
+
+ self.assertFalse(adapter.has_outgoing_messages)
+ self.assertTrue(len(ids) == 0)
+
+ self.assertTrue(client.has_outgoing_messages)
+ data = client.pop_message()
+ request = JsonRpcRequest.decode(data.body)
+ self.assertEquals(request.method, 'VM.create')
+ self.assertEquals(request.id, 'e8a936a6-d886-4cfa-97b9-2d54209053ff')
--
To view, visit https://gerrit.ovirt.org/65846
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I622cb7f3b39a19314b7de4c325a62fa47faeaa4d
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
7 years, 5 months
Change in vdsm[master]: doc: add basic quickstart for containers
by fromani@redhat.com
Francesco Romani has uploaded a new change for review.
Change subject: doc: add basic quickstart for containers
......................................................................
doc: add basic quickstart for containers
Change-Id: I0fb768ea97dd719cde9bd5e57e1b7cabe4b0f0ae
Signed-off-by: Francesco Romani <fromani(a)redhat.com>
---
M README
1 file changed, 57 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/24/59824/1
diff --git a/README b/README
index 955dfa9..370028e 100644
--- a/README
+++ b/README
@@ -32,6 +32,63 @@
The 'vdsm.spec' file demonstrates how to distribute Vdsm as an RPM
package.
+Containers support
+==================
+
+While Vdsm focus is on managing KVM virtual machines, it could also run
+containers alongside virtual machines using popular formats and runtimes,
+such as rkt and appc images.
+
+Containers are reported as special-purpose VMs to the clients, and responds
+to the Vdsm API invoked on them.
+If a particular container runtime doesn't support an operation, this will
+fail with a standard Vdsm error.
+
+To try this out, you just need to install the 'vdsm-containers subpackage'.
+Make sure to restart *both* supervdsmd and vdsmd once that package is installed.
+You'll also need to have the container runtime you wish to use installed on
+the same host which runs Vdsm. The supported runtimes are:
+
+- rkt (main supported runtime)
+- runc (work in progress)
+- docker (very experimental)
+
+The supported image container formats depend on the container runtime.
+For example, modern rkt could run docker images transparently.
+
+To check if the Vdsm is properly configured to run containers, just do:
+
+# vdsClient -s 0 getVdsCaps | grep containers
+
+ containers = ['rkt']
+
+This means that this Vdsm could also run containers using `rkt`.
+
+Any Engine >= 3.6 could handle containers - they are just VMs from its perspective.
+You just need to set few custom properties. Run this command
+on your Engine host:
+
+# engine-config -s UserDefinedVMProperties='volumeMap=^[a-zA-Z_-]+:[a-zA-Z_-]+$;containerImage=^[a-zA-Z]+(://|)[a-zA-Z]+$;containerType=^(rkt|docker)$' --cver=3.6
+
+replace --cver=3.6 with the version of the Engine you are using.
+Now restart Ovirt Engine, and log in.
+
+You can now run any container. The user defined VM properties define
+the key settings which are not (yet) exposed in the engine UI.
+
+- volumeMap allows you to mount any disk inside the container, should you
+ need any persistence. It is a mapping between disks (e.g. vda)
+ and mountpoint (e.g. data). The mountpoints are just container-dependent labels.
+
+- containerImage is the URL or path of any container image supported by your
+ runtime. E.g. 'docker://redis'
+
+- containerType allows to select the runtime you want to use (e.g. rkt)
+
+Please be aware that many settings are ignored by containers, like all
+the device configurations. Only memory and CPU settings are honoured.
+
+
Getting Help
============
--
To view, visit https://gerrit.ovirt.org/59824
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I0fb768ea97dd719cde9bd5e57e1b7cabe4b0f0ae
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Francesco Romani <fromani(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: tests: containers: add testsuite
by fromani@redhat.com
Francesco Romani has uploaded a new change for review.
Change subject: tests: containers: add testsuite
......................................................................
tests: containers: add testsuite
Change-Id: I27ba3cecbd71b7bbba94992d6bc63ca29333e313
WIP: more tests are being ported
Signed-off-by: Francesco Romani <fromani(a)redhat.com>
---
M configure.ac
M tests/Makefile.am
A tests/containers/Makefile.am
A tests/containers/__init__.py
A tests/containers/cgroups_test.py
A tests/containers/conttestlib.py
A tests/containers/data/bridge_down.xml
A tests/containers/data/bridge_no_source.xml
A tests/containers/data/disk_dev.xml
A tests/containers/data/disk_file_malformed.xml
A tests/containers/data/full_dom.xml
A tests/containers/data/metadata_drive_map.xml
A tests/containers/data/minimal_dom.xml
A tests/containers/data/only_disk.xml
A tests/containers/data/only_mem.xml
A tests/containers/errors_test.py
A tests/containers/fake/bin/docker
A tests/containers/fake/bin/rkt
A tests/containers/fake/bin/systemctl
A tests/containers/fake/bin/systemd-run
A tests/containers/fake/cgroups.tgz
A tests/containers/fs_test.py
A tests/containers/monkey.py
A tests/containers/xmlfile_test.py
24 files changed, 1,083 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/78/60678/1
diff --git a/configure.ac b/configure.ac
index c1dbba5..1087766 100644
--- a/configure.ac
+++ b/configure.ac
@@ -428,6 +428,7 @@
lib/vdsm/virt/containers/runtimes/Makefile
tests/Makefile
tests/common/Makefile
+ tests/containers/Makefile
tests/cpuinfo/Makefile
tests/functional/Makefile
tests/devices/Makefile
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 96c66b7..64e768d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,6 +22,7 @@
SUBDIRS = \
common \
+ containers \
cpuinfo \
functional \
devices \
@@ -30,6 +31,8 @@
$(NULL)
common_modules = common/*_test.py
+
+containers_modules = containers/*_test.py
device_modules = \
devices/parsing/complex_vm_tests.py \
@@ -360,6 +363,7 @@
./makecert.sh
run_modules = $(test_modules)
+run_modules += $(containers_modules)
run_modules += $(network_modules)
run_modules += $(device_modules)
run_modules += $(common_modules)
diff --git a/tests/containers/Makefile.am b/tests/containers/Makefile.am
new file mode 100644
index 0000000..afeca74
--- /dev/null
+++ b/tests/containers/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Copyright 2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+vdsmcontainerstestsdir = ${vdsmtestsdir}/containers
+
+dist_vdsmcontainerstests_PYTHON = \
+ __init__.py \
+ *_test.py \
+ conttestlib.py \
+ $(NULL)
+
+dist_vdsmcontainerstests_DATA = \
+ $(NULL)
+
diff --git a/tests/containers/__init__.py b/tests/containers/__init__.py
new file mode 100644
index 0000000..44b8a6b
--- /dev/null
+++ b/tests/containers/__init__.py
@@ -0,0 +1,19 @@
+#
+# Copyright 2015-2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
diff --git a/tests/containers/cgroups_test.py b/tests/containers/cgroups_test.py
new file mode 100644
index 0000000..49835d0
--- /dev/null
+++ b/tests/containers/cgroups_test.py
@@ -0,0 +1,69 @@
+#
+# Copyright 2015-2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+from __future__ import absolute_import
+
+import vdsm.virt.containers.metrics.cgroups
+
+from . import conttestlib
+
+
+class CgroupTests(conttestlib.CgroupTestCase):
+
+ def test_empty_cgroups(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable(self.pid)
+ self.assertEquals(mon.cgroups, ())
+
+ def test_empty_cpuacct(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable(self.pid)
+ self.assertIs(mon.cpuacct, None)
+
+ def test_empty_memory(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable(self.pid)
+ self.assertIs(mon.memory, None)
+
+ def test_empty_blkio(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable(self.pid)
+ self.assertIs(mon.blkio, None)
+
+ def test_from_pid(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable.from_pid(
+ self.pid
+ )
+ self.assertTrue(mon.cgroups)
+
+ def test_pid_matches(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable(self.pid)
+ self.assertEquals(mon.pid, self.pid)
+
+ def test_setup(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable(self.pid)
+ mon.setup()
+ self.assertTrue(mon.cgroups)
+
+ def test_cgroups_found(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable(self.pid)
+ mon.setup()
+ for cg in ('memory', 'cpuacct', 'blkio'):
+ self.assertIn(cg, mon.cgroups)
+
+ def test_update_without_setup(self):
+ mon = vdsm.virt.containers.metrics.cgroups.Monitorable(self.pid)
+ mon.update()
+ self.assertEquals(mon.cgroups, ())
diff --git a/tests/containers/conttestlib.py b/tests/containers/conttestlib.py
new file mode 100644
index 0000000..5ad3c4f
--- /dev/null
+++ b/tests/containers/conttestlib.py
@@ -0,0 +1,290 @@
+from __future__ import absolute_import
+#
+# Copyright 2015-2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+from contextlib import contextmanager
+import collections
+import gzip
+import os
+import os.path
+import shutil
+import tarfile
+import tempfile
+import uuid
+import unittest
+
+import vdsm.virt.containers.command
+import vdsm.virt.containers.config
+import vdsm.virt.containers.config.environ
+import vdsm.virt.containers.metrics.cgroups
+import vdsm.virt.containers.runtime
+import vdsm.virt.containers.runtimes
+
+from . import monkey
+
+
+class TestCase(unittest.TestCase):
+
+ def assertNotRaises(self, callableObj=None, *args, **kwargs):
+ # This is required when any exception raised during the call should be
+ # considered as a test failure.
+ context = not_raises(self)
+ if callableObj is None:
+ return context
+ with context:
+ callableObj(*args, **kwargs)
+
+
+@contextmanager
+def not_raises(test_case):
+ try:
+ yield
+ except Exception as e:
+ raise test_case.failureException("Exception raised: %s" % e)
+
+
+class TruePath(object):
+ def cmd(self):
+ return True
+
+
+class NonePath(object):
+ def __init__(self):
+ self.cmd = None
+
+
+TEMPDIR = '/tmp'
+
+
+@contextmanager
+def named_temp_dir(base=TEMPDIR):
+ tmp_dir = tempfile.mkdtemp(dir=base)
+ try:
+ yield tmp_dir
+ finally:
+ shutil.rmtree(tmp_dir)
+
+
+def make_conf(**kwargs):
+ conf = vdsm.virt.containers.config.environ.current()
+ for k, v in list(kwargs.items()):
+ setattr(conf, k, v)
+ return conf
+
+
+@contextmanager
+def global_conf(**kwargs):
+ saved_conf = vdsm.virt.containers.config.environ.current()
+
+ conf = make_conf(**kwargs)
+ vdsm.virt.containers.config.environ.setup(conf)
+ try:
+ yield conf
+ finally:
+ vdsm.virt.containers.config.environ.setup(saved_conf)
+
+
+def fake_executables():
+ paths = ['.', './tests', './fake/bin', './tests/fake/bin']
+ return {
+ 'machinectl': vdsm.virt.containers.command.Path(
+ 'true'
+ ),
+ 'systemctl': vdsm.virt.containers.command.Path(
+ 'systemctl', paths=paths
+ ),
+ 'rkt': vdsm.virt.containers.command.Path(
+ 'rkt', paths=paths
+ ),
+ 'systemd-run': vdsm.virt.containers.command.Path(
+ 'systemd-run', paths=paths
+ ),
+ }
+
+
+class RunnableTestCase(TestCase):
+
+ def setUp(self):
+ self.guid = uuid.uuid4()
+ self.run_dir = tempfile.mkdtemp()
+ self.patch = monkey.Patch([
+ (vdsm.virt.containers.runtimes.rkt.Network, 'DIR', self.run_dir),
+ (vdsm.virt.containers.command, 'executables', fake_executables()),
+ ])
+ self.patch.apply()
+ vdsm.virt.containers.runtime.clear()
+ vdsm.virt.containers.runtime.configure()
+
+ def tearDown(self):
+ self.patch.revert()
+ shutil.rmtree(self.run_dir)
+
+
+@contextmanager
+def move_into(path):
+ oldpath = os.getcwd()
+ try:
+ os.chdir(path)
+ yield
+ finally:
+ os.chdir(oldpath)
+
+
+class CgroupTestCase(TestCase):
+
+ def setUp(self):
+ self.pid = 0
+ testdir = os.path.dirname(os.path.abspath(__file__))
+ self.root = os.path.join(testdir, 'fake')
+
+ self.procfsroot = os.path.join(
+ self.root, vdsm.virt.containers.metrics.cgroups.PROCFS
+ )
+ self.cgroupfsroot = os.path.join(
+ self.root, vdsm.virt.containers.metrics.cgroups.CGROUPFS
+ )
+
+ with move_into(self.root):
+ cgroupsdata = os.path.join(self.root, 'cgroups.tgz')
+ with gzip.GzipFile(cgroupsdata) as gz:
+ tar = tarfile.TarFile(fileobj=gz)
+ tar.extractall()
+
+ self.patch = monkey.Patch([
+ (vdsm.virt.containers.metrics.cgroups,
+ '_PROCBASE', self.procfsroot),
+ (vdsm.virt.containers.metrics.cgroups,
+ '_CGROUPBASE', self.cgroupfsroot),
+ ])
+ self.patch.apply()
+
+ def tearDown(self):
+ self.patch.revert()
+ shutil.rmtree(self.procfsroot)
+ shutil.rmtree(self.cgroupfsroot)
+
+
+class FakeRunnableTestCase(TestCase):
+
+ def setUp(self):
+ def _fake_create(rt, conf, repo, **kwargs):
+ return vdsm.virt.containers.runtimes.fake.Fake(
+ conf,
+ repo,
+ **kwargs
+ )
+
+ self.patch = monkey.Patch([
+ (vdsm.virt.containers.runtime, 'create', _fake_create),
+ ])
+ self.patch.apply()
+ self.dom = vdsm.virt.containers.domain.Domain(
+ minimal_dom_xml(),
+ vdsm.virt.containers.config.environ.current(),
+ FakeRepo()
+ )
+
+ def tearDown(self):
+ self.patch.revert()
+
+
+class FakeRunner(object):
+ def __init__(self):
+ self.stopped = False
+ self.started = False
+ self.setup_done = False
+ self.teardown_done = False
+ self.configured = False
+ self.resynced = False
+ self.uuid = '00000000-0000-0000-0000-000000000000'
+
+ def setup(self, *args, **kwargs):
+ self.setup_done = True
+
+ def teardown(self, *args, **kwargs):
+ self.teardown_done = True
+
+ def start(self, *args, **kwargs):
+ self.started = True
+
+ def resync(self):
+ self.resynced = True
+
+ def stop(self):
+ self.stopped = True
+
+ def configure(self, *args, **kwargs):
+ self.configured = True
+
+
+def minimal_dom_xml(vm_uuid=None):
+ data = _read_dom_xml('minimal_dom.xml')
+ vm_uuid = str(uuid.uuid4()) if vm_uuid is None else vm_uuid
+ return data.format(vm_uuid=vm_uuid)
+
+
+def full_dom_xml(vm_uuid=None):
+ data = _read_dom_xml('full_dom.xml')
+ vm_uuid = str(uuid.uuid4()) if vm_uuid is None else vm_uuid
+ return data.format(vm_uuid=vm_uuid)
+
+
+def only_disk_dom_xml():
+ return _read_dom_xml('only_disk.xml')
+
+
+def only_mem_dom_xml():
+ return _read_dom_xml('only_mem.xml')
+
+
+def disk_dev_dom_xml():
+ return _read_dom_xml('disk_dev.xml')
+
+
+def disk_file_malformed_dom_xml():
+ return _read_dom_xml('disk_file_malformed.xml')
+
+
+def bridge_down_dom_xml():
+ return _read_dom_xml('bridge_down.xml')
+
+
+def bridge_no_source_dom_xml():
+ return _read_dom_xml('bridge_no_source.xml')
+
+
+def metadata_drive_map_dom_xml():
+ return _read_dom_xml('metadata_drive_map.xml')
+
+
+def _read_dom_xml(name):
+ testdir = os.path.dirname(os.path.abspath(__file__))
+ tmpl = os.path.join(testdir, 'data', name)
+ with open(tmpl, 'rt') as src:
+ return src.read()
+
+
+class FakeRepo(vdsm.virt.containers.command.Repo):
+
+ def __init__(self):
+ super(FakeRepo, self).__init__(execs=fake_executables())
+ self._cmds = collections.defaultdict(
+ lambda: vdsm.virt.containers.command.FakeCommand
+ )
diff --git a/tests/containers/data/bridge_down.xml b/tests/containers/data/bridge_down.xml
new file mode 100644
index 0000000..2ef73c7
--- /dev/null
+++ b/tests/containers/data/bridge_down.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain type="kvm" xmlns:ovirt="http://ovirt.org/vm/tune/1.0">
+ <name>testVm</name>
+ <uuid>35ef4734-4ed5-4f95-abf8-928e23a76619</uuid>
+ <maxMemory>16384</maxMemory>
+ <metadata>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ </metadata>
+ <devices>
+ <emulator>kvm</emulator>
+ <disk type='file' device='disk' snapshot='no'>
+ <driver name='qemu' type='raw' cache='none' error_policy='stop' io='threads'/>
+ <source file='/rhev/data-center/00000001-0001-0001-0001-00000000027f/43db3789-bb16-40bd-a9fc-3cced1b23ea6/images/90bece76-2df6-4a88-bfc8-f6f7461b7b8b/844e5378-6700-45ba-a846-67eba730e24b'>
+ <seclabel model='selinux' labelskip='yes'/>
+ </source>
+ <backingStore/>
+ <target dev='vda' bus='virtio'/>
+ <serial>90bece76-2df6-4a88-bfc8-f6f7461b7b8b</serial>
+ <alias name='virtio-disk0'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
+ </disk>
+ <interface type="bridge">
+ <mac address="00:1a:4a:16:01:5a"/>
+ <model type="virtio"/>
+ <source bridge="ovirtmgmt"/>
+ <filterref filter="vdsm-no-mac-spoofing"/>
+ <link state="down"/>
+ <bandwidth/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/containers/data/bridge_no_source.xml b/tests/containers/data/bridge_no_source.xml
new file mode 100644
index 0000000..ed99cae
--- /dev/null
+++ b/tests/containers/data/bridge_no_source.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain type="kvm" xmlns:ovirt="http://ovirt.org/vm/tune/1.0">
+ <name>testVm</name>
+ <uuid>35ef4734-4ed5-4f95-abf8-928e23a76619</uuid>
+ <maxMemory>16384</maxMemory>
+ <metadata>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ </metadata>
+ <devices>
+ <emulator>kvm</emulator>
+ <disk type='file' device='disk' snapshot='no'>
+ <driver name='qemu' type='raw' cache='none' error_policy='stop' io='threads'/>
+ <source file='/rhev/data-center/00000001-0001-0001-0001-00000000027f/43db3789-bb16-40bd-a9fc-3cced1b23ea6/images/90bece76-2df6-4a88-bfc8-f6f7461b7b8b/844e5378-6700-45ba-a846-67eba730e24b'>
+ <seclabel model='selinux' labelskip='yes'/>
+ </source>
+ <backingStore/>
+ <target dev='vda' bus='virtio'/>
+ <serial>90bece76-2df6-4a88-bfc8-f6f7461b7b8b</serial>
+ <alias name='virtio-disk0'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
+ </disk>
+ <interface type="bridge">
+ <mac address="00:1a:4a:16:01:5a"/>
+ <model type="virtio"/>
+ <filterref filter="vdsm-no-mac-spoofing"/>
+ <link state="up"/>
+ <bandwidth/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/containers/data/disk_dev.xml b/tests/containers/data/disk_dev.xml
new file mode 100644
index 0000000..cec0cbc
--- /dev/null
+++ b/tests/containers/data/disk_dev.xml
@@ -0,0 +1,15 @@
+<domain type='kvm' id='2'>
+ <metadata>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ </metadata>
+ <devices>
+ <emulator>kvm</emulator>
+ <disk device="disk" snapshot="no" type="block">
+ <source dev="/rhev/data-center/00000001-0001-0001-0001-00000000027f/38fa52fc-f62f-4d83-b89d-956f3d75c518/images/e0482091-853c-4e97-b80f-87f174730688/c36bb9f1-976a-414b-abd5-d72f5d2a94d2"/>
+ <target bus="virtio" dev="vda"/>
+ <serial>e0482091-853c-4e97-b80f-87f174730688</serial>
+ <boot order="1"/>
+ <driver cache="none" error_policy="stop" io="native" name="qemu" type="qcow2"/>
+ </disk>
+ </devices>
+ </domain>
diff --git a/tests/containers/data/disk_file_malformed.xml b/tests/containers/data/disk_file_malformed.xml
new file mode 100644
index 0000000..e15aebf
--- /dev/null
+++ b/tests/containers/data/disk_file_malformed.xml
@@ -0,0 +1,16 @@
+<domain type='kvm' id='2'>
+ <maxMemory slots='16' unit='KiB'>4294967296</maxMemory>
+ <metadata>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ </metadata>
+ <devices>
+ <emulator>kvm</emulator>
+ <disk device="disk" snapshot="no" type="file">
+ <source dev="/rhev/data-center/00000001-0001-0001-0001-00000000027f/38fa52fc-f62f-4d83-b89d-956f3d75c518/images/e0482091-853c-4e97-b80f-87f174730688/c36bb9f1-976a-414b-abd5-d72f5d2a94d2"/>
+ <target bus="virtio" dev="vda"/>
+ <serial>e0482091-853c-4e97-b80f-87f174730688</serial>
+ <boot order="1"/>
+ <driver cache="none" error_policy="stop" io="native" name="qemu" type="qcow2"/>
+ </disk>
+ </devices>
+ </domain>
diff --git a/tests/containers/data/full_dom.xml b/tests/containers/data/full_dom.xml
new file mode 100644
index 0000000..901e3ca
--- /dev/null
+++ b/tests/containers/data/full_dom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain type="kvm" xmlns:ovirt="http://ovirt.org/vm/tune/1.0">
+ <name>a_c7_2</name>
+ <uuid>{vm_uuid}</uuid>
+ <memory>4194304</memory>
+ <currentMemory>4194304</currentMemory>
+ <maxMemory slots="16">4294967296</maxMemory>
+ <vcpu current="2">16</vcpu>
+ <metadata>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ <ovirt:qos/>
+ </metadata>
+ <devices>
+ <emulator>kvm</emulator>
+ <channel type="unix">
+ <target name="com.redhat.rhevm.vdsm" type="virtio"/>
+ <source mode="bind" path="/var/lib/libvirt/qemu/channels/338175a6-44e7-45d0-8321-2c6f2d5b3d6b.com.redhat.rhevm.vdsm"/>
+ </channel>
+ <channel type="unix">
+ <target name="org.qemu.guest_agent.0" type="virtio"/>
+ <source mode="bind" path="/var/lib/libvirt/qemu/channels/338175a6-44e7-45d0-8321-2c6f2d5b3d6b.org.qemu.guest_agent.0"/>
+ </channel>
+ <input bus="ps2" type="mouse"/>
+ <memballoon model="none"/>
+ <video>
+ <model heads="1" ram="65536" type="qxl" vgamem="16384" vram="32768"/>
+ </video>
+ <graphics autoport="yes" passwd="*****" passwdValidTo="1970-01-01T00:00:01" port="-1" tlsPort="-1" type="spice">
+ <listen network="vdsm-ovirtmgmt" type="network"/>
+ </graphics>
+ <interface type="bridge">
+ <mac address="00:1a:4a:16:01:57"/>
+ <model type="virtio"/>
+ <source bridge="ovirtmgmt"/>
+ <filterref filter="vdsm-no-mac-spoofing"/>
+ <link state="up"/>
+ <bandwidth/>
+ </interface>
+ <disk device="cdrom" snapshot="no" type="file">
+ <source file="/rhev/data-center/00000001-0001-0001-0001-00000000027f/43db3789-bb16-40bd-a9fc-3cced1b23ea6/images/27101aac-10ec-468a-aaf5-694c663b2c33/373d166e-d21a-4ad0-8166-571f49c22d64" startupPolicy="optional"/>
+ <target bus="ide" dev="hdc"/>
+ <readonly/>
+ <serial>522169df-603d-4229-8451-69a4e860554a</serial>
+ <boot order="1"/>
+ </disk>
+ <disk device="disk" snapshot="no" type="file">
+ <source file="/rhev/data-center/00000001-0001-0001-0001-00000000027f/43db3789-bb16-40bd-a9fc-3cced1b23ea6/images/27101aac-10ec-468a-aaf5-694c663b2c33/19bb423f-7db0-4cd1-9fe9-5aa3d4d8c1af"/>
+ <target bus="virtio" dev="vda"/>
+ <serial>27101aac-10ec-468a-aaf5-694c663b2c33</serial>
+ <boot order="2"/>
+ <driver cache="none" error_policy="stop" io="threads" name="qemu" type="raw"/>
+ </disk>
+ <channel type="spicevmc">
+ <target name="com.redhat.spice.0" type="virtio"/>
+ </channel>
+ </devices>
+ <os>
+ <type arch="x86_64" machine="pc-i440fx-rhel7.2.0">hvm</type>
+ <smbios mode="sysinfo"/>
+ </os>
+ <sysinfo type="smbios">
+ <system>
+ <entry name="manufacturer">oVirt</entry>
+ <entry name="product">oVirt Node</entry>
+ <entry name="version">7-2.1511.el7.centos.2.10</entry>
+ <entry name="serial">0A9C980E-6B95-3D34-C5AC-40167EB07D87</entry>
+ <entry name="uuid">338175a6-44e7-45d0-8321-2c6f2d5b3d6b</entry>
+ </system>
+ </sysinfo>
+ <clock adjustment="0" offset="variable">
+ <timer name="rtc" tickpolicy="catchup"/>
+ <timer name="pit" tickpolicy="delay"/>
+ <timer name="hpet" present="no"/>
+ </clock>
+ <features>
+ <acpi/>
+ </features>
+ <cpu match="exact">
+ <model>Opteron_G2</model>
+ <topology cores="1" sockets="16" threads="1"/>
+ <numa>
+ <cell cpus="0,1" memory="4194304"/>
+ </numa>
+ </cpu>
+ <numatune>
+ <memory mode="interleave" nodeset="0"/>
+ </numatune>
+</domain>
diff --git a/tests/containers/data/metadata_drive_map.xml b/tests/containers/data/metadata_drive_map.xml
new file mode 100644
index 0000000..daf194c
--- /dev/null
+++ b/tests/containers/data/metadata_drive_map.xml
@@ -0,0 +1,35 @@
+<domain type='kvm' id='2'>
+ <metadata>
+ <convirt:drivemap xmlns:convirt="http://github.com/ovirt/containers/drivemap/1.0">
+ <volume name="data" drive="vda"/>
+ </convirt:drivemap>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ </metadata>
+ <memory>4194304</memory>
+ <currentMemory>4194304</currentMemory>
+ <maxMemory slots="16">4294967296</maxMemory>
+ <vcpu current="2">16</vcpu>
+ <devices>
+ <emulator>kvm</emulator>
+ <disk device="cdrom" snapshot="no" type="file">
+ <source file="/rhev/data-center/00000001-0001-0001-0001-00000000027f/43db3789-bb16-40bd-a9fc-3cced1b23ea6/images/27101aac-10ec-468a-aaf5-694c663b2c33/373d166e-d21a-4ad0-8166-571f49c22d64" startupPolicy="optional"/>
+ <target bus="ide" dev="hdc"/>
+ <readonly/>
+ <serial>522169df-603d-4229-8451-69a4e860554a</serial>
+ <boot order="1"/>
+ </disk>
+ <disk device="disk" snapshot="no" type="block">
+ <source dev="/rhev/data-center/00000001-0001-0001-0001-00000000027f/38fa52fc-f62f-4d83-b89d-956f3d75c518/images/e0482091-853c-4e97-b80f-87f174730688/c36bb9f1-976a-414b-abd5-d72f5d2a94d2"/>
+ <target bus="virtio" dev="vda"/>
+ <serial>e0482091-853c-4e97-b80f-87f174730688</serial>
+ <boot order="2"/>
+ <driver cache="none" error_policy="stop" io="native" name="qemu" type="qcow2"/>
+ </disk>
+ <interface type="bridge">
+ <mac address="00:1a:4a:16:01:57"/>
+ <model type="virtio"/>
+ <source bridge="ovirtmgmt"/>
+ <link state="up"/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/containers/data/minimal_dom.xml b/tests/containers/data/minimal_dom.xml
new file mode 100644
index 0000000..cfcb6fa
--- /dev/null
+++ b/tests/containers/data/minimal_dom.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain type="kvm" xmlns:ovirt="http://ovirt.org/vm/tune/1.0">
+ <name>testVm</name>
+ <uuid>{vm_uuid}</uuid>
+ <maxMemory>16384</maxMemory>
+ <metadata>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ </metadata>
+ <devices>
+ <emulator>rkt</emulator>
+ <disk type='file' device='cdrom' snapshot='no'>
+ <driver name='qemu' type='raw' cache='none' error_policy='stop' io='threads'/>
+ <source file='/rhev/data-center/00000001-0001-0001-0001-00000000027f/43db3789-bb16-40bd-a9fc-3cced1b23ea6/images/90bece76-2df6-4a88-bfc8-f6f7461b7b8b/844e5378-6700-45ba-a846-67eba730e24b'>
+ <seclabel model='selinux' labelskip='yes'/>
+ </source>
+ <backingStore/>
+ <target dev='vda' bus='virtio'/>
+ <serial>90bece76-2df6-4a88-bfc8-f6f7461b7b8b</serial>
+ <alias name='virtio-disk0'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
+ </disk>
+ <interface type="bridge">
+ <mac address="00:1a:4a:16:01:57"/>
+ <model type="virtio"/>
+ <source bridge="ovirtmgmt"/>
+ <link state="up"/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/containers/data/only_disk.xml b/tests/containers/data/only_disk.xml
new file mode 100644
index 0000000..dc00f51
--- /dev/null
+++ b/tests/containers/data/only_disk.xml
@@ -0,0 +1,19 @@
+<domain type='kvm' id='2'>
+ <metadata>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ </metadata>
+ <devices>
+ <emulator>kvm</emulator>
+ <disk type='file' device='disk' snapshot='no'>
+ <driver name='qemu' type='raw' cache='none' error_policy='stop' io='threads'/>
+ <source file='/random/path/to/disk/image'>
+ <seclabel model='selinux' labelskip='yes'/>
+ </source>
+ <backingStore/>
+ <target dev='vdb' bus='virtio'/>
+ <serial>90bece76-2df6-4a88-bfc8-f6f7461b7b8b</serial>
+ <alias name='virtio-disk1'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
+ </disk>
+ </devices>
+ </domain>
diff --git a/tests/containers/data/only_mem.xml b/tests/containers/data/only_mem.xml
new file mode 100644
index 0000000..ff4190b
--- /dev/null
+++ b/tests/containers/data/only_mem.xml
@@ -0,0 +1,9 @@
+<domain type='kvm' id='2'>
+ <maxMemory slots='16' unit='KiB'>4294967296</maxMemory>
+ <metadata>
+ <convirt:container xmlns:convirt="http://github.com/ovirt/containers/1.0">rkt</convirt:container>
+ </metadata>
+ <devices>
+ <emulator>kvm</emulator>
+ </devices>
+</domain>
diff --git a/tests/containers/errors_test.py b/tests/containers/errors_test.py
new file mode 100644
index 0000000..9b5b159
--- /dev/null
+++ b/tests/containers/errors_test.py
@@ -0,0 +1,34 @@
+from __future__ import absolute_import
+#
+# Copyright 2015-2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+
+import unittest
+
+import libvirt
+
+import vdsm.virt.containers.errors
+
+
+class ErrorsTests(unittest.TestCase):
+
+ def test_throw(self):
+ self.assertRaises(libvirt.libvirtError,
+ vdsm.virt.containers.errors.throw)
diff --git a/tests/containers/fake/bin/docker b/tests/containers/fake/bin/docker
new file mode 100755
index 0000000..6ef02d1
--- /dev/null
+++ b/tests/containers/fake/bin/docker
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+import os
+import os.path
+import sys
+
+
+def _main(args):
+ sys.exit(0)
+
+
+if __name__ == "__main__":
+ _main(sys.argv[1:])
diff --git a/tests/containers/fake/bin/rkt b/tests/containers/fake/bin/rkt
new file mode 100755
index 0000000..bc8f566
--- /dev/null
+++ b/tests/containers/fake/bin/rkt
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+import os
+import os.path
+import random
+import sys
+import uuid
+
+_ARG = '--uuid-file-save'
+
+def find_path(args):
+ for arg in args:
+ if arg.startswith(_ARG):
+ opt, val = arg.split('=', 1)
+ return val
+ return None
+
+
+def _gc(args):
+ sys.exit(0)
+
+
+def _run(args):
+ try:
+ path = find_path(args)
+ except ValueError:
+ sys.exit(1)
+
+ try:
+ path = path.strip('"')
+ with open(path, 'wt') as fp:
+ fp.write(str(uuid.uuid4()))
+ except IOError as e:
+ sys.exit(2)
+
+
+def _status(rkt_uuid):
+ print(
+"""state=running
+created=2016-06-29 10:10:07.441 +0200 CEST
+started=2016-06-29 10:10:08.548 +0200 CEST
+networks=default:ip4=172.16.28.5
+pid=%i
+exited=false""" % (random.randint(10, 65535)))
+
+
+
+def _main(args):
+ if not args:
+ sys.exit(1)
+
+ if 'gc' in args:
+ _gc(args)
+ elif 'run' in args:
+ _run(args)
+ elif 'status' == args[0]:
+ if len(args) != 2:
+ sys.exit(1)
+ _status(args[1])
+
+
+if __name__ == "__main__":
+ _main(sys.argv[1:])
diff --git a/tests/containers/fake/bin/systemctl b/tests/containers/fake/bin/systemctl
new file mode 100755
index 0000000..f1dee5d
--- /dev/null
+++ b/tests/containers/fake/bin/systemctl
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+import sys
+
+sys.exit(0)
diff --git a/tests/containers/fake/bin/systemd-run b/tests/containers/fake/bin/systemd-run
new file mode 100755
index 0000000..5454dd7
--- /dev/null
+++ b/tests/containers/fake/bin/systemd-run
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+import subprocess
+import sys
+
+for idx, arg in enumerate(sys.argv[1:]):
+ if not arg.startswith('--'):
+ break
+
+torun = sys.argv[idx+1:]
+
+subprocess.check_call(torun)
diff --git a/tests/containers/fake/cgroups.tgz b/tests/containers/fake/cgroups.tgz
new file mode 100644
index 0000000..fe3f58f
--- /dev/null
+++ b/tests/containers/fake/cgroups.tgz
Binary files differ
diff --git a/tests/containers/fs_test.py b/tests/containers/fs_test.py
new file mode 100644
index 0000000..e58df27
--- /dev/null
+++ b/tests/containers/fs_test.py
@@ -0,0 +1,75 @@
+#
+# Copyright 2015-2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+from __future__ import absolute_import
+
+import os
+import tempfile
+import uuid
+
+import vdsm.virt.containers
+import vdsm.virt.containers.fs
+
+from . import conttestlib
+
+
+class ReadFileTests(conttestlib.TestCase):
+
+ def test_read_existing_file(self):
+ content = str(uuid.uuid4())
+ with tempfile.NamedTemporaryFile() as f:
+ f.write(content.encode('ascii'))
+ f.flush()
+ self.assertEqual(content,
+ vdsm.virt.containers.fs.read_file(f.name))
+
+ def test_read_unexisting_file(self):
+ path = '/most/likely/does/not/exist'
+ self.assertRaises(IOError,
+ vdsm.virt.containers.fs.read_file,
+ path)
+
+
+class RMFileTests(conttestlib.TestCase):
+
+ def test_rm_file_once(self):
+ with conttestlib.named_temp_dir() as tmp_dir:
+ path = os.path.join(tmp_dir, "foobar")
+ with open(path, 'wt') as f:
+ f.write('%s\n' % str(uuid.uuid4()))
+ self.assertEquals(os.listdir(tmp_dir), ['foobar'])
+ vdsm.virt.containers.fs.rm_file(path)
+ self.assertEquals(os.listdir(tmp_dir), [])
+
+ def test_rm_file_twice(self):
+ with conttestlib.named_temp_dir() as tmp_dir:
+ path = os.path.join(tmp_dir, "foobar")
+ with open(path, 'wt') as f:
+ f.write('%s\n' % str(uuid.uuid4()))
+ self.assertEquals(os.listdir(tmp_dir), ['foobar'])
+ vdsm.virt.containers.fs.rm_file(path)
+ self.assertEquals(os.listdir(tmp_dir), [])
+ self.assertNotRaises(vdsm.virt.containers.fs.rm_file, path)
+ self.assertEquals(os.listdir(tmp_dir), [])
+
+ def test_rm_file_fails(self):
+ self.assertNotEqual(os.geteuid(), 0)
+ self.assertRaises(OSError,
+ vdsm.virt.containers.fs.rm_file,
+ '/var/log/lastlog')
diff --git a/tests/containers/monkey.py b/tests/containers/monkey.py
new file mode 100644
index 0000000..b71e0825
--- /dev/null
+++ b/tests/containers/monkey.py
@@ -0,0 +1,108 @@
+from __future__ import absolute_import
+#
+# Copyright 2012-2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+from contextlib import contextmanager
+from functools import wraps
+import inspect
+
+
+class Patch(object):
+
+ def __init__(self, what):
+ self.what = what
+ self.old = []
+
+ def apply(self):
+ for module, name, that in self.what:
+ old = getattr(module, name)
+ self.old.append((module, name, old))
+ # The following block is done so that if it is a method we are
+ # patching in, that it will have the same type as the method it
+ # replaced.
+ if inspect.isclass(module):
+ if inspect.isfunction(old):
+ that = staticmethod(that)
+ elif (inspect.ismethod(old) and
+ getattr(old, 'im_self', None) is not None):
+ that = classmethod(that)
+ setattr(module, name, that)
+
+ def revert(self):
+ assert self.old != []
+ while self.old:
+ module, name, that = self.old.pop()
+ # The following block is only necessary for Python2 as it wrongly
+ # sets the function as instancemethod instead of keeping it as
+ # staticmethod.
+ if inspect.isclass(module):
+ if inspect.isfunction(that):
+ that = staticmethod(that)
+
+ setattr(module, name, that)
+
+
+@contextmanager
+def patch_scope(what):
+ patch = Patch(what)
+ patch.apply()
+ try:
+ yield
+ finally:
+ patch.revert()
+
+
+def patch_function(module, name, that):
+ def decorator(f):
+ @wraps(f)
+ def wrapper(*args, **kw):
+ with patch_scope([(module, name, that)]):
+ return f(*args, **kw)
+ return wrapper
+ return decorator
+
+
+def patch_class(module, name, that):
+
+ def setup_decorator(func):
+ @wraps(func)
+ def setup(self, *a, **kw):
+ if not hasattr(self, '__monkeystack__'):
+ self.__monkeystack__ = []
+ patch = Patch([(module, name, that)])
+ self.__monkeystack__.append(patch)
+ patch.apply()
+ return func(self, *a, **kw)
+ return setup
+
+ def teardown_decorator(func):
+ @wraps(func)
+ def teardown(self, *a, **kw):
+ patch = self.__monkeystack__.pop()
+ patch.revert()
+ return func(self, *a, **kw)
+ return teardown
+
+ def wrapper(cls):
+ cls.setUp = setup_decorator(cls.setUp)
+ cls.tearDown = teardown_decorator(cls.tearDown)
+ return cls
+
+ return wrapper
diff --git a/tests/containers/xmlfile_test.py b/tests/containers/xmlfile_test.py
new file mode 100644
index 0000000..8b50652
--- /dev/null
+++ b/tests/containers/xmlfile_test.py
@@ -0,0 +1,87 @@
+#
+# Copyright 2015-2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+from __future__ import absolute_import
+
+import contextlib
+import os
+import uuid
+import xml.etree.ElementTree as ET
+
+import vdsm.virt.containers
+import vdsm.virt.containers.config
+import vdsm.virt.containers.config.environ
+import vdsm.virt.containers.xmlfile
+
+from . import conttestlib
+
+
+class XMLFileTests(conttestlib.TestCase):
+
+ def setUp(self):
+ self.vm_uuid = str(uuid.uuid4())
+
+ @contextlib.contextmanager
+ def test_env(self):
+ with conttestlib.named_temp_dir() as tmp_dir:
+ with conttestlib.global_conf(run_dir=tmp_dir):
+ yield vdsm.virt.containers.xmlfile.XMLFile(
+ self.vm_uuid,
+ vdsm.virt.containers.config.environ.current()
+ )
+
+ def test_fails_without_conf(self):
+ self.assertRaises(vdsm.virt.containers.xmlfile.UnconfiguredXML,
+ vdsm.virt.containers.xmlfile.XMLFile,
+ self.vm_uuid,
+ None)
+
+ def test_path(self):
+ with self.test_env() as xf:
+ self.assertTrue(xf.path.endswith('xml'))
+ self.assertIn(self.vm_uuid, xf.path)
+
+ def test_save(self):
+ root = ET.fromstring(conttestlib.minimal_dom_xml())
+ with self.test_env() as xf:
+ conf = vdsm.virt.containers.config.environ.current()
+ self.assertEquals(os.listdir(conf.run_dir), [])
+ self.assertNotRaises(xf.save, root)
+ self.assertTrue(len(os.listdir(conf.run_dir)), 1)
+
+ def test_load(self):
+ xml_data = conttestlib.minimal_dom_xml()
+ root = ET.fromstring(xml_data)
+ with self.test_env() as xf:
+ xf.save(root)
+ new_root = xf.load()
+ xml_copy = vdsm.virt.containers.xmlfile.XMLFile.encode(new_root)
+ # FIXME: nasty trick to tidy up the XML
+ xml_ref = vdsm.virt.containers.xmlfile.XMLFile.encode(root)
+ self.assertEquals(xml_ref, xml_copy)
+
+ def test_clear(self):
+ xml_data = conttestlib.minimal_dom_xml()
+ root = ET.fromstring(xml_data)
+ with self.test_env() as xf:
+ xf.save(root)
+ conf = vdsm.virt.containers.config.environ.current()
+ self.assertTrue(len(os.listdir(conf.run_dir)), 1)
+ self.assertNotRaises(xf.clear)
+ self.assertEquals(os.listdir(conf.run_dir), [])
--
To view, visit https://gerrit.ovirt.org/60678
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I27ba3cecbd71b7bbba94992d6bc63ca29333e313
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Francesco Romani <fromani(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: vmxml: export container metadata
by fromani@redhat.com
Francesco Romani has uploaded a new change for review.
Change subject: vmxml: export container metadata
......................................................................
vmxml: export container metadata
Add a metadata tags to let Engine specifiy the container
specific parameters, using custom properties.
This way we don't require changes to the Engine UI.
The metadata we add is about:
- container runtime type to use (rkt, runc...).
- mapping between VM drives and container volumes
- the image to run, to let users freely
experiment with the public repositories.
Change-Id: I1ade3c0c7d300c5ce33cb23723c3d0e59e4af664
Signed-off-by: Francesco Romani <fromani(a)redhat.com>
---
M vdsm/virt/vmxml.py
1 file changed, 31 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/81/60481/1
diff --git a/vdsm/virt/vmxml.py b/vdsm/virt/vmxml.py
index 4b5edd5..cc14770 100644
--- a/vdsm/virt/vmxml.py
+++ b/vdsm/virt/vmxml.py
@@ -23,13 +23,18 @@
import xml.dom.minidom
import xml.etree.ElementTree as etree
+import six
+
from vdsm import constants
+from vdsm import convirtconnection
from vdsm import cpuarch
from vdsm import utils
+
METADATA_VM_TUNE_URI = 'http://ovirt.org/vm/tune/1.0'
METADATA_VM_TUNE_ELEMENT = 'qos'
METADATA_VM_TUNE_PREFIX = 'ovirt'
+
_BOOT_MENU_TIMEOUT = 10000 # milliseconds
@@ -243,12 +248,26 @@
metadata = Element('metadata')
self._appendMetadataQOS(metadata)
+ self._appendMetadataContainer(metadata)
self.dom.appendChild(metadata)
def _appendMetadataQOS(self, metadata):
qos = Element(METADATA_VM_TUNE_PREFIX + ':' + METADATA_VM_TUNE_ELEMENT)
qos.setAttr('xmlns:' + METADATA_VM_TUNE_PREFIX, METADATA_VM_TUNE_URI)
metadata.appendChild(qos)
+
+ def _appendMetadataContainer(self, metadata):
+ custom = self.conf.get('custom', {})
+ container_type = custom.get('containerType')
+ if not container_type:
+ return
+
+ container_image = custom.get('containerImage')
+ drive_map = find_drive_mapping(self.conf.get('custom', {}))
+
+ convirtconnection.append_metadata(
+ Element, metadata, container_type, container_image, drive_map,
+ )
def appendOs(self, use_serial_console=False):
"""
@@ -554,3 +573,15 @@
def _getMaxVCpus(self):
return self.conf.get('maxVCpus', self._getSmp())
+
+
+def find_drive_mapping(custom):
+ drive_mapping = {}
+ for key, value in six.iteritems(custom):
+ if key.startswith('volume:'):
+ try:
+ tag, name = key.split(':', 1)
+ except ValueError:
+ continue
+ drive_mapping[name.strip()] = value.strip()
+ return drive_mapping
--
To view, visit https://gerrit.ovirt.org/60481
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I1ade3c0c7d300c5ce33cb23723c3d0e59e4af664
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Francesco Romani <fromani(a)redhat.com>
7 years, 5 months