Skip to content

Commit

Permalink
Merge pull request #206 from DeliZhangX/private/deliz/CP-35235
Browse files Browse the repository at this point in the history
CP-35235: Replace paramiko by ssh command
  • Loading branch information
DeliZhangX authored Aug 3, 2023
2 parents 3e15924 + 2826d65 commit a75c1db
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 334 deletions.
158 changes: 158 additions & 0 deletions autocertkit/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Copyright (c) 2023-07-20 Cloud Software Group Holdings, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms,
# with or without modification, are permitted provided
# that the following conditions are met:
#
# * Redistributions of source code must retain the above
# copyright notice, this list of conditions and the
# following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the
# following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

import os
import time
import subprocess
import uuid


SSH = '/usr/bin/ssh'
SCP = '/usr/bin/scp'
EXPECT = '/usr/bin/expect'
ACK_HOME = '/opt/xensource/packages/files/auto-cert-kit/'


def set_logger(logger):
global log
log = logger


def make_local_call(call, logging=True, std_out=subprocess.PIPE,
std_err=subprocess.PIPE, shell=False, timeout=None):
"""Function wrapper for making a simple call to shell"""
if logging:
log.debug(f"make_local_call: {call}")
process = subprocess.Popen(call, stdout=std_out, stderr=std_err,
shell=shell, universal_newlines=True)
stdout, stderr = process.communicate(timeout=timeout)
res = {"returncode": process.returncode, "stdout": stdout.strip(),
"stderr": str(stderr).strip()}
if logging:
log.debug("returncode: %d" % process.returncode)
log.debug("stdout: %s" % str(stdout))
log.debug("stderr: %s" % str(stderr))
if res["returncode"] != 0:
log.error(f"ERR: Could not make local call: {call}")

return res


class SecureChannel:
"""Wrap of ssh and scp"""

def __init__(self, ip, user, password, timeout=300):
self.ip = ip
self.user = user
self.password = password
self.timeout = timeout

def _wrap_cmd(self, cmd):
escape_cmd = cmd.replace('$', '\$')
return fr'''{EXPECT} << EOF
set timeout {self.timeout}
spawn {escape_cmd}
expect {{
"continue connecting (yes/no)?" {{send "yes\n"; exp_continue}}
"password:" {{send "{self.password}\n"; exp_continue}}
eof {{catch wait result; exit [lindex \$result 3]}}
timeout {{exit 250}}
}}
EOF
'''

def _wrap_ssh(self, cmd):
return self._wrap_cmd(f'{SSH} {self.user}@{self.ip} {{ {cmd} }}')

def run_cmd(self, cmd):
"""Run command simply and ignore stderr"""
return make_local_call(self._wrap_ssh(cmd), shell=True, timeout=self.timeout)

def run_cmd_ext(self, cmd):
"""Run command and capture stdout and stderr separately"""
id = str(uuid.uuid4())
fcmd = f'.ack_cmd.{id}'
frc = f'.ack_rc.{id}'
fout = f'.ack_out.{id}'
ferr = f'.ack_err.{id}'

with open(f'/tmp/{fcmd}', 'w') as f:
f.write(cmd)
self.put_file(f'/tmp/{fcmd}')

self.run_cmd(fr'sh {fcmd} >{fout} 2>{ferr}; echo "$?" >{frc}')

self.get_file(f'.ack_*.{id}', '/tmp/')
contents = []
for f in [frc, fout, ferr]:
with open(f'/tmp/{f}', 'r') as f:
contents.append(f.read().strip())

self.run_cmd(fr'rm -f .ack_*.{id}')
for f in [fcmd, frc, fout, ferr]:
os.remove(f'/tmp/{f}')

res = {'returncode': int(contents[0]), 'stdout': contents[1], 'stderr': contents[2]}
log.debug(f'Real result: {res}')

return res

def _wrap_scp(self, src, dst):
return self._wrap_cmd(f'{SCP} {src} {dst}')

def put_file(self, src, dst=''):
cmd = self._wrap_scp(src, f'{self.user}@{self.ip}:{dst}')
return make_local_call(cmd, shell=True, timeout=self.timeout)

def get_file(self, src, dst='.'):
cmd = self._wrap_scp(f'{self.user}@{self.ip}:{src}', dst)
return make_local_call(cmd, shell=True, timeout=self.timeout)


def ssh_command(ip, username, password, cmd_str, dbg_str=None, attempts=10, timeout=900):
"""execute an SSH command, return both exit code, stdout and stderr."""
if dbg_str:
log.debug(dbg_str)

for i in range(0, attempts):
log.debug("Attempt %d/%d: %s" % (i, attempts, cmd_str))

try:
result = SecureChannel(ip, username, password, timeout).run_cmd_ext(cmd_str)
except Exception as e:
log.debug("Exception: %s" % str(e))
# Sleep before next attempt
time.sleep(20)
else:
return result

log.debug("Max attempt reached %d/%d" % (attempts, attempts))
return {"returncode": -1, "stdout": "", "stderr": "An unkown error has occured!"}

20 changes: 11 additions & 9 deletions autocertkit/network_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,6 @@ def __init__(self, session,

self.config = opt.get('config', self.default_config)

# Store pool master in order to make plugin calls
self.host = get_pool_master(self.session)

self.timeout = 60

# Validate the references and setup run method
Expand Down Expand Up @@ -234,11 +231,16 @@ def run(self):
def deploy_iperf(self):
"""deploy iPerf on both client and server"""
def deploy(vm_ref):
self.plugin_call('deploy_iperf',
{'vm_ref': vm_ref,
'mip': self.vm_info[vm_ref]['ip_m'],
'username': self.username,
'password': self.password})
host = None
if self.session.xenapi.VM.get_is_control_domain(vm_ref):
host = self.session.xenapi.VM.get_resident_on(vm_ref)

call_ack_plugin(self.session, 'deploy_iperf',
{'vm_ref': vm_ref,
'mip': self.vm_info[vm_ref]['ip_m'],
'username': self.username,
'password': self.password},
host)

deploy(self.client)
deploy(self.server)
Expand Down Expand Up @@ -296,7 +298,7 @@ def parse_iperf_line(self, data):

def plugin_call(self, method, args):
"""Make a plugin call to autocertkit"""
return call_ack_plugin(self.session, method, args, self.host)
return call_ack_plugin(self.session, method, args)

def get_iperf_client_cmd(self):
params = []
Expand Down
Loading

0 comments on commit a75c1db

Please sign in to comment.