[PATCH 0/4] Live migration backend: additional features

From: Daniel Henrique Barboza <dhbarboza82@gmail.com> *** REQUIRES THE FOLLOWING PATCH TO APPLY *** 'Live migration backend: non-shared storage VM migration' This patchset implements the remaining features of the live migration backend to be shipped in Kimchi 2.0: - password-less root setup: if provided with a user/password that has SUDO ALL access in the remote host, the backend will set the password-less environment automatically. - added same remote hypervisor verification - added same remote architecture verification - (ppc64 only): if applicable, verify subcores-per-core This is the usage after this patchset: - migrating a VM without any credentials: if the password less root login isn't set, it will simply abort the operation: $ curl -k -u user -H "Content-Type: application/json" -H "Accept: application/json" "https://localhost:8001/plugins/kimchi/vms/<vm_name>/migrate" -X POST -d '{"remote_host":"a_remote_host" }' - migrating a vm with credentials: same thing as above, but will set up the password less root login if it's not set yet: $ curl -k -u root -H "Content-Type: application/json" -H "Accept: application/json" "https://localhost:8001/plugins/kimchi/vms/<vm_name>/migrate" -X POST -d '{"remote_host":"a_remote_host", "user":"a_sudoall_user", "password":"passwd_of_user" }' Note that 'sudoall_user' can be the root of the remote host. *** NOTE *** First patch is a change in WoK necessary to make the 'user' and 'password' fields optional. Daniel Henrique Barboza (4): WoK: control/base.py: _generate_action_handler_base changes Live migration: new features changes in docs/API/i18n Live migration: model changes for the new features Live migration: unit tests for the new features src/wok/control/base.py | 5 +- src/wok/plugins/kimchi/API.json | 6 + src/wok/plugins/kimchi/control/vms.py | 3 +- src/wok/plugins/kimchi/docs/API.md | 3 +- src/wok/plugins/kimchi/docs/README.md | 6 +- src/wok/plugins/kimchi/i18n.py | 8 + src/wok/plugins/kimchi/model/vms.py | 177 +++++++++++++++++++-- src/wok/plugins/kimchi/tests/test_livemigration.py | 81 +++++++++- 8 files changed, 270 insertions(+), 19 deletions(-) -- 2.4.3

From: Daniel Henrique Barboza <dhbarboza82@gmail.com> The function _generate_action_handler_base was enforcing all parameters passed in the 'action_args' array as if they were obligatory, disregarding any setting of the API.json of the plug-in. This patch makes this process more lenient, adding only the parameters that exists in the request. The API.json of each plug-in will handle the parameters usage. Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/control/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 638e196..fc42cea 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -109,7 +109,10 @@ class Resource(object): model_args = list(self.model_args) if action_args is not None: request = parse_request() - model_args.extend(request[key] for key in action_args) + model_args.extend( + request[key] for key in action_args + if key in request.keys() + ) action_fn = getattr(self.model, model_fn(self, action_name)) action_result = action_fn(*model_args) -- 2.4.3

Reviewed-By: Paulo Vital <pvital@linux.vnet.ibm.com> On Mon, 2015-11-16 at 21:04 -0200, dhbarboza82@gmail.com wrote:
From: Daniel Henrique Barboza <dhbarboza82@gmail.com>
The function _generate_action_handler_base was enforcing all parameters passed in the 'action_args' array as if they were obligatory, disregarding any setting of the API.json of the plug-in.
This patch makes this process more lenient, adding only the parameters that exists in the request. The API.json of each plug-in will handle the parameters usage.
Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/control/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 638e196..fc42cea 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -109,7 +109,10 @@ class Resource(object): model_args = list(self.model_args) if action_args is not None: request = parse_request() - model_args.extend(request[key] for key in action_args) + model_args.extend( + request[key] for key in action_args + if key in request.keys() + )
action_fn = getattr(self.model, model_fn(self, action_name)) action_result = action_fn(*model_args)

From: Daniel Henrique Barboza <dhbarboza82@gmail.com> This patch adds: - a new optional parameter called 'password' in the vm_migrate API. It also sets one of the existing parameters, 'user', as optional too. - control/vms.py changes to support the new parameter - added 'paramiko' and 'mock' as install/test dependencies in README.md - extra messages in i18n Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/API.json | 6 ++++++ src/wok/plugins/kimchi/control/vms.py | 3 ++- src/wok/plugins/kimchi/docs/API.md | 3 ++- src/wok/plugins/kimchi/docs/README.md | 6 ++++-- src/wok/plugins/kimchi/i18n.py | 8 ++++++++ 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/wok/plugins/kimchi/API.json b/src/wok/plugins/kimchi/API.json index 961f35f..e236e51 100644 --- a/src/wok/plugins/kimchi/API.json +++ b/src/wok/plugins/kimchi/API.json @@ -315,6 +315,7 @@ "remote_host": { "description": "IP address or hostname of the remote server", "type": "string", + "required": true, "minLength": 1, "error": "KCHVM0060E" }, @@ -323,6 +324,11 @@ "type": "string", "minLength": 1, "error": "KCHVM0059E" + }, + "password": { + "description": "Password of the user in the remote server", + "type": "string", + "error": "KCHVM0069E" } }, "additionalProperties": false diff --git a/src/wok/plugins/kimchi/control/vms.py b/src/wok/plugins/kimchi/control/vms.py index 5e24538..96bdb20 100644 --- a/src/wok/plugins/kimchi/control/vms.py +++ b/src/wok/plugins/kimchi/control/vms.py @@ -51,7 +51,8 @@ class VM(Resource): self.clone = self.generate_action_handler_task('clone') self.migrate = self.generate_action_handler_task('migrate', ['remote_host', - 'user']) + 'user', + 'password']) self.suspend = self.generate_action_handler('suspend') self.resume = self.generate_action_handler('resume') diff --git a/src/wok/plugins/kimchi/docs/API.md b/src/wok/plugins/kimchi/docs/API.md index 52368b7..5f0b234 100644 --- a/src/wok/plugins/kimchi/docs/API.md +++ b/src/wok/plugins/kimchi/docs/API.md @@ -185,7 +185,8 @@ server. * migrate: Migrate a virtual machine to a remote server, only support live mode without block migration. * remote_host: IP address or hostname of the remote server. - * user: User to log on at the remote server. + * user *(optional)*: User to log on at the remote server. + * password *(optional)*: password of the user in the remote server. ### Sub-resource: Virtual Machine Screenshot diff --git a/src/wok/plugins/kimchi/docs/README.md b/src/wok/plugins/kimchi/docs/README.md index 2b0427a..16fd1e5 100644 --- a/src/wok/plugins/kimchi/docs/README.md +++ b/src/wok/plugins/kimchi/docs/README.md @@ -54,7 +54,7 @@ Install Dependencies nfs-utils iscsi-initiator-utils pyparted \ python-libguestfs libguestfs-tools \ python-websockify novnc spice-html5 \ - python-configobj python-magic + python-configobj python-magic python-paramiko # If using RHEL, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict @@ -64,6 +64,8 @@ Install Dependencies # These dependencies are only required if you want to run the tests: $ sudo yum install pyflakes python-pep8 python-requests + $ sudo pip install mock + *Note for RHEL users*: Some of the above packages are located in the Red Hat EPEL repositories. See @@ -80,7 +82,7 @@ channel at RHN Classic or Red Hat Satellite. qemu-kvm python-parted python-ethtool sosreport \ xsltproc python-ipaddr python-lxml open-iscsi \ python-guestfs libguestfs-tools spice-html5 \ - python-magic \ + python-magic python-paramiko \ # These dependencies are only required if you want to run the tests: $ sudo apt-get install pep8 pyflakes python-requests diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py index 5e9eee4..d2bc408 100644 --- a/src/wok/plugins/kimchi/i18n.py +++ b/src/wok/plugins/kimchi/i18n.py @@ -118,6 +118,14 @@ messages = { "KCHVM0061E": _("Unable to create file %(path)s at %(host)s using user %(user)s."), "KCHVM0062E": _("Unable to read disk size of %(path)s, error: %(error)s"), "KCHVM0063E": _("Unable to create disk image %(path)s at %(host)s using user %(user)s. Error: %(error)s"), + "KCHVM0064E": _("Unable to migrate virtual machine to remote host %(host)s with arch %(destarch)s using localhost with arch %(srcarch)s."), + "KCHVM0065E": _("Unable to migrate virtual machine to remote host %(host)s with hypervisor %(desthyp)s because localhost uses hypervisor %(srchyp)s."), + "KCHVM0066E": _("Unable to determine remote host hypervisor and architecture. Error: %(error)s"), + "KCHVM0067E": _("Unable to migrate virtual machine: subcores per core setting from localhostand remote host %(host)s differs."), + "KCHVM0068E": _("Unable to setup password-less login at remote host %(host)s using user %(user)s. Error: %(error)s"), + "KCHVM0069E": _("Password field must be a string."), + "KCHVM0070E": _("Error creating local host ssh rsa key of user 'root'."), + "KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."), -- 2.4.3

On Mon, 2015-11-16 at 21:04 -0200, dhbarboza82@gmail.com wrote:
From: Daniel Henrique Barboza <dhbarboza82@gmail.com>
This patch adds:
- a new optional parameter called 'password' in the vm_migrate API. It also sets one of the existing parameters, 'user', as optional too.
- control/vms.py changes to support the new parameter
- added 'paramiko' and 'mock' as install/test dependencies in README.md
- extra messages in i18n
Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/API.json | 6 ++++++ src/wok/plugins/kimchi/control/vms.py | 3 ++- src/wok/plugins/kimchi/docs/API.md | 3 ++- src/wok/plugins/kimchi/docs/README.md | 6 ++++-- src/wok/plugins/kimchi/i18n.py | 8 ++++++++ 5 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/wok/plugins/kimchi/API.json b/src/wok/plugins/kimchi/API.json index 961f35f..e236e51 100644 --- a/src/wok/plugins/kimchi/API.json +++ b/src/wok/plugins/kimchi/API.json @@ -315,6 +315,7 @@ "remote_host": { "description": "IP address or hostname of the remote server", "type": "string", + "required": true, "minLength": 1, "error": "KCHVM0060E" }, @@ -323,6 +324,11 @@ "type": "string", "minLength": 1, "error": "KCHVM0059E" + }, + "password": { + "description": "Password of the user in the remote server", + "type": "string", + "error": "KCHVM0069E" } }, "additionalProperties": false diff --git a/src/wok/plugins/kimchi/control/vms.py b/src/wok/plugins/kimchi/control/vms.py index 5e24538..96bdb20 100644 --- a/src/wok/plugins/kimchi/control/vms.py +++ b/src/wok/plugins/kimchi/control/vms.py @@ -51,7 +51,8 @@ class VM(Resource): self.clone = self.generate_action_handler_task('clone') self.migrate = self.generate_action_handler_task('migrate', ['remote_ho st', - 'user']) + 'user', + 'password' ]) self.suspend = self.generate_action_handler('suspend') self.resume = self.generate_action_handler('resume')
diff --git a/src/wok/plugins/kimchi/docs/API.md b/src/wok/plugins/kimchi/docs/API.md index 52368b7..5f0b234 100644 --- a/src/wok/plugins/kimchi/docs/API.md +++ b/src/wok/plugins/kimchi/docs/API.md @@ -185,7 +185,8 @@ server. * migrate: Migrate a virtual machine to a remote server, only support live mode without block migration. * remote_host: IP address or hostname of the remote server. - * user: User to log on at the remote server. + * user *(optional)*: User to log on at the remote server. + * password *(optional)*: password of the user in the remote server.
### Sub-resource: Virtual Machine Screenshot
diff --git a/src/wok/plugins/kimchi/docs/README.md b/src/wok/plugins/kimchi/docs/README.md index 2b0427a..16fd1e5 100644 --- a/src/wok/plugins/kimchi/docs/README.md +++ b/src/wok/plugins/kimchi/docs/README.md @@ -54,7 +54,7 @@ Install Dependencies nfs-utils iscsi-initiator-utils pyparted \ python-libguestfs libguestfs-tools \ python-websockify novnc spice-html5 \ - python-configobj python-magic + python-configobj python-magic python- paramiko
No packages for Debian/Ubuntu?
# If using RHEL, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict @@ -64,6 +64,8 @@ Install Dependencies
# These dependencies are only required if you want to run the tests: $ sudo yum install pyflakes python-pep8 python-requests + $ sudo pip install mock +
*Note for RHEL users*: Some of the above packages are located in the Red Hat EPEL repositories. See @@ -80,7 +82,7 @@ channel at RHN Classic or Red Hat Satellite. qemu-kvm python-parted python-ethtool sosreport \ xsltproc python-ipaddr python-lxml open- iscsi \ python-guestfs libguestfs-tools spice- html5 \ - python-magic \ + python-magic python-paramiko \
# These dependencies are only required if you want to run the tests: $ sudo apt-get install pep8 pyflakes python-requests diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py index 5e9eee4..d2bc408 100644 --- a/src/wok/plugins/kimchi/i18n.py +++ b/src/wok/plugins/kimchi/i18n.py @@ -118,6 +118,14 @@ messages = { "KCHVM0061E": _("Unable to create file %(path)s at %(host)s using user %(user)s."), "KCHVM0062E": _("Unable to read disk size of %(path)s, error: %(error)s"), "KCHVM0063E": _("Unable to create disk image %(path)s at %(host)s using user %(user)s. Error: %(error)s"), + "KCHVM0064E": _("Unable to migrate virtual machine to remote host %(host)s with arch %(destarch)s using localhost with arch %(srcarch)s."), + "KCHVM0065E": _("Unable to migrate virtual machine to remote host %(host)s with hypervisor %(desthyp)s because localhost uses hypervisor %(srchyp)s."), + "KCHVM0066E": _("Unable to determine remote host hypervisor and architecture. Error: %(error)s"), + "KCHVM0067E": _("Unable to migrate virtual machine: subcores per core setting from localhostand remote host %(host)s differs."), + "KCHVM0068E": _("Unable to setup password-less login at remote host %(host)s using user %(user)s. Error: %(error)s"), + "KCHVM0069E": _("Password field must be a string."), + "KCHVM0070E": _("Error creating local host ssh rsa key of user 'root'."), +
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."),

On 11/17/2015 09:21 AM, Paulo Ricardo Paz Vital wrote:
On Mon, 2015-11-16 at 21:04 -0200, dhbarboza82@gmail.com wrote:
From: Daniel Henrique Barboza <dhbarboza82@gmail.com>
This patch adds:
- a new optional parameter called 'password' in the vm_migrate API. It also sets one of the existing parameters, 'user', as optional too.
- control/vms.py changes to support the new parameter
- added 'paramiko' and 'mock' as install/test dependencies in README.md
- extra messages in i18n
Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/API.json | 6 ++++++ src/wok/plugins/kimchi/control/vms.py | 3 ++- src/wok/plugins/kimchi/docs/API.md | 3 ++- src/wok/plugins/kimchi/docs/README.md | 6 ++++-- src/wok/plugins/kimchi/i18n.py | 8 ++++++++ 5 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/wok/plugins/kimchi/API.json b/src/wok/plugins/kimchi/API.json index 961f35f..e236e51 100644 --- a/src/wok/plugins/kimchi/API.json +++ b/src/wok/plugins/kimchi/API.json @@ -315,6 +315,7 @@ "remote_host": { "description": "IP address or hostname of the remote server", "type": "string", + "required": true, "minLength": 1, "error": "KCHVM0060E" }, @@ -323,6 +324,11 @@ "type": "string", "minLength": 1, "error": "KCHVM0059E" + }, + "password": { + "description": "Password of the user in the remote server", + "type": "string", + "error": "KCHVM0069E" } }, "additionalProperties": false diff --git a/src/wok/plugins/kimchi/control/vms.py b/src/wok/plugins/kimchi/control/vms.py index 5e24538..96bdb20 100644 --- a/src/wok/plugins/kimchi/control/vms.py +++ b/src/wok/plugins/kimchi/control/vms.py @@ -51,7 +51,8 @@ class VM(Resource): self.clone = self.generate_action_handler_task('clone') self.migrate = self.generate_action_handler_task('migrate', ['remote_ho st', - 'user']) + 'user', + 'password' ]) self.suspend = self.generate_action_handler('suspend') self.resume = self.generate_action_handler('resume')
diff --git a/src/wok/plugins/kimchi/docs/API.md b/src/wok/plugins/kimchi/docs/API.md index 52368b7..5f0b234 100644 --- a/src/wok/plugins/kimchi/docs/API.md +++ b/src/wok/plugins/kimchi/docs/API.md @@ -185,7 +185,8 @@ server. * migrate: Migrate a virtual machine to a remote server, only support live mode without block migration. * remote_host: IP address or hostname of the remote server. - * user: User to log on at the remote server. + * user *(optional)*: User to log on at the remote server. + * password *(optional)*: password of the user in the remote server.
### Sub-resource: Virtual Machine Screenshot
diff --git a/src/wok/plugins/kimchi/docs/README.md b/src/wok/plugins/kimchi/docs/README.md index 2b0427a..16fd1e5 100644 --- a/src/wok/plugins/kimchi/docs/README.md +++ b/src/wok/plugins/kimchi/docs/README.md @@ -54,7 +54,7 @@ Install Dependencies nfs-utils iscsi-initiator-utils pyparted \ python-libguestfs libguestfs-tools \ python-websockify novnc spice-html5 \ - python-configobj python-magic + python-configobj python-magic python- paramiko No packages for Debian/Ubuntu?
Down below
# If using RHEL, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict @@ -64,6 +64,8 @@ Install Dependencies
# These dependencies are only required if you want to run the tests: $ sudo yum install pyflakes python-pep8 python-requests + $ sudo pip install mock +
*Note for RHEL users*: Some of the above packages are located in the Red Hat EPEL repositories. See @@ -80,7 +82,7 @@ channel at RHN Classic or Red Hat Satellite. qemu-kvm python-parted python-ethtool sosreport \ xsltproc python-ipaddr python-lxml open- iscsi \ python-guestfs libguestfs-tools spice- html5 \ - python-magic \ + python-magic python-paramiko \
Ubuntu/Debian packages changes here ^
# These dependencies are only required if you want to run the tests: $ sudo apt-get install pep8 pyflakes python-requests diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py index 5e9eee4..d2bc408 100644 --- a/src/wok/plugins/kimchi/i18n.py +++ b/src/wok/plugins/kimchi/i18n.py @@ -118,6 +118,14 @@ messages = { "KCHVM0061E": _("Unable to create file %(path)s at %(host)s using user %(user)s."), "KCHVM0062E": _("Unable to read disk size of %(path)s, error: %(error)s"), "KCHVM0063E": _("Unable to create disk image %(path)s at %(host)s using user %(user)s. Error: %(error)s"), + "KCHVM0064E": _("Unable to migrate virtual machine to remote host %(host)s with arch %(destarch)s using localhost with arch %(srcarch)s."), + "KCHVM0065E": _("Unable to migrate virtual machine to remote host %(host)s with hypervisor %(desthyp)s because localhost uses hypervisor %(srchyp)s."), + "KCHVM0066E": _("Unable to determine remote host hypervisor and architecture. Error: %(error)s"), + "KCHVM0067E": _("Unable to migrate virtual machine: subcores per core setting from localhostand remote host %(host)s differs."), + "KCHVM0068E": _("Unable to setup password-less login at remote host %(host)s using user %(user)s. Error: %(error)s"), + "KCHVM0069E": _("Password field must be a string."), + "KCHVM0070E": _("Error creating local host ssh rsa key of user 'root'."), +
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."),
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

Reviewed-By: Paulo Vital <pvital@linux.vnet.ibm.com> On Tue, 2015-11-17 at 09:43 -0200, Daniel Henrique Barboza wrote:
On 11/17/2015 09:21 AM, Paulo Ricardo Paz Vital wrote:
On Mon, 2015-11-16 at 21:04 -0200, dhbarboza82@gmail.com wrote:
From: Daniel Henrique Barboza <dhbarboza82@gmail.com>
This patch adds:
- a new optional parameter called 'password' in the vm_migrate API. It also sets one of the existing parameters, 'user', as optional too.
- control/vms.py changes to support the new parameter
- added 'paramiko' and 'mock' as install/test dependencies in README.md
- extra messages in i18n
Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/API.json | 6 ++++++ src/wok/plugins/kimchi/control/vms.py | 3 ++- src/wok/plugins/kimchi/docs/API.md | 3 ++- src/wok/plugins/kimchi/docs/README.md | 6 ++++-- src/wok/plugins/kimchi/i18n.py | 8 ++++++++ 5 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/wok/plugins/kimchi/API.json b/src/wok/plugins/kimchi/API.json index 961f35f..e236e51 100644 --- a/src/wok/plugins/kimchi/API.json +++ b/src/wok/plugins/kimchi/API.json @@ -315,6 +315,7 @@ "remote_host": { "description": "IP address or hostname of the remote server", "type": "string", + "required": true, "minLength": 1, "error": "KCHVM0060E" }, @@ -323,6 +324,11 @@ "type": "string", "minLength": 1, "error": "KCHVM0059E" + }, + "password": { + "description": "Password of the user in the remote server", + "type": "string", + "error": "KCHVM0069E" } }, "additionalProperties": false diff --git a/src/wok/plugins/kimchi/control/vms.py b/src/wok/plugins/kimchi/control/vms.py index 5e24538..96bdb20 100644 --- a/src/wok/plugins/kimchi/control/vms.py +++ b/src/wok/plugins/kimchi/control/vms.py @@ -51,7 +51,8 @@ class VM(Resource): self.clone = self.generate_action_handler_task('clone') self.migrate = self.generate_action_handler_task('migrate', ['remo te_ho st', - 'user' ]) + 'user' , + 'passw ord' ]) self.suspend = self.generate_action_handler('suspend') self.resume = self.generate_action_handler('resume')
diff --git a/src/wok/plugins/kimchi/docs/API.md b/src/wok/plugins/kimchi/docs/API.md index 52368b7..5f0b234 100644 --- a/src/wok/plugins/kimchi/docs/API.md +++ b/src/wok/plugins/kimchi/docs/API.md @@ -185,7 +185,8 @@ server. * migrate: Migrate a virtual machine to a remote server, only support live mode without block migration. * remote_host: IP address or hostname of the remote server. - * user: User to log on at the remote server. + * user *(optional)*: User to log on at the remote server. + * password *(optional)*: password of the user in the remote server.
### Sub-resource: Virtual Machine Screenshot
diff --git a/src/wok/plugins/kimchi/docs/README.md b/src/wok/plugins/kimchi/docs/README.md index 2b0427a..16fd1e5 100644 --- a/src/wok/plugins/kimchi/docs/README.md +++ b/src/wok/plugins/kimchi/docs/README.md @@ -54,7 +54,7 @@ Install Dependencies nfs-utils iscsi-initiator-utils pyparted \ python-libguestfs libguestfs-tools \ python-websockify novnc spice-html5 \ - python-configobj python-magic + python-configobj python-magic python- paramiko No packages for Debian/Ubuntu?
Down below
# If using RHEL, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict @@ -64,6 +64,8 @@ Install Dependencies
# These dependencies are only required if you want to run the tests: $ sudo yum install pyflakes python-pep8 python-requests + $ sudo pip install mock +
*Note for RHEL users*: Some of the above packages are located in the Red Hat EPEL repositories. See @@ -80,7 +82,7 @@ channel at RHN Classic or Red Hat Satellite. qemu-kvm python-parted python- ethtool sosreport \ xsltproc python-ipaddr python-lxml open- iscsi \ python-guestfs libguestfs-tools spice- html5 \ - python-magic \ + python-magic python-paramiko \
Ubuntu/Debian packages changes here ^
# These dependencies are only required if you want to run the tests: $ sudo apt-get install pep8 pyflakes python-requests diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py index 5e9eee4..d2bc408 100644 --- a/src/wok/plugins/kimchi/i18n.py +++ b/src/wok/plugins/kimchi/i18n.py @@ -118,6 +118,14 @@ messages = { "KCHVM0061E": _("Unable to create file %(path)s at %(host)s using user %(user)s."), "KCHVM0062E": _("Unable to read disk size of %(path)s, error: %(error)s"), "KCHVM0063E": _("Unable to create disk image %(path)s at %(host)s using user %(user)s. Error: %(error)s"), + "KCHVM0064E": _("Unable to migrate virtual machine to remote host %(host)s with arch %(destarch)s using localhost with arch %(srcarch)s."), + "KCHVM0065E": _("Unable to migrate virtual machine to remote host %(host)s with hypervisor %(desthyp)s because localhost uses hypervisor %(srchyp)s."), + "KCHVM0066E": _("Unable to determine remote host hypervisor and architecture. Error: %(error)s"), + "KCHVM0067E": _("Unable to migrate virtual machine: subcores per core setting from localhostand remote host %(host)s differs."), + "KCHVM0068E": _("Unable to setup password-less login at remote host %(host)s using user %(user)s. Error: %(error)s"), + "KCHVM0069E": _("Password field must be a string."), + "KCHVM0070E": _("Error creating local host ssh rsa key of user 'root'."), +
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."),
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

From: Daniel Henrique Barboza <dhbarboza82@gmail.com> - added extra pre migration checks to ensure that the migration process will not fail due to a predictable error - added the capability of set up a password-less root login to a remote host if given a valid user/password. Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/model/vms.py | 177 +++++++++++++++++++++++++++++++++--- 1 file changed, 166 insertions(+), 11 deletions(-) diff --git a/src/wok/plugins/kimchi/model/vms.py b/src/wok/plugins/kimchi/model/vms.py index c98558c..48e99a3 100644 --- a/src/wok/plugins/kimchi/model/vms.py +++ b/src/wok/plugins/kimchi/model/vms.py @@ -21,8 +21,11 @@ import copy import libvirt import lxml.etree as ET import os +import paramiko +import platform import random import socket +import subprocess import string import threading import time @@ -1323,15 +1326,156 @@ class VMModel(object): if remote_host in ['localhost', '127.0.0.1', hostname]: raise OperationFailed("KCHVM0055E", {'host': remote_host}) - def _check_if_password_less_login_enabled(self, remote_host, user): - username_host = "%s@%s" % (user, remote_host) + def _check_if_migrating_same_arch_hypervisor(self, remote_host, + user='root'): + remote_conn = None + try: + remote_conn = self._get_remote_libvirt_conn( + remote_host, + user + ) + source_hyp = self.conn.get().getType() + dest_hyp = remote_conn.getType() + if source_hyp != dest_hyp: + raise OperationFailed( + "KCHVM0065E", + { + 'host': remote_host, + 'srchyp': source_hyp, + 'desthyp': dest_hyp + } + ) + source_arch = self.conn.get().getInfo()[0] + dest_arch = remote_conn.getInfo()[0] + if source_arch != dest_arch: + raise OperationFailed( + "KCHVM0064E", + { + 'host': remote_host, + 'srcarch': source_arch, + 'destarch': dest_arch + } + ) + except Exception, e: + raise OperationFailed("KCHVM0066E", {'error': e.message}) + + finally: + if remote_conn: + remote_conn.close() + + def _check_ppc64_subcores_per_core(self, remote_host, user): + """ + Output expected from command-line: + + $ ppc64_cpu --subcores-per-core + Subcores per core: N + """ + + def _get_local_ppc64_subpercore(): + local_cmd = ['ppc64_cpu', '--subcores-per-core'] + out, err, returncode = run_command(local_cmd, 5, silent=True) + if returncode != 0: + return None + local_sub_per_core = out.strip()[-1] + return local_sub_per_core + + def _get_remote_ppc64_subpercore(remote_host, user): + username_host = "%s@%s" % ('root', remote_host) + ssh_cmd = ['ssh', '-oNumberOfPasswordPrompts=0', + '-oStrictHostKeyChecking=no', username_host, + 'ppc64_cpu', '--subcores-per-core'] + out, err, returncode = run_command(ssh_cmd, 5, silent=True) + if returncode != 0: + return None + remote_sub_per_core = out.strip()[-1] + return remote_sub_per_core + + local_sub_per_core = _get_local_ppc64_subpercore() + if local_sub_per_core is None: + return + + remote_sub_per_core = _get_remote_ppc64_subpercore(remote_host, user) + + if local_sub_per_core != remote_sub_per_core: + raise OperationFailed("KCHVM0067E", {'host': remote_host}) + + def _check_if_password_less_login_enabled(self, remote_host, + user, password): + username_host = "%s@%s" % ('root', remote_host) ssh_cmd = ['ssh', '-oNumberOfPasswordPrompts=0', '-oStrictHostKeyChecking=no', username_host, 'echo', 'hello'] stdout, stderr, returncode = run_command(ssh_cmd, 5, silent=True) if returncode != 0: - raise OperationFailed("KCHVM0056E", - {'host': remote_host, 'user': user}) + if password is None: + raise OperationFailed("KCHVM0056E", + {'host': remote_host, 'user': 'root'}) + else: + self._set_password_less_login(remote_host, user, password) + + def _set_password_less_login(self, remote_host, user, passwd): + id_rsa_file = "/root/.ssh/id_rsa.pub" + ssh_port = 22 + ssh_client = None + + def create_root_ssh_key_if_required(): + if not os.path.isfile(id_rsa_file): + + with open("/dev/zero") as zero_input: + cmd = ['ssh-keygen', '-q', '-N', ''] + proc = subprocess.Popen( + cmd, + stdin=zero_input, + stdout=open(os.devnull, 'wb') + ) + out, err = proc.communicate() + if not os.path.isfile(id_rsa_file): + raise OperationFailed("KCHVM0070E") + + def read_id_rsa_pub_file(): + data = None + with open(id_rsa_file, "r") as id_file: + data = id_file.read() + return data + + def get_ssh_client(remote_host, user, passwd): + ssh_client = paramiko.SSHClient() + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_client.connect(remote_host, ssh_port, username=user, + password=passwd, timeout=4) + return ssh_client + + def append_id_rsa_to_remote_authorized_keys(ssh_client, id_rsa_data): + sftp_client = ssh_client.open_sftp() + + file_handler = sftp_client.file( + '/root/.ssh/authorized_keys', + mode='a', + bufsize=1 + ) + file_handler.write(id_rsa_data) + file_handler.flush() + file_handler.close() + + sftp_client.close() + + try: + create_root_ssh_key_if_required() + id_rsa_data = read_id_rsa_pub_file() + ssh_client = get_ssh_client(remote_host, user, passwd) + append_id_rsa_to_remote_authorized_keys( + ssh_client, + id_rsa_data + ) + except Exception, e: + raise OperationFailed( + "KCHVM0068E", + {'host': remote_host, 'user': user, 'error': e.message} + ) + + finally: + if ssh_client: + ssh_client.close() def _get_remote_libvirt_conn(self, remote_host, user='root', transport='ssh'): @@ -1339,12 +1483,20 @@ class VMModel(object): # TODO: verify why LibvirtConnection(dest_uri) does not work here return libvirt.open(dest_uri) - def migration_pre_check(self, remote_host, user): + def migration_pre_check(self, remote_host, user, password): self._check_if_host_not_localhost(remote_host) - self._check_if_password_less_login_enabled(remote_host, user) + self._check_if_password_less_login_enabled( + remote_host, + user, + password + ) + self._check_if_migrating_same_arch_hypervisor(remote_host, user) + + if platform.machine() in ['ppc64', 'ppc64le']: + self._check_ppc64_subcores_per_core(remote_host, user) def _check_if_path_exists_in_remote_host(self, path, remote_host, user): - username_host = "%s@%s" % (user, remote_host) + username_host = "%s@%s" % ('root', remote_host) cmd = ['ssh', '-oStrictHostKeyChecking=no', username_host, 'test', '-f', path] _, _, returncode = run_command(cmd, 5, silent=True) @@ -1365,7 +1517,7 @@ class VMModel(object): return False def _create_remote_path(self, path, remote_host, user): - username_host = "%s@%s" % (user, remote_host) + username_host = "%s@%s" % ('root', remote_host) cmd = ['ssh', '-oStrictHostKeyChecking=no', username_host, 'touch', path] _, _, returncode = run_command(cmd, 5, silent=True) @@ -1387,7 +1539,7 @@ class VMModel(object): ) def _create_remote_disk(self, disk_info, remote_host, user): - username_host = "%s@%s" % (user, remote_host) + username_host = "%s@%s" % ('root', remote_host) disk_fmt = disk_info.get('format') disk_path = disk_info.get('path') disk_size = self._get_img_size(disk_path) @@ -1424,11 +1576,14 @@ class VMModel(object): user ) - def migrate(self, name, remote_host, user='root'): + def migrate(self, name, remote_host, user=None, password=None): name = name.decode('utf-8') remote_host = remote_host.decode('utf-8') - self.migration_pre_check(remote_host, user) + if user is None: + user = 'root' + + self.migration_pre_check(remote_host, user, password) dest_conn = self._get_remote_libvirt_conn(remote_host) non_shared = self._check_if_nonshared_migration( -- 2.4.3

Reviewed-By: Paulo Vital <pvital@linux.vnet.ibm.com> On Mon, 2015-11-16 at 21:04 -0200, dhbarboza82@gmail.com wrote:
From: Daniel Henrique Barboza <dhbarboza82@gmail.com>
- added extra pre migration checks to ensure that the migration process will not fail due to a predictable error
- added the capability of set up a password-less root login to a remote host if given a valid user/password.
Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/model/vms.py | 177 +++++++++++++++++++++++++++++++++--- 1 file changed, 166 insertions(+), 11 deletions(-)
diff --git a/src/wok/plugins/kimchi/model/vms.py b/src/wok/plugins/kimchi/model/vms.py index c98558c..48e99a3 100644 --- a/src/wok/plugins/kimchi/model/vms.py +++ b/src/wok/plugins/kimchi/model/vms.py @@ -21,8 +21,11 @@ import copy import libvirt import lxml.etree as ET import os +import paramiko +import platform import random import socket +import subprocess import string import threading import time @@ -1323,15 +1326,156 @@ class VMModel(object): if remote_host in ['localhost', '127.0.0.1', hostname]: raise OperationFailed("KCHVM0055E", {'host': remote_host})
- def _check_if_password_less_login_enabled(self, remote_host, user): - username_host = "%s@%s" % (user, remote_host) + def _check_if_migrating_same_arch_hypervisor(self, remote_host, + user='root'): + remote_conn = None + try: + remote_conn = self._get_remote_libvirt_conn( + remote_host, + user + ) + source_hyp = self.conn.get().getType() + dest_hyp = remote_conn.getType() + if source_hyp != dest_hyp: + raise OperationFailed( + "KCHVM0065E", + { + 'host': remote_host, + 'srchyp': source_hyp, + 'desthyp': dest_hyp + } + ) + source_arch = self.conn.get().getInfo()[0] + dest_arch = remote_conn.getInfo()[0] + if source_arch != dest_arch: + raise OperationFailed( + "KCHVM0064E", + { + 'host': remote_host, + 'srcarch': source_arch, + 'destarch': dest_arch + } + ) + except Exception, e: + raise OperationFailed("KCHVM0066E", {'error': e.message}) + + finally: + if remote_conn: + remote_conn.close() + + def _check_ppc64_subcores_per_core(self, remote_host, user): + """ + Output expected from command-line: + + $ ppc64_cpu --subcores-per-core + Subcores per core: N + """ + + def _get_local_ppc64_subpercore(): + local_cmd = ['ppc64_cpu', '--subcores-per-core'] + out, err, returncode = run_command(local_cmd, 5, silent=True) + if returncode != 0: + return None + local_sub_per_core = out.strip()[-1] + return local_sub_per_core + + def _get_remote_ppc64_subpercore(remote_host, user): + username_host = "%s@%s" % ('root', remote_host) + ssh_cmd = ['ssh', '-oNumberOfPasswordPrompts=0', + '-oStrictHostKeyChecking=no', username_host, + 'ppc64_cpu', '--subcores-per-core'] + out, err, returncode = run_command(ssh_cmd, 5, silent=True) + if returncode != 0: + return None + remote_sub_per_core = out.strip()[-1] + return remote_sub_per_core + + local_sub_per_core = _get_local_ppc64_subpercore() + if local_sub_per_core is None: + return + + remote_sub_per_core = _get_remote_ppc64_subpercore(remote_host, user) + + if local_sub_per_core != remote_sub_per_core: + raise OperationFailed("KCHVM0067E", {'host': remote_host}) + + def _check_if_password_less_login_enabled(self, remote_host, + user, password): + username_host = "%s@%s" % ('root', remote_host) ssh_cmd = ['ssh', '-oNumberOfPasswordPrompts=0', '-oStrictHostKeyChecking=no', username_host, 'echo', 'hello'] stdout, stderr, returncode = run_command(ssh_cmd, 5, silent=True) if returncode != 0: - raise OperationFailed("KCHVM0056E", - {'host': remote_host, 'user': user}) + if password is None: + raise OperationFailed("KCHVM0056E", + {'host': remote_host, 'user': 'root'}) + else: + self._set_password_less_login(remote_host, user, password) + + def _set_password_less_login(self, remote_host, user, passwd): + id_rsa_file = "/root/.ssh/id_rsa.pub" + ssh_port = 22 + ssh_client = None + + def create_root_ssh_key_if_required(): + if not os.path.isfile(id_rsa_file): + + with open("/dev/zero") as zero_input: + cmd = ['ssh-keygen', '-q', '-N', ''] + proc = subprocess.Popen( + cmd, + stdin=zero_input, + stdout=open(os.devnull, 'wb') + ) + out, err = proc.communicate() + if not os.path.isfile(id_rsa_file): + raise OperationFailed("KCHVM0070E") + + def read_id_rsa_pub_file(): + data = None + with open(id_rsa_file, "r") as id_file: + data = id_file.read() + return data + + def get_ssh_client(remote_host, user, passwd): + ssh_client = paramiko.SSHClient() + ssh_client.set_missing_host_key_policy(paramiko.AutoAddP olicy()) + ssh_client.connect(remote_host, ssh_port, username=user, + password=passwd, timeout=4) + return ssh_client + + def append_id_rsa_to_remote_authorized_keys(ssh_client, id_rsa_data): + sftp_client = ssh_client.open_sftp() + + file_handler = sftp_client.file( + '/root/.ssh/authorized_keys', + mode='a', + bufsize=1 + ) + file_handler.write(id_rsa_data) + file_handler.flush() + file_handler.close() + + sftp_client.close() + + try: + create_root_ssh_key_if_required() + id_rsa_data = read_id_rsa_pub_file() + ssh_client = get_ssh_client(remote_host, user, passwd) + append_id_rsa_to_remote_authorized_keys( + ssh_client, + id_rsa_data + ) + except Exception, e: + raise OperationFailed( + "KCHVM0068E", + {'host': remote_host, 'user': user, 'error': e.message} + ) + + finally: + if ssh_client: + ssh_client.close()
def _get_remote_libvirt_conn(self, remote_host, user='root', transport='ssh'): @@ -1339,12 +1483,20 @@ class VMModel(object): # TODO: verify why LibvirtConnection(dest_uri) does not work here return libvirt.open(dest_uri)
- def migration_pre_check(self, remote_host, user): + def migration_pre_check(self, remote_host, user, password): self._check_if_host_not_localhost(remote_host) - self._check_if_password_less_login_enabled(remote_host, user) + self._check_if_password_less_login_enabled( + remote_host, + user, + password + ) + self._check_if_migrating_same_arch_hypervisor(remote_host, user) + + if platform.machine() in ['ppc64', 'ppc64le']: + self._check_ppc64_subcores_per_core(remote_host, user)
def _check_if_path_exists_in_remote_host(self, path, remote_host, user): - username_host = "%s@%s" % (user, remote_host) + username_host = "%s@%s" % ('root', remote_host) cmd = ['ssh', '-oStrictHostKeyChecking=no', username_host, 'test', '-f', path] _, _, returncode = run_command(cmd, 5, silent=True) @@ -1365,7 +1517,7 @@ class VMModel(object): return False
def _create_remote_path(self, path, remote_host, user): - username_host = "%s@%s" % (user, remote_host) + username_host = "%s@%s" % ('root', remote_host) cmd = ['ssh', '-oStrictHostKeyChecking=no', username_host, 'touch', path] _, _, returncode = run_command(cmd, 5, silent=True) @@ -1387,7 +1539,7 @@ class VMModel(object): )
def _create_remote_disk(self, disk_info, remote_host, user): - username_host = "%s@%s" % (user, remote_host) + username_host = "%s@%s" % ('root', remote_host) disk_fmt = disk_info.get('format') disk_path = disk_info.get('path') disk_size = self._get_img_size(disk_path) @@ -1424,11 +1576,14 @@ class VMModel(object): user )
- def migrate(self, name, remote_host, user='root'): + def migrate(self, name, remote_host, user=None, password=None): name = name.decode('utf-8') remote_host = remote_host.decode('utf-8')
- self.migration_pre_check(remote_host, user) + if user is None: + user = 'root' + + self.migration_pre_check(remote_host, user, password) dest_conn = self._get_remote_libvirt_conn(remote_host)
non_shared = self._check_if_nonshared_migration(

From: Daniel Henrique Barboza <dhbarboza82@gmail.com> Using python mock allowed to make unit test for otherwise invasive and arch dependent features that live migration now supports. Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/tests/test_livemigration.py | 81 +++++++++++++++++++++- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/src/wok/plugins/kimchi/tests/test_livemigration.py b/src/wok/plugins/kimchi/tests/test_livemigration.py index b9eb9a4..34c0b7e 100644 --- a/src/wok/plugins/kimchi/tests/test_livemigration.py +++ b/src/wok/plugins/kimchi/tests/test_livemigration.py @@ -19,6 +19,7 @@ import json import libvirt +import mock import os import socket import unittest @@ -77,7 +78,11 @@ def running_root_and_remoteserver_defined(): def check_if_vm_migration_test_possible(): inst = model.Model(objstore_loc='/tmp/kimchi-store-test') try: - inst.vm_migration_pre_check(KIMCHI_LIVE_MIGRATION_TEST, 'root') + inst.vm_migration_pre_check( + KIMCHI_LIVE_MIGRATION_TEST, + 'root', + None + ) except: return False return True @@ -169,8 +174,58 @@ class LiveMigrationTests(unittest.TestCase): self.assertRaises(OperationFailed, self.inst.vm_migrate, 'test_vm_migrate', - KIMCHI_LIVE_MIGRATION_TEST, - user='test_vm_migrate_fake_user') + 'this_is_a_fake_remote_host') + + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_get_remote_libvirt_conn') + def test_vm_migrate_fails_different_remote_hypervisor( + self, mock_get_remote_conn): + + class MockRemoteConnObj(object): + def getType(self): + return 'another_hypervisor' + + def close(self): + pass + + mock_get_remote_conn.return_value = MockRemoteConnObj() + + with RollbackContext() as rollback: + self.create_vm_test() + rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete, + u'test_vm_migrate') + + self.assertRaises(OperationFailed, + self.inst.vm_migrate, + 'test_vm_migrate', + KIMCHI_LIVE_MIGRATION_TEST) + + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_get_remote_libvirt_conn') + def test_vm_migrate_fails_different_remote_arch( + self, mock_get_remote_conn): + + class MockRemoteConnObj(object): + def getType(self): + return 'QEMU' + + def getInfo(self): + return ['another_arch', 'QEMU'] + + def close(self): + pass + + mock_get_remote_conn.return_value = MockRemoteConnObj() + + with RollbackContext() as rollback: + self.create_vm_test() + rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete, + u'test_vm_migrate') + + self.assertRaises(OperationFailed, + self.inst.vm_migrate, + 'test_vm_migrate', + KIMCHI_LIVE_MIGRATION_TEST) def get_remote_conn(self): remote_uri = 'qemu+ssh://%s@%s/system' % \ @@ -411,3 +466,23 @@ class LiveMigrationTests(unittest.TestCase): remote_vm.undefine() except Exception, e: self.fail('Migration test failed: %s' % e.message) + + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_set_password_less_login') + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_check_if_migrating_same_arch_hypervisor') + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_check_ppc64_subcores_per_core') + def test_set_passwordless_login(self, mock_ppc64_subpercore, + mock_same_arch, + mock_password_less_login): + self.inst.vm_migration_pre_check( + 'this_is_a_fake_remote_host', + 'test_vm_migrate_fake_user', + 'fake_password' + ) + mock_password_less_login.assert_called_once_with( + 'this_is_a_fake_remote_host', + 'test_vm_migrate_fake_user', + 'fake_password' + ) -- 2.4.3

Reviewed-By: Paulo Vital <pvital@linux.vnet.ibm.com> On Mon, 2015-11-16 at 21:04 -0200, dhbarboza82@gmail.com wrote:
From: Daniel Henrique Barboza <dhbarboza82@gmail.com>
Using python mock allowed to make unit test for otherwise invasive and arch dependent features that live migration now supports.
Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/tests/test_livemigration.py | 81 +++++++++++++++++++++- 1 file changed, 78 insertions(+), 3 deletions(-)
diff --git a/src/wok/plugins/kimchi/tests/test_livemigration.py b/src/wok/plugins/kimchi/tests/test_livemigration.py index b9eb9a4..34c0b7e 100644 --- a/src/wok/plugins/kimchi/tests/test_livemigration.py +++ b/src/wok/plugins/kimchi/tests/test_livemigration.py @@ -19,6 +19,7 @@
import json import libvirt +import mock import os import socket import unittest @@ -77,7 +78,11 @@ def running_root_and_remoteserver_defined(): def check_if_vm_migration_test_possible(): inst = model.Model(objstore_loc='/tmp/kimchi-store-test') try: - inst.vm_migration_pre_check(KIMCHI_LIVE_MIGRATION_TEST, 'root') + inst.vm_migration_pre_check( + KIMCHI_LIVE_MIGRATION_TEST, + 'root', + None + ) except: return False return True @@ -169,8 +174,58 @@ class LiveMigrationTests(unittest.TestCase): self.assertRaises(OperationFailed, self.inst.vm_migrate, 'test_vm_migrate', - KIMCHI_LIVE_MIGRATION_TEST, - user='test_vm_migrate_fake_user') + 'this_is_a_fake_remote_host') + + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_get_remote_libvirt_conn') + def test_vm_migrate_fails_different_remote_hypervisor( + self, mock_get_remote_conn): + + class MockRemoteConnObj(object): + def getType(self): + return 'another_hypervisor' + + def close(self): + pass + + mock_get_remote_conn.return_value = MockRemoteConnObj() + + with RollbackContext() as rollback: + self.create_vm_test() + rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete, + u'test_vm_migrate') + + self.assertRaises(OperationFailed, + self.inst.vm_migrate, + 'test_vm_migrate', + KIMCHI_LIVE_MIGRATION_TEST) + + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_get_remote_libvirt_conn') + def test_vm_migrate_fails_different_remote_arch( + self, mock_get_remote_conn): + + class MockRemoteConnObj(object): + def getType(self): + return 'QEMU' + + def getInfo(self): + return ['another_arch', 'QEMU'] + + def close(self): + pass + + mock_get_remote_conn.return_value = MockRemoteConnObj() + + with RollbackContext() as rollback: + self.create_vm_test() + rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete, + u'test_vm_migrate') + + self.assertRaises(OperationFailed, + self.inst.vm_migrate, + 'test_vm_migrate', + KIMCHI_LIVE_MIGRATION_TEST)
def get_remote_conn(self): remote_uri = 'qemu+ssh://%s@%s/system' % \ @@ -411,3 +466,23 @@ class LiveMigrationTests(unittest.TestCase): remote_vm.undefine() except Exception, e: self.fail('Migration test failed: %s' % e.message) + + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_set_password_less_login') + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_check_if_migrating_same_arch_hypervisor') + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_check_ppc64_subcores_per_core') + def test_set_passwordless_login(self, mock_ppc64_subpercore, + mock_same_arch, + mock_password_less_login): + self.inst.vm_migration_pre_check( + 'this_is_a_fake_remote_host', + 'test_vm_migrate_fake_user', + 'fake_password' + ) + mock_password_less_login.assert_called_once_with( + 'this_is_a_fake_remote_host', + 'test_vm_migrate_fake_user', + 'fake_password' + )
participants (4)
-
Aline Manera
-
Daniel Henrique Barboza
-
dhbarboza82@gmail.com
-
Paulo Ricardo Paz Vital