[PATCH] [Kimchi 0/3] Live migration enhancements

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> This patch set makes enhancements to the live migration backend, covering more scenarios and delivering more error messages when things goes wrong. Daniel Henrique Barboza (3): Github #1007: use provided user for password-less setup Github #1007: Fixing non-root ssh key generation Adding libvirt remote connection verification i18n.py | 2 ++ model/vms.py | 94 ++++++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 24 deletions(-) -- 2.7.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> This patch changes the password-less setup to use the provided user instead of defaulting to 'root'. A new message was added to reflect a case where the remote host does not have the '.ssh' dir and the setup couldn't be completed. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 38 +++++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/i18n.py b/i18n.py index 82c679b..35a147c 100644 --- a/i18n.py +++ b/i18n.py @@ -139,6 +139,7 @@ messages = { "KCHVM0086E": _("Virtual machine description must be a string"), "KCHVM0087E": _("console parameter is only supported for s390x/s390 architecture."), "KCHVM0088E": _("invalid console type, supported types are sclp/virtio."), + "KCHVM0089E": _("Unable to setup password-less login at remote host %(host)s using user %(user)s: remote directory %(sshdir)s does not exist."), "KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."), "KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to directly assign to VM."), diff --git a/model/vms.py b/model/vms.py index a1b0df7..209b41b 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1716,7 +1716,7 @@ class VMModel(object): return local_sub_per_core def _get_remote_ppc64_subpercore(remote_host, user): - username_host = "%s@%s" % ('root', remote_host) + username_host = "%s@%s" % (user, remote_host) ssh_cmd = ['ssh', '-oNumberOfPasswordPrompts=0', '-oStrictHostKeyChecking=no', username_host, 'ppc64_cpu', '--subcores-per-core'] @@ -1737,7 +1737,7 @@ class VMModel(object): def _check_if_password_less_login_enabled(self, remote_host, user, password): - username_host = "%s@%s" % ('root', remote_host) + username_host = "%s@%s" % (user, remote_host) ssh_cmd = ['ssh', '-oNumberOfPasswordPrompts=0', '-oStrictHostKeyChecking=no', username_host, 'echo', 'hello'] @@ -1745,32 +1745,35 @@ class VMModel(object): if returncode != 0: if password is None: raise OperationFailed("KCHVM0056E", - {'host': remote_host, 'user': 'root'}) + {'host': remote_host, 'user': user}) 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" + home_dir = '/root' if user is 'root' else '/home/%s' % user + + id_rsa_file = "%s/.ssh/id_rsa" % home_dir + id_rsa_pub_file = id_rsa_file + '.pub' ssh_port = 22 ssh_client = None def create_root_ssh_key_if_required(): - if not os.path.isfile(id_rsa_file): + if not os.path.isfile(id_rsa_pub_file): with open("/dev/zero") as zero_input: - cmd = ['ssh-keygen', '-q', '-N', ''] + cmd = ['ssh-keygen', '-q', '-N', '', '-f', id_rsa_file] proc = subprocess.Popen( cmd, stdin=zero_input, stdout=open(os.devnull, 'wb') ) out, err = proc.communicate() - if not os.path.isfile(id_rsa_file): + if not os.path.isfile(id_rsa_pub_file): raise OperationFailed("KCHVM0070E") def read_id_rsa_pub_file(): data = None - with open(id_rsa_file, "r") as id_file: + with open(id_rsa_pub_file, "r") as id_file: data = id_file.read() return data @@ -1783,9 +1786,18 @@ class VMModel(object): def append_id_rsa_to_remote_authorized_keys(ssh_client, id_rsa_data): sftp_client = ssh_client.open_sftp() + ssh_dir = '%s/.ssh' % home_dir + + try: + sftp_client.chdir(ssh_dir) + except IOError: + raise OperationFailed( + "KCHVM0089E", + {'host': remote_host, 'user': user, 'sshdir': ssh_dir} + ) file_handler = sftp_client.file( - '/root/.ssh/authorized_keys', + '%s/.ssh/authorized_keys' % home_dir, mode='a', bufsize=1 ) @@ -1832,7 +1844,7 @@ class VMModel(object): 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" % ('root', remote_host) + username_host = "%s@%s" % (user, remote_host) cmd = ['ssh', '-oStrictHostKeyChecking=no', username_host, 'test', '-e', path] _, _, returncode = run_command(cmd, 5, silent=True) @@ -1853,7 +1865,7 @@ class VMModel(object): return False def _create_remote_path(self, path, remote_host, user): - username_host = "%s@%s" % ('root', remote_host) + username_host = "%s@%s" % (user, remote_host) cmd = ['ssh', '-oStrictHostKeyChecking=no', username_host, 'touch', path] _, _, returncode = run_command(cmd, 5, silent=True) @@ -1875,7 +1887,7 @@ class VMModel(object): ) def _create_remote_disk(self, disk_info, remote_host, user): - username_host = "%s@%s" % ('root', remote_host) + username_host = "%s@%s" % (user, remote_host) disk_fmt = disk_info.get('format') disk_path = disk_info.get('path') disk_size = self._get_img_size(disk_path) @@ -1920,7 +1932,7 @@ class VMModel(object): user = 'root' self.migration_pre_check(remote_host, user, password) - dest_conn = self._get_remote_libvirt_conn(remote_host) + dest_conn = self._get_remote_libvirt_conn(remote_host, user) non_shared = self._check_if_nonshared_migration( name, -- 2.7.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> Running ssh-keygen as root (or with sudo) will always generated a ssh-key binded to the user 'root' under /root/.ssh dir. This patch makes the following changes in the ssh-key generation process when the user is not 'root': - ssh-keygen now always generate the key under /home/<user>/.ssh - the generated .pub file is edited, changing 'root@...' to 'user@...' - file permissions are changed accordingly to the new generated key files (both private and public). Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- model/vms.py | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/model/vms.py b/model/vms.py index 209b41b..b702fe1 100644 --- a/model/vms.py +++ b/model/vms.py @@ -23,6 +23,7 @@ import lxml.etree as ET import os import paramiko import platform +import pwd import random import socket import subprocess @@ -1757,26 +1758,41 @@ class VMModel(object): ssh_port = 22 ssh_client = None - def create_root_ssh_key_if_required(): - if not os.path.isfile(id_rsa_pub_file): - - with open("/dev/zero") as zero_input: - cmd = ['ssh-keygen', '-q', '-N', '', '-f', id_rsa_file] - proc = subprocess.Popen( - cmd, - stdin=zero_input, - stdout=open(os.devnull, 'wb') - ) - out, err = proc.communicate() - if not os.path.isfile(id_rsa_pub_file): - raise OperationFailed("KCHVM0070E") - def read_id_rsa_pub_file(): data = None with open(id_rsa_pub_file, "r") as id_file: data = id_file.read() return data + def create_root_ssh_key_if_required(): + if os.path.isfile(id_rsa_pub_file): + return + + with open("/dev/zero") as zero_input: + cmd = ['ssh-keygen', '-q', '-N', '', '-f', id_rsa_file] + proc = subprocess.Popen( + cmd, + stdin=zero_input, + stdout=open(os.devnull, 'wb') + ) + out, err = proc.communicate() + + if not os.path.isfile(id_rsa_pub_file): + raise OperationFailed("KCHVM0070E") + + if user is not 'root': + id_rsa_content = read_id_rsa_pub_file() + updated_content = id_rsa_content.replace( + ' root@', ' %s@' % user + ) + with open(id_rsa_pub_file, 'w+') as f: + f.write(updated_content) + + user_uid = pwd.getpwnam(user).pw_uid + user_gid = pwd.getpwnam(user).pw_gid + os.chown(id_rsa_pub_file, user_uid, user_gid) + os.chown(id_rsa_file, user_uid, user_gid) + def get_ssh_client(remote_host, user, passwd): ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) -- 2.7.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> Depending on the remote libvirt configuration, setting a password-less login between the hosts isn't enough to ensure a password-less API connection to the remote libvirt service. This patch adds a virsh verification to ensure that this password-less connection can be estabished before moving further with the live migration process. If the verification fails, a proper error message is given to warn the user about the need of making a manual configuration in the libvirt service of the remote host. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/i18n.py b/i18n.py index 35a147c..814a8d9 100644 --- a/i18n.py +++ b/i18n.py @@ -140,6 +140,7 @@ messages = { "KCHVM0087E": _("console parameter is only supported for s390x/s390 architecture."), "KCHVM0088E": _("invalid console type, supported types are sclp/virtio."), "KCHVM0089E": _("Unable to setup password-less login at remote host %(host)s using user %(user)s: remote directory %(sshdir)s does not exist."), + "KCHVM0090E": _("Unable to create a password-less libvirt connection to the remote libvirt daemon at host %(host)s with the user %(user)s. Please verify the remote server libvirt configuration. More information: http://libvirt.org/auth.html ."), "KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."), "KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to directly assign to VM."), diff --git a/model/vms.py b/model/vms.py index b702fe1..79660a4 100644 --- a/model/vms.py +++ b/model/vms.py @@ -25,6 +25,7 @@ import paramiko import platform import pwd import random +import signal import socket import subprocess import string @@ -1841,6 +1842,22 @@ class VMModel(object): if ssh_client: ssh_client.close() + def _check_remote_libvirt_conn(self, remote_host, + user='root', transport='ssh'): + + dest_uri = 'qemu+%s://%s@%s/system' % (transport, user, remote_host) + cmd = ['virsh', '-c', dest_uri, 'list'] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + shell=True, preexec_fn=os.setsid) + timeout = 0 + while proc.poll() is None: + time.sleep(1) + timeout += 1 + if timeout == 5: + os.killpg(os.getpgid(proc.pid), signal.SIGTERM) + raise OperationFailed("KCHVM0090E", + {'host': remote_host, 'user': user}) + def _get_remote_libvirt_conn(self, remote_host, user='root', transport='ssh'): dest_uri = 'qemu+%s://%s@%s/system' % (transport, user, remote_host) @@ -1854,6 +1871,7 @@ class VMModel(object): user, password ) + self._check_remote_libvirt_conn(remote_host, user) self._check_if_migrating_same_arch_hypervisor(remote_host, user) if platform.machine() in ['ppc64', 'ppc64le']: -- 2.7.4

Reviewed-By: Lucio Correia <luciojhc@linux.vnet.ibm.com> On 11/10/2016 12:49, dhbarboza82@gmail.com wrote:
From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
This patch set makes enhancements to the live migration backend, covering more scenarios and delivering more error messages when things goes wrong.
Daniel Henrique Barboza (3): Github #1007: use provided user for password-less setup Github #1007: Fixing non-root ssh key generation Adding libvirt remote connection verification
i18n.py | 2 ++ model/vms.py | 94 ++++++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 24 deletions(-)
-- Lucio Correia Software Engineer IBM LTC Brazil
participants (3)
-
Aline Manera
-
dhbarboza82@gmail.com
-
Lucio Correia