"""
connection tools to manage kinds of connection.
"""
import logging
import os
import shutil
import tempfile
import commands
from autotest.client import utils, os_dep
from virttest import propcan, remote, utils_libvirtd
from virttest import data_dir, aexpect
[docs]class ConnectionError(Exception):
"""
The base error in connection.
"""
pass
[docs]class ConnForbiddenError(ConnectionError):
"""
Error in forbidden operation.
"""
def __init__(self, detail):
ConnectionError.__init__(self)
self.detail = detail
def __str__(self):
return ('Operation is forbidden.\n'
'Message: %s' % self.detail)
[docs]class ConnCopyError(ConnectionError):
"""
Error in coping file.
"""
def __init__(self, src_path, dest_path):
ConnectionError.__init__(self)
self.src_path = src_path
self.dest_path = dest_path
def __str__(self):
return ('Copy file from %s to %s failed.'
% (self.src_path, self.dest_path))
[docs]class ConnNotImplementedError(ConnectionError):
"""
Error in calling unimplemented method
"""
def __init__(self, method_type, class_type):
ConnectionError.__init__(self)
self.method_type = method_type
self.class_type = class_type
def __str__(self):
return ('Method %s is not implemented in class %s\n'
% (self.method_type, self.class_type))
[docs]class ConnLoginError(ConnectionError):
"""
Error in login.
"""
def __init__(self, dest, detail):
ConnectionError.__init__(self)
self.dest = dest
self.detail = detail
def __str__(self):
return ("Got a error when login to %s.\n"
"Error: %s\n" % (self.dest, self.detail))
[docs]class ConnSCPError(ConnectionError):
"""
Error in SCP.
"""
def __init__(self, src_ip, src_path, dest_ip, dest_path, detail):
ConnectionError.__init__(self)
self.src_ip = src_ip
self.src_path = src_path
self.dest_ip = dest_ip
self.dest_path = dest_path
self.detail = detail
def __str__(self):
return ("Failed scp from %s on %s to %s on %s.\n"
"error: %s.\n" %
(self.src_path, self.src_ip, self.dest_path,
self.dest_ip, self.detail))
[docs]class SSHCheckError(ConnectionError):
"""
Base Error in check of SSH connection.
"""
def __init__(self, server_ip, output):
ConnectionError.__init__(self)
self.server_ip = server_ip
self.output = output
def __str__(self):
return ("SSH to %s failed.\n"
"output: %s " % (self.server_ip, self.output))
[docs]class SSHRmAuthKeysError(ConnectionError):
"""
Error in removing authorized_keys file.
"""
def __init__(self, auth_keys, output):
ConnectionError.__init__(self)
self.auth_keys = auth_keys
self.output = output
def __str__(self):
return ("Failed to remove authorized_keys file (%s).\n"
"output: %s .\n" % (self.auth_keys, self.output))
[docs]class ConnCmdClientError(ConnectionError):
"""
Error in executing cmd on client.
"""
def __init__(self, cmd, output):
ConnectionError.__init__(self)
self.cmd = cmd
self.output = output
def __str__(self):
return ("Execute command '%s' on client failed.\n"
"output: %s" % (self.cmd, self.output))
[docs]class ConnPrivKeyError(ConnectionError):
"""
Error in building private key with certtool command.
"""
def __init__(self, key, output):
ConnectionError.__init__(self)
self.key = key
self.output = output
def __str__(self):
return ("Failed to build private key file (%s).\n"
"output: %s .\n" % (self.key, self.output))
[docs]class ConnCertError(ConnectionError):
"""
Error in building certificate file with certtool command.
"""
def __init__(self, cert, output):
ConnectionError.__init__(self)
self.cert = cert
self.output = output
def __str__(self):
return ("Failed to build certificate file (%s).\n"
"output: %s .\n" % (self.cert, self.output))
[docs]class ConnRmCertError(ConnectionError):
"""
Error in removing certificate file with rm command.
"""
def __init__(self, cert, output):
ConnectionError.__init__(self)
self.cert = cert
self.output = output
def __str__(self):
return ("Failed to remove certificate file/path (%s).\n"
"output: %s .\n" % (self.cert, self.output))
[docs]class ConnMkdirError(ConnectionError):
"""
Error in making directory.
"""
def __init__(self, directory, output):
ConnectionError.__init__(self)
self.directory = directory
self.output = output
def __str__(self):
return ("Failed to make directory %s \n"
"output: %s.\n" % (self.directory, self.output))
[docs]class ConnServerRestartError(ConnectionError):
"""
Error in restarting libvirtd on server.
"""
def __init__(self, output):
ConnectionError.__init__(self)
self.output = output
def __str__(self):
return ("Failed to restart libvirtd service on server.\n"
"output: %s.\n" % (self.output))
[docs]class ConnectionBase(propcan.PropCanBase):
"""
Base class of a connection between server and client.
Connection is build to from client to server. And there are
some information for server and client in ConnectionBase.
"""
__slots__ = ('server_ip', 'server_user', 'server_pwd',
'client_ip', 'client_user', 'client_pwd',
'server_session', 'client_session',
'tmp_dir', 'auto_recover')
def __init__(self, *args, **dargs):
"""
Initialize instance with server info and client info.
:param server_ip: Ip of server.
:param server_user: Username to login server.
:param server_pwd: Password for server_user.
:param client_ip: IP of client.
:param client_user: Username to login client.
:param client_pwd: Password for client_user.
:param server_session: Session to server and execute command on
server.
:param client_session: Session to client and execute command on
client.
:param tmp_dir: A tmp dir to store some tmp file.
:param auto_recover: If it is False same as the default,
conn_recover() will not called by __del__()
If it is True, Connection class will call
conn_recover() in __del__(), then user need not
call it manually. But the errors in conn_recover()
will be ignored.
Example:
::
connection = ConnectionBase(server_ip=server_ip,
server_user=server_user,
server_pwd=server_pwd,
client_ip=client_ip,
client_user=client_user,
client_pwd=client_pwd)
connection.conn_setup()
virsh.connect(URI)
connection.conn_recover()
We suggest *not* to pass auto_recover=True to __init__(),
and call conn_recover() manually when you don't need this
connection any more.
"""
init_dict = dict(*args, **dargs)
init_dict['server_ip'] = init_dict.get('server_ip', 'SERVER.IP')
init_dict['server_user'] = init_dict.get('server_user', 'root')
init_dict['server_pwd'] = init_dict.get('server_pwd', None)
init_dict['client_ip'] = init_dict.get('client_ip', 'CLIENT.IP')
init_dict['client_user'] = init_dict.get('client_user', 'root')
init_dict['client_pwd'] = init_dict.get('client_pwd', None)
init_dict['auto_recover'] = init_dict.get('auto_recover', False)
super(ConnectionBase, self).__init__(init_dict)
self.__dict_set__('client_session', None)
self.__dict_set__('server_session', None)
# make a tmp dir as a workspace
tmp_dir = tempfile.mkdtemp(dir=data_dir.get_tmp_dir())
if not os.path.isdir(tmp_dir):
os.makedirs(tmp_dir)
self.tmp_dir = tmp_dir
def __del__(self):
"""
Clean up any leftover sessions and tmp_dir.
"""
self.close_session()
if self.auto_recover:
try:
self.conn_recover()
except ConnNotImplementedError:
pass
tmp_dir = self.tmp_dir
if (tmp_dir is not None) and (os.path.exists(tmp_dir)):
shutil.rmtree(tmp_dir)
[docs] def close_session(self):
"""
If some session exists, close it down.
"""
session_list = ['client_session', 'server_session']
for session_name in session_list:
session = self.__dict_get__(session_name)
if session is not None:
session.close()
else:
continue
[docs] def conn_setup(self):
"""
waiting for implemented by subclass.
"""
raise ConnNotImplementedError('conn_setup', self.__class__)
[docs] def conn_check(self):
"""
waiting for implemented by subclass.
"""
raise ConnNotImplementedError('conn_check', self.__class__)
[docs] def conn_recover(self):
"""
waiting for implemented by subclass.
"""
raise ConnNotImplementedError('conn_recover', self.__class__)
def _new_client_session(self):
"""
Build a new client session.
"""
transport = 'ssh'
host = self.client_ip
port = 22
username = self.client_user
password = self.client_pwd
prompt = r"[\#\$]\s*$"
try:
client_session = remote.wait_for_login(transport, host, port,
username, password, prompt)
except remote.LoginTimeoutError:
raise ConnLoginError("Got a timeout error when login to client.")
except remote.LoginAuthenticationError:
raise ConnLoginError("Authentication failed to login to client.")
except remote.LoginProcessTerminatedError:
raise ConnLoginError("Host terminates during login to client.")
except remote.LoginError:
raise ConnLoginError("Some error occurs login to client failed.")
return client_session
[docs] def get_client_session(self):
"""
If the client session exists,return it.
else create a session to client and set client_session.
"""
client_session = self.__dict_get__('client_session')
if (client_session is not None) and (client_session.is_alive()):
return client_session
else:
client_session = self._new_client_session()
self.__dict_set__('client_session', client_session)
return client_session
[docs] def set_client_session(self, value):
"""
Set client session to value.
"""
if value:
message = "Forbid to set client_session to %s." % value
else:
message = "Forbid to set client_session."
raise ConnForbiddenError(message)
[docs] def del_client_session(self):
"""
Delete client session.
"""
raise ConnForbiddenError('Forbid to del client_session')
def _new_server_session(self):
"""
Build a new server session.
"""
transport = 'ssh'
host = self.server_ip
port = 22
username = self.server_user
password = self.server_pwd
prompt = r"[\#\$]\s*$"
try:
server_session = remote.wait_for_login(transport, host, port,
username, password, prompt)
except remote.LoginTimeoutError:
raise ConnLoginError("Got a timeout error when login to server.")
except remote.LoginAuthenticationError:
raise ConnLoginError("Authentication failed to login to server.")
except remote.LoginProcessTerminatedError:
raise ConnLoginError("Host terminates during login to server.")
except remote.LoginError:
raise ConnLoginError("Some error occurs login to client server.")
return server_session
[docs] def get_server_session(self):
"""
If the server session exists,return it.
else create a session to server and set server_session.
"""
server_session = self.__dict_get__('server_session')
if (server_session is not None) and (server_session.is_alive()):
return server_session
else:
server_session = self._new_server_session()
self.__dict_set__('server_session', server_session)
return server_session
[docs] def set_server_session(self, value=None):
"""
Set server session to value.
"""
if value:
message = "Forbid to set server_session to %s." % value
else:
message = "Forbid to set server_session."
raise ConnForbiddenError(message)
[docs] def del_server_session(self):
"""
Delete server session.
"""
raise ConnForbiddenError('Forbid to del server_session')
[docs]class SSHConnection(ConnectionBase):
"""
Connection of SSH transport.
Some specific variables in SSHConnection class.
ssh_rsa_pub_path: Path of id_rsa.pub, default is /root/.ssh/id_rsa.pub.
ssh_id_rsa_path: Path of id_rsa, default is /root/.ssh/id_rsa.
SSH_KEYGEN, SSH_ADD, SSH_COPY_ID, SSH_AGENT, SHELL, SSH: tools to build
a non-pwd connection.
"""
__slots__ = ('ssh_rsa_pub_path', 'ssh_id_rsa_path', 'SSH_KEYGEN',
'SSH_ADD', 'SSH_COPY_ID', 'SSH_AGENT', 'SHELL', 'SSH')
def __init__(self, *args, **dargs):
"""
Initialization of SSH connection.
(1). Call __init__ of class ConnectionBase.
(2). Initialize tools will be used in conn setup.
"""
init_dict = dict(*args, **dargs)
init_dict['ssh_rsa_pub_path'] = init_dict.get('ssh_rsa_pub_path',
'/root/.ssh/id_rsa.pub')
init_dict['ssh_id_rsa_path'] = init_dict.get('ssh_id_rsa_path',
'/root/.ssh/id_rsa')
super(SSHConnection, self).__init__(init_dict)
# set the tool for ssh setup.
tool_dict = {'SSH_KEYGEN': 'ssh-keygen',
'SSH_ADD': 'ssh-add',
'SSH_COPY_ID': 'ssh-copy-id',
'SSH_AGENT': 'ssh-agent',
'SHELL': 'sh',
'SSH': 'ssh'}
for key in tool_dict:
toolName = tool_dict[key]
try:
tool = os_dep.command(toolName)
except ValueError:
logging.debug("%s executable not set or found on path,"
"some function of connection will fail.",
toolName)
tool = '/bin/true'
self.__dict_set__(key, tool)
[docs] def conn_check(self):
"""
Check the SSH connection.
(1).Initialize some variables.
(2).execute ssh command to check conn.
"""
client_session = self.client_session
server_user = self.server_user
server_ip = self.server_ip
ssh = self.SSH
if ssh is '/bin/true':
raise ConnToolNotFoundError('ssh',
"executable not set or found on path, ")
cmd = "%s %s@%s exit 0" % (ssh, server_user, server_ip)
try:
client_session.cmd(cmd, timeout=5)
except aexpect.ShellError, detail:
client_session.close()
raise SSHCheckError(server_ip, detail)
logging.debug("Check the SSH to %s OK.", server_ip)
[docs] def conn_recover(self):
"""
Clean up authentication host.
"""
# initialize variables
server_ip = self.server_ip
server_user = self.server_user
server_pwd = self.server_pwd
client_ip = self.client_ip
ssh_authorized_keys_path = '/root/.ssh/authorized_keys'
cmd = "rm -rf %s" % ssh_authorized_keys_path
server_session = remote.wait_for_login('ssh', server_ip, '22',
server_user, server_pwd,
r"[\#\$]\s*$")
# remove authentication file
status, output = server_session.cmd_status_output(cmd)
if status:
raise SSHRmAuthKeysError(ssh_authorized_keys_path, output)
# restart libvirtd service on server
try:
libvirtd_service = utils_libvirtd.Libvirtd(session=server_session)
libvirtd_service.restart()
server_session.close()
except (remote.LoginError, aexpect.ShellError), detail:
server_session.close()
raise ConnServerRestartError(detail)
logging.debug("SSH authentication recover successfully.")
[docs] def conn_setup(self):
"""
Setup of SSH connection.
(1).Initialization of some variables.
(2).Check tools.
(3).Initialization of id_rsa.
(4).set a ssh_agent.
(5).copy pub key to server.
"""
client_session = self.client_session
ssh_rsa_pub_path = self.ssh_rsa_pub_path
ssh_id_rsa_path = self.ssh_id_rsa_path
server_user = self.server_user
server_ip = self.server_ip
server_pwd = self.server_pwd
ssh_keygen = self.SSH_KEYGEN
ssh_add = self.SSH_ADD
ssh_copy_id = self.SSH_COPY_ID
ssh_agent = self.SSH_AGENT
shell = self.SHELL
tool_dict = {'ssh_keygen': ssh_keygen,
'ssh_add': ssh_add,
'ssh_copy_id': ssh_copy_id,
'ssh_agent': ssh_agent,
'shell': shell}
for tool_name in tool_dict:
tool = tool_dict[tool_name]
if tool is '/bin/true':
raise ConnToolNotFoundError(tool_name,
"executable not set or found on path,")
if os.path.exists("/root/.ssh/id_rsa"):
pass
else:
cmd = "%s -t rsa -f /root/.ssh/id_rsa -N '' " % (ssh_keygen)
status, output = client_session.cmd_status_output(cmd)
if status:
raise ConnCmdClientError(cmd, output)
cmd = "%s %s" % (ssh_agent, shell)
status, output = client_session.cmd_status_output(cmd)
if status:
raise ConnCmdClientError(cmd, output)
cmd = "%s %s" % (ssh_add, ssh_id_rsa_path)
status, output = client_session.cmd_status_output(cmd)
if status:
raise ConnCmdClientError(cmd, output)
cmd = "%s -i %s %s@%s" % (ssh_copy_id, ssh_rsa_pub_path,
server_user, server_ip)
client_session.sendline(cmd)
try:
remote.handle_prompts(client_session, server_user,
server_pwd, prompt=r"[\#\$]\s*$")
except remote.LoginError, detail:
raise ConnCmdClientError(cmd, detail)
client_session.close()
logging.debug("SSH connection setup successfully.")
[docs]class TCPConnection(ConnectionBase):
"""
Connection class for TCP transport.
Some specific variables for TCPConnection class.
"""
__slots__ = ('tcp_port', 'remote_syslibvirtd',
'remote_libvirtdconf', 'sasl_allowed_users',
'auth_tcp', 'listen_addr')
def __init__(self, *args, **dargs):
"""
init params for TCP connection and init tmp_dir.
:param tcp_port: Port of tcp connection, default is 16509.
:param sysconfig_libvirtd_path: Path of libvirtd file, default is
``/etc/sysconfig/libvirtd``.
:param libvirtd_conf_path: Path of libvirtd.conf, default is
``/etc/libvirt/libvirtd.conf``.
"""
init_dict = dict(*args, **dargs)
init_dict['tcp_port'] = init_dict.get('tcp_port', '16509')
init_dict['auth_tcp'] = init_dict.get('auth_tcp', 'none')
init_dict['listen_addr'] = init_dict.get('listen_addr')
init_dict['sasl_allowed_users'] = init_dict.get('sasl_allowed_users')
super(TCPConnection, self).__init__(init_dict)
self.remote_syslibvirtd = remote.RemoteFile(
address=self.server_ip,
client='scp',
username=self.server_user,
password=self.server_pwd,
port='22',
remote_path='/etc/sysconfig/libvirtd')
self.remote_libvirtdconf = remote.RemoteFile(
address=self.server_ip,
client='scp',
username=self.server_user,
password=self.server_pwd,
port='22',
remote_path='/etc/libvirt/libvirtd.conf')
[docs] def conn_recover(self):
"""
Clean up for TCP connection.
(1).initialize variables.
(2).Delete the RemoteFile.
(3).restart libvirtd on server.
"""
# initialize variables
server_ip = self.server_ip
server_user = self.server_user
server_pwd = self.server_pwd
# delete the RemoteFile object to recover remote file.
del self.remote_syslibvirtd
del self.remote_libvirtdconf
# restart libvirtd service on server
try:
session = remote.wait_for_login('ssh', server_ip, '22',
server_user, server_pwd,
r"[\#\$]\s*$")
libvirtd_service = utils_libvirtd.Libvirtd(session=session)
libvirtd_service.restart()
except (remote.LoginError, aexpect.ShellError), detail:
raise ConnServerRestartError(detail)
logging.debug("TCP connection recover successfully.")
[docs] def conn_setup(self):
"""
Enable tcp connect of libvirtd on server.
(1).initialization for variables.
(2).edit /etc/sysconfig/libvirtd on server.
(3).edit /etc/libvirt/libvirtd.conf on server.
(4).restart libvirtd service on server.
"""
# initialize variables
server_ip = self.server_ip
server_user = self.server_user
server_pwd = self.server_pwd
tcp_port = self.tcp_port
auth_tcp = self.auth_tcp
# require a list data type
sasl_allowed_users = self.sasl_allowed_users
listen_addr = self.listen_addr
# edit the /etc/sysconfig/libvirtd to add --listen args in libvirtd
pattern2repl = {r".*LIBVIRTD_ARGS\s*=\s*\"\s*--listen\s*\".*":
"LIBVIRTD_ARGS=\"--listen\""}
self.remote_syslibvirtd.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf
# listen_tcp=1, tcp_port=$tcp_port, auth_tcp="none"
# listen_tcp=1, tcp_port=$tcp_port, auth_tcp=$auth_tcp
pattern2repl = {r".*listen_tls\s*=.*": 'listen_tls=0',
r".*listen_tcp\s*=.*": 'listen_tcp=1',
r".*tcp_port\s*=.*": 'tcp_port="%s"' % (tcp_port),
r".*auth_tcp\s*=.*": 'auth_tcp="%s"' % (auth_tcp)}
# a whitelist of allowed SASL usernames, it's a list.
# If the list is an empty, no client can connect
if sasl_allowed_users:
pattern2repl[r".*sasl_allowed_username_list\s*=.*"] = \
'sasl_allowed_username_list=%s' % (sasl_allowed_users)
if listen_addr:
pattern2repl[r".*listen_addr\s*=.*"] = \
"listen_addr='%s'" % (listen_addr)
self.remote_libvirtdconf.sub_else_add(pattern2repl)
# restart libvirtd service on server
try:
session = remote.wait_for_login('ssh', server_ip, '22',
server_user, server_pwd,
r"[\#\$]\s*$")
libvirtd_service = utils_libvirtd.Libvirtd(session=session)
libvirtd_service.restart()
except (remote.LoginError, aexpect.ShellError), detail:
raise ConnServerRestartError(detail)
logging.debug("TCP connection setup successfully.")
[docs]class TLSConnection(ConnectionBase):
"""
Connection of TLS transport.
Some specific variables for TLSConnection class.
server_cn, client_cn, ca_cn: Info to build pki key.
CERTOOL: tool to build key for TLS connection.
pki_CA_dir: Dir to store CA key.
libvirt_pki_dir, libvirt_pki_private_dir: Dir to store pki in libvirt.
sysconfig_libvirtd_path, libvirtd_conf_path: Path of libvirt config file.
hosts_path: /etc/hosts
auth_tls, tls_port, listen_addr: custom TLS Auth, port and listen address
tls_allowed_dn_list: DN's list are checked
tls_verify_cert: disable verification, default is to always verify
tls_sanity_cert: disable checks, default is to always run sanity checks
custom_pki_path: custom pki path
ca_cakey_path: CA certification path, sometimes need to reuse previous cert
scp_new_cacert: copy new CA certification, default is to always copy
restart_libvirtd: default is to restart libvirtd
"""
__slots__ = ('server_cn', 'client_cn', 'ca_cn', 'CERTTOOL', 'pki_CA_dir',
'libvirt_pki_dir', 'libvirt_pki_private_dir', 'client_hosts',
'server_libvirtdconf', 'server_syslibvirtd', 'auth_tls',
'tls_port', 'listen_addr', 'tls_allowed_dn_list',
'custom_pki_path', 'tls_verify_cert', 'tls_sanity_cert',
'ca_cakey_path', 'scp_new_cacert', 'restart_libvirtd')
def __init__(self, *args, **dargs):
"""
Initialization of TLSConnection.
(1).call the init func in ConnectionBase.
(2).check and set CERTTOOL.
(3).make a tmp directory as a workspace.
(4).set values of pki related.
"""
init_dict = dict(*args, **dargs)
init_dict['server_cn'] = init_dict.get('server_cn', 'TLSServer')
init_dict['client_cn'] = init_dict.get('client_cn', 'TLSClient')
init_dict['ca_cn'] = init_dict.get('ca_cn', 'AUTOTEST.VIRT')
init_dict['ca_cakey_path'] = init_dict.get('ca_cakey_path', None)
init_dict['auth_tls'] = init_dict.get('auth_tls', 'none')
init_dict['tls_port'] = init_dict.get('tls_port', '16514')
init_dict['listen_addr'] = init_dict.get('listen_addr')
init_dict['custom_pki_path'] = init_dict.get('custom_pki_path')
init_dict['tls_verify_cert'] = init_dict.get('tls_verify_cert', 'yes')
init_dict['tls_sanity_cert'] = init_dict.get('tls_sanity_cert', 'yes')
init_dict['tls_allowed_dn_list'] = init_dict.get('tls_allowed_dn_list')
init_dict['scp_new_cacert'] = init_dict.get('scp_new_cacert', 'yes')
init_dict['restart_libvirtd'] = init_dict.get('restart_libvirtd', 'yes')
super(TLSConnection, self).__init__(init_dict)
# check and set CERTTOOL in slots
try:
CERTTOOL = os_dep.command("certtool")
except ValueError:
logging.warning("certtool executable not set or found on path, "
"TLS connection will not setup normally")
CERTTOOL = '/bin/true'
self.CERTTOOL = CERTTOOL
# set some pki related dir values
if not self.custom_pki_path:
self.pki_CA_dir = ('/etc/pki/CA/')
self.libvirt_pki_dir = ('/etc/pki/libvirt/')
self.libvirt_pki_private_dir = ('/etc/pki/libvirt/private/')
else:
# set custom certifications path
dir_dict = {'CA': 'pki_CA_dir',
'libvirt': 'libvirt_pki_dir',
'libvirt/private': 'libvirt_pki_private_dir'}
if not os.path.exists(self.custom_pki_path):
os.makedirs(self.custom_pki_path)
for dir_name in dir_dict:
setattr(self, dir_dict[dir_name], self.custom_pki_path)
self.client_hosts = remote.RemoteFile(address=self.client_ip,
client='scp',
username=self.client_user,
password=self.client_pwd,
port='22',
remote_path='/etc/hosts')
self.server_syslibvirtd = remote.RemoteFile(
address=self.server_ip,
client='scp',
username=self.server_user,
password=self.server_pwd,
port='22',
remote_path='/etc/sysconfig/libvirtd')
self.server_libvirtdconf = remote.RemoteFile(
address=self.server_ip,
client='scp',
username=self.server_user,
password=self.server_pwd,
port='22',
remote_path='/etc/libvirt/libvirtd.conf')
[docs] def conn_recover(self):
"""
Do the clean up work.
(1).initialize variables.
(2).Delete remote file.
(3).Restart libvirtd on server.
"""
# clean up certifications firstly
if self.auto_recover:
self.cert_recover()
# initialize variables
server_ip = self.server_ip
server_user = self.server_user
server_pwd = self.server_pwd
del self.client_hosts
del self.server_syslibvirtd
del self.server_libvirtdconf
# restart libvirtd service on server
try:
session = remote.wait_for_login('ssh', server_ip, '22',
server_user, server_pwd,
r"[\#\$]\s*$")
libvirtd_service = utils_libvirtd.Libvirtd(session=session)
libvirtd_service.restart()
except (remote.LoginError, aexpect.ShellError), detail:
raise ConnServerRestartError(detail)
logging.debug("TLS connection recover successfully.")
[docs] def cert_recover(self):
"""
Do the clean up certifications work.
(1).initialize variables.
(2).Delete local and remote generated certifications file.
"""
# initialize variables
server_ip = self.server_ip
server_user = self.server_user
server_pwd = self.server_pwd
cert_dict = {'CA': '%s*' % self.pki_CA_dir,
'cert': self.libvirt_pki_dir,
'key': self.libvirt_pki_private_dir}
# remove local generated certifications file
for cert in cert_dict:
cert_path = cert_dict[cert]
cmd = "rm -rf %s" % cert_path
if os.path.exists(cert_path):
shutil.rmtree(cert_path)
else:
status, output = commands.getstatusoutput(cmd)
if status:
raise ConnRmCertError(cert_path, output)
# remove remote generated certifications file
server_session = remote.wait_for_login('ssh', server_ip, '22',
server_user, server_pwd,
r"[\#\$]\s*$")
for cert in cert_dict:
cert_path = cert_dict[cert]
cmd = "rm -rf %s" % cert_path
status, output = server_session.cmd_status_output(cmd)
if status:
raise ConnRmCertError(cert_path, output)
server_session.close()
logging.debug("TLS certifications recover successfully.")
[docs] def conn_setup(self, server_setup=True, client_setup=True):
"""
setup a TLS connection between server and client.
At first check the certtool needed to setup.
Then call some setup functions to complete connection setup.
"""
if self.CERTTOOL == '/bin/true':
raise ConnToolNotFoundError('certtool',
"certtool executable not set or found on path.")
# support build multiple CAs with different CA CN
build_CA(self.tmp_dir, self.ca_cn, self.ca_cakey_path, self.CERTTOOL)
# not always need to setup CA, client and server together
if server_setup:
self.server_setup()
if client_setup:
self.client_setup()
self.close_session()
logging.debug("TLS connection setup successfully.")
[docs] def server_setup(self):
"""
setup private key and certificate file for server.
(1).initialization for variables.
(2).build server key.
(3).copy files to server.
(4).edit /etc/sysconfig/libvirtd on server.
(5).edit /etc/libvirt/libvirtd.conf on server.
(6).restart libvirtd service on server.
"""
# initialize variables
tmp_dir = self.tmp_dir
scp_new_cacert = self.scp_new_cacert
# sometimes, need to reuse previous CA cert
if self.ca_cakey_path and scp_new_cacert == 'no':
cacert_path = '%s/cacert.pem' % self.ca_cakey_path
else:
cacert_path = '%s/cacert.pem' % tmp_dir
serverkey_path = '%s/serverkey.pem' % tmp_dir
servercert_path = '%s/servercert.pem' % tmp_dir
server_ip = self.server_ip
server_user = self.server_user
server_pwd = self.server_pwd
auth_tls = self.auth_tls
tls_port = self.tls_port
listen_addr = self.listen_addr
restart_libvirtd = self.restart_libvirtd
tls_allowed_dn_list = self.tls_allowed_dn_list
pki_path = self.custom_pki_path
tls_verify_cert = self.tls_verify_cert
tls_sanity_cert = self.tls_sanity_cert
# build a server key.
build_server_key(tmp_dir, self.ca_cakey_path,
self.server_cn, self.CERTTOOL)
# scp cacert.pem, servercert.pem and serverkey.pem to server.
server_session = self.server_session
cmd = "mkdir -p %s" % self.libvirt_pki_private_dir
status, output = server_session.cmd_status_output(cmd)
if status:
raise ConnMkdirError(self.libvirt_pki_private_dir, output)
scp_dict = {cacert_path: self.pki_CA_dir,
servercert_path: self.libvirt_pki_dir,
serverkey_path: self.libvirt_pki_private_dir}
for key in scp_dict:
local_path = key
remote_path = scp_dict[key]
try:
remote.copy_files_to(server_ip, 'scp', server_user,
server_pwd, '22', local_path, remote_path)
except remote.SCPError, detail:
raise ConnSCPError('AdminHost', local_path,
server_ip, remote_path, detail)
# edit the /etc/sysconfig/libvirtd to add --listen args in libvirtd
pattern2repl = {r".*LIBVIRTD_ARGS\s*=\s*\"\s*--listen\s*\".*":
"LIBVIRTD_ARGS=\"--listen\""}
self.server_syslibvirtd.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add listen_tls=1
pattern2repl = {r".*listen_tls\s*=\s*.*": "listen_tls=1"}
self.server_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add
# listen_addr=$listen_addr
if listen_addr:
pattern2repl[r".*listen_addr\s*=.*"] = \
"listen_addr='%s'" % (listen_addr)
self.server_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add auth_tls=$auth_tls
if auth_tls != 'none':
pattern2repl = {r".*auth_tls\s*=\s*.*": 'auth_tls="%s"' % auth_tls}
self.server_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add tls_port=$tls_port
if tls_port != '16514':
pattern2repl = {r".*tls_port\s*=\s*.*": 'tls_port="%s"' % tls_port}
self.server_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add
# tls_allowed_dn_list=$tls_allowed_dn_list
if isinstance(tls_allowed_dn_list, list):
pattern2repl = {r".*tls_allowed_dn_list\s*=\s*.*":
'tls_allowed_dn_list=%s' % tls_allowed_dn_list}
self.server_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to override
# the default server certification file path
if pki_path:
cert_path_dict = {'ca_file': cacert_path,
'key_file': serverkey_path,
'cert_file': servercert_path}
pattern2repl = {}
for cert_name in cert_path_dict:
cert_file = os.path.basename(cert_path_dict[cert_name])
abs_cert_file = os.path.join(pki_path, cert_file)
pattern2repl[r".*%s\s*=.*" % (cert_name)] = \
'%s="%s"' % (cert_name, abs_cert_file)
self.server_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to disable client verification
if tls_verify_cert == "no":
pattern2repl = {r".*tls_no_verify_certificate\s*=\s*.*":
'tls_no_verify_certificate=1'}
self.server_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to disable server sanity checks
if tls_sanity_cert == "no":
pattern2repl = {r".*tls_no_sanity_certificate\s*=\s*.*":
'tls_no_sanity_certificate=1'}
self.server_libvirtdconf.sub_else_add(pattern2repl)
# restart libvirtd service on server
if restart_libvirtd == "yes":
try:
session = remote.wait_for_login('ssh', server_ip, '22',
server_user, server_pwd,
r"[\#\$]\s*$")
libvirtd_service = utils_libvirtd.Libvirtd(session=session)
libvirtd_service.restart()
except (remote.LoginError, aexpect.ShellError), detail:
raise ConnServerRestartError(detail)
[docs] def client_setup(self):
"""
setup private key and certificate file for client.
(1).initialization for variables.
(2).build a key for client.
(3).copy files to client.
(4).edit /etc/hosts on client.
"""
# initialize variables
tmp_dir = self.tmp_dir
cacert_path = '%s/cacert.pem' % tmp_dir
clientkey_path = '%s/clientkey.pem' % tmp_dir
clientcert_path = '%s/clientcert.pem' % tmp_dir
client_ip = self.client_ip
client_user = self.client_user
client_pwd = self.client_pwd
# build a client key.
build_client_key(tmp_dir, self.client_cn, self.CERTTOOL)
# scp cacert.pem, clientcert.pem and clientkey.pem to client.
client_session = self.client_session
cmd = "mkdir -p %s" % self.libvirt_pki_private_dir
status, output = client_session.cmd_status_output(cmd)
if status:
raise ConnMkdirError(self.libvirt_pki_private_dir, output)
scp_dict = {cacert_path: self.pki_CA_dir,
clientcert_path: self.libvirt_pki_dir,
clientkey_path: self.libvirt_pki_private_dir}
for key in scp_dict:
local_path = key
remote_path = scp_dict[key]
try:
remote.copy_files_to(client_ip, 'scp', client_user,
client_pwd, '22', local_path, remote_path)
except remote.SCPError, detail:
raise ConnSCPError('AdminHost', local_path,
client_ip, remote_path, detail)
# edit /etc/hosts on client
pattern2repl = {r".*%s.*" % self.server_cn:
"%s %s" % (self.server_ip, self.server_cn)}
self.client_hosts.sub_else_add(pattern2repl)
[docs]def build_client_key(tmp_dir, client_cn="TLSClient", certtool="certtool"):
"""
(1).initialization for variables.
(2).make a private key with certtool command.
(3).prepare a info file.
(4).make a certificate file with certtool command.
"""
# Initialize variables
cakey_path = '%s/cakey.pem' % tmp_dir
cacert_path = '%s/cacert.pem' % tmp_dir
clientkey_path = '%s/clientkey.pem' % tmp_dir
clientcert_path = '%s/clientcert.pem' % tmp_dir
clientinfo_path = '%s/client.info' % tmp_dir
# make a private key.
cmd = "%s --generate-privkey > %s" % (certtool, clientkey_path)
CmdResult = utils.run(cmd, ignore_status=True)
if CmdResult.exit_status:
raise ConnPrivKeyError(CmdResult.stderr)
# prepare a info file to build clientcert.
clientinfo_file = open(clientinfo_path, "w")
clientinfo_file.write("organization = AUTOTEST.VIRT\n")
clientinfo_file.write("cn = %s\n" % (client_cn))
clientinfo_file.write("tls_www_client\n")
clientinfo_file.write("encryption_key\n")
clientinfo_file.write("signing_key\n")
clientinfo_file.close()
# make a client certificate file and a client key file.
cmd = ("%s --generate-certificate --load-privkey %s \
--load-ca-certificate %s --load-ca-privkey %s \
--template %s --outfile %s" %
(certtool, clientkey_path, cacert_path,
cakey_path, clientinfo_path, clientcert_path))
CmdResult = utils.run(cmd, ignore_status=True)
if CmdResult.exit_status:
raise ConnCertError(clientinfo_path, CmdResult.stderr)
[docs]def build_server_key(tmp_dir, ca_cakey_path=None,
server_cn="TLSServer", certtool="certtool"):
"""
(1).initialization for variables.
(2).make a private key with certtool command.
(3).prepare a info file.
(4).make a certificate file with certtool command.
"""
# initialize variables
# sometimes, need to reuse previous CA cert
if not ca_cakey_path:
cakey_path = '%s/cakey.pem' % tmp_dir
else:
cakey_path = '%s/cakey.pem' % ca_cakey_path
serverkey_path = '%s/serverkey.pem' % tmp_dir
cacert_path = '%s/cacert.pem' % tmp_dir
servercert_path = '%s/servercert.pem' % tmp_dir
serverinfo_path = '%s/server.info' % tmp_dir
# make a private key
cmd = "%s --generate-privkey > %s" % (certtool, serverkey_path)
cmd_result = utils.run(cmd, ignore_status=True)
if cmd_result.exit_status:
raise ConnPrivKeyError(serverkey_path, cmd_result.stderr)
# prepare a info file to build servercert and serverkey
serverinfo_file = open(serverinfo_path, "w")
serverinfo_file.write("organization = AUTOTEST.VIRT\n")
serverinfo_file.write("cn = %s\n" % (server_cn))
serverinfo_file.write("tls_www_server\n")
serverinfo_file.write("encryption_key\n")
serverinfo_file.write("signing_key\n")
serverinfo_file.close()
# make a server certificate file and a server key file
cmd = ("%s --generate-certificate --load-privkey %s \
--load-ca-certificate %s --load-ca-privkey %s \
--template %s --outfile %s" %
(certtool, serverkey_path, cacert_path,
cakey_path, serverinfo_path, servercert_path))
CmdResult = utils.run(cmd, ignore_status=True)
if CmdResult.exit_status:
raise ConnCertError(serverinfo_path, CmdResult.stderr)
[docs]def build_CA(tmp_dir, cn="AUTOTEST.VIRT", ca_cakey_path=None, certtool="certtool"):
"""
setup private key and certificate file which are needed to build.
certificate file for client and server.
(1).initialization for variables.
(2).make a private key with certtool command.
(3).prepare a info file.
(4).make a certificate file with certtool command.
"""
# initialize variables
if not ca_cakey_path:
cakey_path = '%s/cakey.pem' % tmp_dir
else:
cakey_path = '%s/cakey.pem' % ca_cakey_path
cainfo_path = '%s/ca.info' % tmp_dir
cacert_path = '%s/cacert.pem' % tmp_dir
# make a private key
# sometimes, may reuse previous CA cert, so don't always need to
# generate private key
if not ca_cakey_path:
cmd = "%s --generate-privkey > %s " % (certtool, cakey_path)
cmd_result = utils.run(cmd, ignore_status=True, timeout=10)
if cmd_result.exit_status:
raise ConnPrivKeyError(cakey_path, cmd_result.stderr)
# prepare a info file to build certificate file
cainfo_file = open(cainfo_path, "w")
cainfo_file.write("cn = %s\n" % cn)
cainfo_file.write("ca\n")
cainfo_file.write("cert_signing_key\n")
cainfo_file.close()
# make a certificate file to build clientcert and servercert
cmd = ("%s --generate-self-signed --load-privkey %s\
--template %s --outfile %s" %
(certtool, cakey_path, cainfo_path, cacert_path))
CmdResult = utils.run(cmd, ignore_status=True)
if CmdResult.exit_status:
raise ConnCertError(cainfo_path, CmdResult.stderr)
[docs]class UNIXConnection(ConnectionBase):
"""
Connection class for UNIX transport.
Some specific variables for UNIXConnection class.
"""
__slots__ = ('auth_unix_ro', 'auth_unix_rw', 'unix_sock_dir',
'unix_sock_group', 'unix_sock_ro_perms',
'unix_sock_rw_perms', 'access_drivers',
'client_ip', 'client_user', 'client_pwd',
'client_libvirtdconf', 'restart_libvirtd')
def __init__(self, *args, **dargs):
"""
init params for UNIX connection.
:param auth_unix_ro: UNIX R/O sockets, default is 'none'.
:param auth_unix_rw: UNIX R/W sockets, default is 'none'.
:param unix_sock_group: UNIX domain socket group ownership,
default is 'libvirt'.
:param access_drivers: access control restrictions,
default is '["polkit"]'.
:param unix_sock_ro_perms: UNIX socket permissions for the
R/O socket, default is '0777'.
:param unix_sock_rw_perms: UNIX socket permissions for the
R/W socket, default is '0770'.
:param client_libvirtdconf: Path of client libvirtd.conf, default is
'/etc/libvirt/libvirtd.conf'.
:param restart_libvirtd: default is to restart libvirtd.
"""
init_dict = dict(*args, **dargs)
init_dict['auth_unix_ro'] = init_dict.get('auth_unix_ro', 'none')
init_dict['auth_unix_rw'] = init_dict.get('auth_unix_rw', 'none')
init_dict['unix_sock_dir'] = init_dict.get('unix_sock_dir', '/var/run/libvirt')
init_dict['unix_sock_group'] = init_dict.get('unix_sock_group', 'libvirt')
init_dict['access_drivers'] = init_dict.get('access_drivers', ["polkit"])
init_dict['unix_sock_ro_perms'] = init_dict.get('unix_sock_ro_perms', '0777')
init_dict['unix_sock_rw_perms'] = init_dict.get('unix_sock_rw_perms', '0770')
init_dict['restart_libvirtd'] = init_dict.get('restart_libvirtd', 'yes')
super(UNIXConnection, self).__init__(init_dict)
self.client_libvirtdconf = remote.RemoteFile(
address=self.client_ip,
client='scp',
username=self.client_user,
password=self.client_pwd,
port='22',
remote_path='/etc/libvirt/libvirtd.conf')
[docs] def conn_recover(self):
"""
Do the clean up work.
(1).Delete remote file.
(2).Restart libvirtd on server.
"""
del self.client_libvirtdconf
# restart libvirtd service on server
client_session = self.client_session
try:
libvirtd_service = utils_libvirtd.Libvirtd(session=client_session)
libvirtd_service.restart()
except (remote.LoginError, aexpect.ShellError), detail:
raise ConnServerRestartError(detail)
logging.debug("UNIX connection recover successfully.")
[docs] def conn_setup(self):
"""
Setup a UNIX connection.
(1).Initialize variables.
(2).Update libvirtd.conf configuration.
(3).Restart libvirtd on client.
"""
# initialize variables
auth_unix_ro = self.auth_unix_ro
auth_unix_rw = self.auth_unix_rw
unix_sock_group = self.unix_sock_group
unix_sock_dir = self.unix_sock_dir
unix_sock_ro_perms = self.unix_sock_ro_perms
unix_sock_rw_perms = self.unix_sock_rw_perms
access_drivers = self.access_drivers
restart_libvirtd = self.restart_libvirtd
client_session = self.client_session
# edit the /etc/libvirt/libvirtd.conf to add auth_unix_ro arg
if auth_unix_ro:
pattern2repl = {r".*auth_unix_ro\s*=.*":
'auth_unix_ro="%s"' % auth_unix_ro}
self.client_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add auth_unix_rw arg
if auth_unix_rw:
pattern2repl = {r".*auth_unix_rw\s*=.*":
'auth_unix_rw="%s"' % auth_unix_rw}
self.client_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add unix_sock_group arg
if unix_sock_group != 'libvirt':
pattern2repl = {r".*unix_sock_group\s*=.*":
'unix_sock_group="%s"' % unix_sock_group}
self.client_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add unix_sock_dir arg
if unix_sock_dir != '/var/run/libvirt':
pattern2repl = {r".*unix_sock_dir\s*=.*":
'unix_sock_dir="%s"' % unix_sock_dir}
self.client_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add access_drivers arg
if access_drivers != ["polkit"]:
pattern2repl = {r".*access_drivers\s*=.*":
'access_drivers="%s"' % access_drivers}
self.client_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add unix_sock_ro_perms arg
if unix_sock_ro_perms:
pattern2repl = {r".*unix_sock_ro_perms\s*=.*":
'unix_sock_ro_perms="%s"' % unix_sock_ro_perms}
self.client_libvirtdconf.sub_else_add(pattern2repl)
# edit the /etc/libvirt/libvirtd.conf to add unix_sock_rw_perms arg
if unix_sock_rw_perms:
pattern2repl = {r".*unix_sock_rw_perms\s*=.*":
'unix_sock_rw_perms="%s"' % unix_sock_rw_perms}
self.client_libvirtdconf.sub_else_add(pattern2repl)
# restart libvirtd service on server
if restart_libvirtd == "yes":
try:
libvirtd_service = utils_libvirtd.Libvirtd(session=client_session)
libvirtd_service.restart()
except (remote.LoginError, aexpect.ShellError), detail:
raise ConnServerRestartError(detail)
logging.debug("UNIX connection setup successfully.")