Switch to SSHClient API usage in execute_on_remote

Switch to SSHClient API usage in execute_on_remote: remote.check_call
check_call has been aligned to SSHClient

Change-Id: I6a6a0db5ccc1175c296a64d0c928d51558d51189
This commit is contained in:
Alexey Stepanov 2016-08-10 14:29:13 +03:00
parent 5e5d98a024
commit 8e90a5b653
5 changed files with 67 additions and 87 deletions

View File

@ -156,14 +156,15 @@ def verify_network_list_api(os_conn, net_count=None):
def check_ceph_image_size(ip, expected_size, device='vdc'): def check_ceph_image_size(ip, expected_size, device='vdc'):
ret = ssh_manager.check_call( ret = ssh_manager.check_call(
ip=ip, ip=ip,
cmd="df -m /dev/{device}* | grep ceph | awk" command="df -m /dev/{device}* | grep ceph | awk"
" {size}".format(device=device, " {size}".format(device=device,
size=re.escape('{print $2}')) size=re.escape('{print $2}'))
)['stdout'] ).stdout
if not ret: if not ret:
logger.error("Partition not present! {}: ".format( logger.error(
ssh_manager.check_call(ip=ip, cmd="df -m"))) "Partition not present! {}: ".format(
ssh_manager.check_call(ip=ip, command="df -m").stdout_str))
raise Exception() raise Exception()
logger.debug("Partitions: {part}".format(part=ret)) logger.debug("Partitions: {part}".format(part=ret))
assert_true(abs(float(ret[0].rstrip()) / expected_size - 1) < 0.1, assert_true(abs(float(ret[0].rstrip()) / expected_size - 1) < 0.1,
@ -238,7 +239,7 @@ def enable_feature_group(env, group):
# update nailgun configs via puppet from that value # update nailgun configs via puppet from that value
ssh_manager.check_call( ssh_manager.check_call(
ip=ssh_manager.admin_ip, ip=ssh_manager.admin_ip,
cmd='puppet apply /etc/puppet/modules/fuel/examples/nailgun.pp' command='puppet apply /etc/puppet/modules/fuel/examples/nailgun.pp'
) )
def check_api_group_enabled(): def check_api_group_enabled():

View File

@ -11,31 +11,3 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
class UnexpectedExitCode(Exception):
def __init__(self, command, ec, expected_ec, stdout=None, stderr=None):
"""Exception for unexpected exit code after executing shell/ssh command
:param command: str - executed command
:param ec: int - actual exit code
:param expected_ec: list of integers - expected exit codes
:param stdout: str
:param stderr: str
"""
self.ec = ec
self.expected_ec = expected_ec
self.cmd = command
self.stdout = stdout
self.stderr = stderr
super(UnexpectedExitCode, self).__init__()
def __str__(self):
message = "Command '{cmd:s}' returned unexpected exit code {code:d}," \
" while waiting for {exp}".format(cmd=self.cmd,
code=self.ec,
exp=self.expected_ec)
if self.stdout:
message += "stdout: {}\n".format(self.stdout)
if self.stderr:
message += "stderr: {}\n".format(self.stderr)
return message

View File

@ -17,6 +17,7 @@ import os
import posixpath import posixpath
import re import re
import traceback import traceback
from warnings import warn
from devops.helpers.helpers import wait from devops.helpers.helpers import wait
from devops.models.node import SSHClient from devops.models.node import SSHClient
@ -26,7 +27,6 @@ import yaml
from fuelweb_test import logger from fuelweb_test import logger
from fuelweb_test.helpers.metaclasses import SingletonMeta from fuelweb_test.helpers.metaclasses import SingletonMeta
from fuelweb_test.helpers.exceptions import UnexpectedExitCode
@six.add_metaclass(SingletonMeta) @six.add_metaclass(SingletonMeta)
@ -85,8 +85,10 @@ class SSHManager(object):
""" Function returns remote SSH connection to node by ip address """ Function returns remote SSH connection to node by ip address
:param ip: IP of host :param ip: IP of host
:type ip: str
:param port: port for SSH :param port: port for SSH
:return: SSHClient :type port: int
:rtype: SSHClient
""" """
if (ip, port) not in self.connections: if (ip, port) not in self.connections:
logger.debug('SSH_MANAGER:Create new connection for ' logger.debug('SSH_MANAGER:Create new connection for '
@ -142,9 +144,34 @@ class SSHManager(object):
remote = self._get_remote(ip=ip, port=port) remote = self._get_remote(ip=ip, port=port)
return remote.execute(cmd) return remote.execute(cmd)
def check_call(self, ip, cmd, port=22, verbose=False): def check_call(
self,
ip,
command, port=22, verbose=False, timeout=None,
error_info=None,
expected=None, raise_on_err=True):
"""Execute command and check for return code
:type ip: str
:type command: str
:type port: int
:type verbose: bool
:type timeout: int
:type error_info: str
:type expected: list
:type raise_on_err: bool
:rtype: ExecResult
:raises: DevopsCalledProcessError
"""
remote = self._get_remote(ip=ip, port=port) remote = self._get_remote(ip=ip, port=port)
return remote.check_call(cmd, verbose) return remote.check_call(
command=command,
verbose=verbose,
timeout=timeout,
error_info=error_info,
expected=expected,
raise_on_err=raise_on_err
)
def execute_on_remote(self, ip, cmd, port=22, err_msg=None, def execute_on_remote(self, ip, cmd, port=22, err_msg=None,
jsonify=False, assert_ec_equal=None, jsonify=False, assert_ec_equal=None,
@ -168,11 +195,16 @@ class SSHManager(object):
if yamlify and jsonify: if yamlify and jsonify:
raise ValueError('Conflicting arguments: yamlify and jsonify!') raise ValueError('Conflicting arguments: yamlify and jsonify!')
orig_result = self.execute(ip=ip, port=port, cmd=cmd) remote = self._get_remote(ip=ip, port=port)
orig_result = remote.check_call(
command=cmd,
error_info=err_msg,
expected=assert_ec_equal,
raise_on_err=raise_on_assert
)
# Now create fallback result # Now create fallback result
# TODO(astepanov): switch to SSHClient output after tests adoptation # TODO(astepanov): switch to SSHClient output after tests adoptation
# TODO(astepanov): process whole parameters on SSHClient().check_call()
result = { result = {
'stdout': orig_result['stdout'], 'stdout': orig_result['stdout'],
@ -182,43 +214,10 @@ class SSHManager(object):
'stderr_str': ''.join(orig_result['stderr']).strip(), 'stderr_str': ''.join(orig_result['stderr']).strip(),
} }
details_log = (
"Host: {host}\n"
"Command: '{cmd}'\n"
"Exit code: {code}\n"
"STDOUT:\n{stdout}\n"
"STDERR:\n{stderr}".format(
host=ip, cmd=cmd, code=result['exit_code'],
stdout=result['stdout_str'], stderr=result['stderr_str']
))
if result['exit_code'] not in assert_ec_equal:
error_msg = (
err_msg or
"Unexpected exit_code returned: actual {0}, expected {1}."
"".format(
result['exit_code'],
' '.join(map(str, assert_ec_equal))))
log_msg = (
"{0} Command: '{1}' "
"Details:\n{2}".format(
error_msg, cmd, details_log))
logger.error(log_msg)
if raise_on_assert:
raise UnexpectedExitCode(cmd,
result['exit_code'],
assert_ec_equal,
stdout=result['stdout_str'],
stderr=result['stderr_str'])
else:
logger.debug(details_log)
if jsonify: if jsonify:
result['stdout_json'] = \ result['stdout_json'] = orig_result.stdout_json
self._json_deserialize(result['stdout_str'])
elif yamlify: elif yamlify:
result['stdout_yaml'] = \ result['stdout_yaml'] = orig_result.stdout_yaml
self._yaml_deserialize(result['stdout_str'])
return result return result
@ -234,6 +233,10 @@ class SSHManager(object):
:return: obj :return: obj
:raise: Exception :raise: Exception
""" """
warn(
'_json_deserialize is not used anymore and will be removed later',
DeprecationWarning)
if isinstance(json_string, list): if isinstance(json_string, list):
json_string = ''.join(json_string).strip() json_string = ''.join(json_string).strip()
@ -254,6 +257,10 @@ class SSHManager(object):
:return: obj :return: obj
:raise: Exception :raise: Exception
""" """
warn(
'_yaml_deserialize is not used anymore and will be removed later',
DeprecationWarning)
if isinstance(yaml_string, list): if isinstance(yaml_string, list):
yaml_string = ''.join(yaml_string).strip() yaml_string = ''.join(yaml_string).strip()

View File

@ -1044,14 +1044,14 @@ def get_ceph_partitions(ip, device, fs_type="xfs"):
# Moved from checkers.py for improvement of code # Moved from checkers.py for improvement of code
ret = SSHManager().check_call( ret = SSHManager().check_call(
ip=ip, ip=ip,
cmd="parted {device} print | grep {type}".format(device=device, command="parted {device} print | grep {type}".format(device=device,
type=fs_type) type=fs_type)
)['stdout'] ).stdout
if not ret: if not ret:
logger.error( logger.error(
"Partition not present! {partitions}: ".format( "Partition not present! {partitions}: ".format(
partitions=SSHManager().check_call( partitions=SSHManager().check_call(
ip=ip, cmd="parted {device} print"))) ip=ip, command="parted {device} print").stdout_str))
raise Exception() raise Exception()
logger.debug("Partitions: {part}".format(part=ret)) logger.debug("Partitions: {part}".format(part=ret))
return ret return ret
@ -1062,7 +1062,7 @@ def get_mongo_partitions(ip, device):
# Moved from checkers.py for improvement of code # Moved from checkers.py for improvement of code
ret = SSHManager().check_call( ret = SSHManager().check_call(
ip=ip, ip=ip,
cmd="lsblk | grep {device} | awk {size}".format( command="lsblk | grep {device} | awk {size}".format(
device=device, device=device,
size=re.escape('{print $4}')) size=re.escape('{print $4}'))
)['stdout'] )['stdout']
@ -1070,7 +1070,7 @@ def get_mongo_partitions(ip, device):
logger.error( logger.error(
"Partition not present! {partitions}: ".format( "Partition not present! {partitions}: ".format(
partitions=SSHManager().check_call( partitions=SSHManager().check_call(
ip=ip, cmd="parted {device} print"))) ip=ip, command="parted {device} print").stdout_str))
raise Exception() raise Exception()
logger.debug("Partitions: {part}".format(part=ret)) logger.debug("Partitions: {part}".format(part=ret))
return ret return ret
@ -1147,8 +1147,8 @@ def get_quantity_of_numa(ip):
numa = int(SSHManager().check_call( numa = int(SSHManager().check_call(
ip=ip, ip=ip,
cmd="lstopo | grep NUMANode| wc -l" command="lstopo | grep NUMANode| wc -l"
)['stdout'][0]) ).stdout[0])
if not numa: if not numa:
logger.debug("There are no NUMA nodes on {0}".format(ip)) logger.debug("There are no NUMA nodes on {0}".format(ip))

View File

@ -16,6 +16,7 @@ import textwrap
from devops.helpers.helpers import tcp_ping from devops.helpers.helpers import tcp_ping
from devops.helpers.helpers import wait from devops.helpers.helpers import wait
from devops.error import DevopsCalledProcessError
from proboscis.asserts import assert_equal from proboscis.asserts import assert_equal
from proboscis.asserts import assert_not_equal from proboscis.asserts import assert_not_equal
from proboscis.asserts import assert_raises from proboscis.asserts import assert_raises
@ -28,7 +29,6 @@ from fuelweb_test.helpers import utils
from fuelweb_test.helpers.decorators import log_snapshot_after_test from fuelweb_test.helpers.decorators import log_snapshot_after_test
from fuelweb_test import settings from fuelweb_test import settings
from fuelweb_test.tests import base_test_case from fuelweb_test.tests import base_test_case
from fuelweb_test.helpers.exceptions import UnexpectedExitCode
@test(groups=["ubuntu_bootstrap_builder", "bvt_ubuntu_bootstrap"]) @test(groups=["ubuntu_bootstrap_builder", "bvt_ubuntu_bootstrap"])
@ -296,7 +296,7 @@ class UbuntuBootstrapBuild(base_test_case.TestBasic):
"Bootstrap {0} was not deleted and still available: {1}" "Bootstrap {0} was not deleted and still available: {1}"
.format(uuid, bootstrap_uuids)) .format(uuid, bootstrap_uuids))
assert_raises(UnexpectedExitCode, assert_raises(DevopsCalledProcessError,
self.env.fuel_bootstrap_actions.activate_bootstrap_image, self.env.fuel_bootstrap_actions.activate_bootstrap_image,
uuid) uuid)
@ -311,7 +311,7 @@ class UbuntuBootstrapBuild(base_test_case.TestBasic):
assert_true(uuid is not None, "No active bootstrap. Possibly centos " assert_true(uuid is not None, "No active bootstrap. Possibly centos "
"is active or something went wrong.") "is active or something went wrong.")
assert_raises( assert_raises(
UnexpectedExitCode, DevopsCalledProcessError,
self.env.fuel_bootstrap_actions.delete_bootstrap_image, self.env.fuel_bootstrap_actions.delete_bootstrap_image,
uuid) uuid)