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>
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>
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>
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>
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>
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>
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>