def exec_cmd(self, command): print "Executing command " + command + " on host " + self.host lib = SSHLibrary() lib.open_connection(self.host) lib.login(username=self.user,password=self.password) lib.execute_command(command) lib.close_connection()
def execute_ssh_command(ip, username, password, command): print "executing ssh command" lib = SSHLibrary() lib.open_connection(ip) lib.login(username=username, password=password) print "login done" lib.execute_command(command) print "command executed : " + command lib.close_connection()
def execute_ssh_command(ip, username, password, command): print "executing ssh command" lib = SSHLibrary() lib.open_connection(ip) lib.login(username=username,password=password) print "login done" lib.execute_command(command) print "command executed : " + command lib.close_connection()
class IssueCmd(): """ Class to facilitate running commands on remote systems """ def __init__(self): self._ssh_lib = SSHLibrary() def get_creds(self, host): """ Get the server credentials """ user = UserIdentity() cur_dir = os.path.dirname(os.path.abspath(__file__)) creds_file = os.path.join(cur_dir, "../config/", host + ".yaml") with open(creds_file) as creds: try: data = yaml.safe_load(creds) user.username = data["default"]["username"] user.password = data["default"]["password"] return user except yaml.YAMLError as exc: print(exc) def _open_conn(self, host): user = self.get_creds(host) try: self._ssh_lib.open_connection(host) try: self._ssh_lib.login(user.username, user.password) print(f"Connected to host: {host} successfully") except SSHException: print(f"Login to host: {host} failed") except (socket.error, OSError, TimeoutError) as ex: print(f"Connection to host: {host} failed") raise ex def run_cmd(self, command, host): """ Run a command remotely (using ssh) """ print(f"[CMD]: {command}") self._open_conn(host) stdout, stderr, ret_cd = self._ssh_lib.execute_command( command, return_stdout=True, return_stderr=True, return_rc=True) print(f"Exit Value:[{ret_cd}]") print(f"[STDOUT]: {stdout}") if stderr: print(f"[STDERR]: {stderr}") return ret_cd, stdout, stderr
def _get_os_name(self, ssh_client: SSHLibrary): out, err, rc = ssh_client.execute_command( "cat /etc/os-release|grep -E '^ID_LIKE='|awk -F'=' '{print$2}'", return_rc=True, return_stderr=True) assert rc == 0, "Cannot occur OS name" out = out.replace(r'"', '') for _os in self.OS_DATE_FORMAT.keys(): if _os in out: out = _os break logger.debug(f"OS resolved: {out}") return out
def execute_ssh_command(ip, username, password, command): """Execute SSH Command use username and password of controller server for ssh and need karaf distribution location like /root/Documents/dist """ print("executing ssh command") lib = SSHLibrary() lib.open_connection(ip) lib.login(username=username, password=password) print("login done") cmd_response = lib.execute_command(command) print("command executed : " + command) lib.close_connection() return cmd_response
def execute_ssh_command(ip, username, password, command): """Execute SSH Command use username and password of controller server for ssh and need karaf distribution location like /root/Documents/dist """ print "executing ssh command" lib = SSHLibrary() lib.open_connection(ip) lib.login(username=username, password=password) print "login done" cmd_response = lib.execute_command(command) print "command executed : " + command lib.close_connection() return cmd_response
def RSA_ssh_copy_key(self, host, username, password): """ Login With Public Key(username, keyLocations['privateKey'], 'passphrase') """ sshLibSession = SSHLibrary(loglevel='WARN') fo = open(os.path.join(self.keyStore, self.opensshKeyName), "rb") sshKey = fo.read() fo.close() sshLibSession.open_connection(host) sshLibSession.login(username, password) sshLibSession.execute_command("mkdir .ssh") sshLibSession.execute_command((("echo %s > .ssh/authorized_keys") % (sshKey))) sshLibSession.execute_command("chmod 700 .ssh") sshLibSession.execute_command("chmod 600 .ssh/authorized_keys") sshLibSession.close_connection()
class RemoteHost: def __init__(self, host, user, password, rootdir, keyfile=None): self.host = host self.user = user self.password = password self.rootdir = rootdir self.keyfile = keyfile self.lib = SSHLibrary() self.lib.open_connection(self.host) if self.keyfile is not None: self.lib.login_with_public_key(username=self.user, keyfile=self.keyfile) else: self.lib.login(username=self.user, password=self.password) def __del__(self): self.lib.close_connection() def exec_cmd(self, command): print "Executing command " + command + " on host " + self.host rc = self.lib.execute_command(command, return_rc=True) if rc[1] != 0: raise Exception('remote command failed [{0}] with exit code {1}.' 'For linux-based vms, Please make sure requiretty is disabled in the /etc/sudoers file' .format(command, rc)) def mkdir(self, dir_name): self.exec_cmd("mkdir -p " + dir_name) def copy_file(self, src, dest): if src is None: print "src is None not copy anything to " + dest return if os.path.exists(src) is False: print "Src file " + src + " was not found" return print "Copying " + src + " to " + dest + " on " + self.host self.lib.put_file(src, dest) def kill_controller(self): self.exec_cmd("sudo ps axf | grep karaf | grep -v grep " "| awk '{print \"kill -9 \" $1}' | sudo sh") def start_controller(self, dir_name): self.exec_cmd(dir_name + "/odl/bin/start")
def service_check(hostname, login, password): ssh = SSHLibrary() try: ssh.open_connection(hostname) ssh.login(login, password) except: print('Erro ao conectar no host!') ssh_command = ssh.execute_command('systemctl status nginx') if str(ssh_command).find('(running)') != -1: status = 'Servico ok!' else: status = 'Servico com problemas!' return status
def wait_for_controller_stopped(ip, username, password, karafHome): lib = SSHLibrary() lib.open_connection(ip) lib.login(username=username, password=password) # Wait 1 minute for the controller to stop gracefully tries = 20 i = 1 while i <= tries: stdout = lib.execute_command("ps -axf | grep karaf | grep -v grep | wc -l") processCnt = stdout[0].strip('\n') print("processCnt: " + processCnt) if processCnt == '0': break i = i + 1 time.sleep(3) lib.close_connection() if i > tries: print("Killing controller") kill_controller(ip, username, password, karafHome)
def wait_for_controller_stopped(ip, username, password, karafHome): lib = SSHLibrary() lib.open_connection(ip) lib.login(username=username, password=password) # Wait 1 minute for the controller to stop gracefully tries = 20 i = 1 while i <= tries: stdout = lib.execute_command("ps -axf | grep karaf | grep -v grep | wc -l") # print "stdout: "+stdout processCnt = stdout[0].strip('\n') print("processCnt: " + processCnt) if processCnt == '0': break i = i + 1 time.sleep(3) lib.close_connection() if i > tries: print "Killing controller" kill_controller(ip, username, password, karafHome)
def issue_cmd_via_root(command, host, username=HOST_USER, pwd=HOST_PWD, timeout=300, prompt='$ ', sudo=False, sudo_password=None): """ The return value is ["standard output", "error output", return_value] """ sshLib = SSHLibrary() try: # print "[INFO] Begin to open the connection of", str(host) sshLib.open_connection(host) sshLib.login(username, pwd) # http://docs.paramiko.org/en/1.15/api/client.html#paramiko.client.SSHClient.connect except (SSHClientException, paramiko.SSHException, socket.error, socket.timeout) as se: errmsg = "[Error] Failed to connect to {host}".format(host=str(host)) print errmsg os.environ["OUTPUT"] = errmsg sshLib.close_connection() return ["", "", -1] ret = sshLib.execute_command(command, return_stdout=True, return_stderr=True, return_rc=True, sudo=sudo, sudo_password=sudo_password) sshLib.close_connection() if ret[2] == 0: os.environ["OUTPUT"] = ret[0] else: os.environ["OUTPUT"] = ret[1] return ret
class LogGrabber(object): """ Получение логов подсистем с удаленных серверов. \n Принцип работы: - перед началом теста происходит подсчет количества строк в логах - после окончания теста, если количество строк изменилось, то будут скачены только добавленные в лог строки. Для работы библиотеки необходимо cоздать переменную server_logs в python [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#creating-variables-directly|variable file] следующего вида: | server_logs = { | "tmpdir": "/tmp/", | "servers": [ | { | "hostname": "server.com", | "port": 22, | "username": "******", | "password": "******", | "subsystems": [ | { | "name": "Apache_server", | "logs": [ | { | "path_to_log": "/var/log", | "log_name": "access.log" | }, | { | "path_to_log": "/var/log", | "log_name": "error*.log" | } | ] | } | ] | } | ] | } Где: - tmpdir - каталог для временных файлов на удаленном сервере - hostname - имя хоста удаленного сервера - port - порт подключения по ssh - username\password - логин\пароль для подключения по ssh - name - имя подсистемы, для которой собираются логи, должно быть уникальным - path_to_log - путь к логам подсистемы - log_name - имя файла лога; могут использоваться wildcards аналогичные тем, что применяются в linux-команде find. === Ограничения === Логи подсистем должны находится на Linux сервере с возможностью подключения к нему по ssh. === Использование === 1. В качестве [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#using-listener-interface|listener]:\n ``pybot --listener LogGrabber /path/to/test_suite``\n При этом нет необходимости изменять тесты. После завершения теста со статусом FAILED будет скачена лишь та часть логов, которая была записана во время его проходжения. 2. В качестве библиотеки:\n В этом случае можно скачивать логи вне зависимости от статуса теста | *** Settings *** | Documentation Пример | Library LogGrabber | Variables variables.py | Suite Setup SuiteSetup | Suite Teardown SuiteTeardown | | *** Test Cases *** | Fail_test | FAIL fail message | | Passed_test | Pass Execution passed message | | | *** Keywords *** | SuiteSetup | LogGrabber.Set connections | LogGrabber.Prepare logs | | | SuiteTeardown | LogGrabber.Download logs | LogGrabber.Close connections === Зависимости === | robot framework | http://robotframework.org | | AdvancedLogging | http://git.billing.ru/cgit/PS_RF.git/tree/library/AdvancedLogging.py | | SSHLibrary | http://robotframework.org/SSHLibrary/latest/SSHLibrary.html | """ ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LISTENER_API_VERSION = 2 def __init__(self): # загрузка встроенных библиотек self.bi = BuiltIn() self.ssh = SSHLibrary() self.adv_log = AdvancedLogging() # словарь с подготовленными логами self.prepared_logs = dict() def start_suite(self, name, attrs): self.set_connections() def start_test(self, name, attrs): self.prepare_logs() def end_test(self, name, attrs): if attrs['status'] != 'PASS': self.download_logs() def end_suite(self, name, attrs): self.close_connections() def set_connections(self): """ SSH-соединение с удаленными серверами """ # Получаем информацию о логах подсистем self.logs = self.bi.get_variable_value('${server_logs}') # Системный разделитель для платформы запуска тестов self.sys_separator = self.bi.get_variable_value('${/}') # Разделитель в unix self.nix_separator = '/' # перебираем сервера из словаря настройки # для каждого сервера создаем одтельное ssh-соединение, # которое будет жить в течение всего suite # дописываем alias в словарь для каждого сервера for _, server in enumerate(self.logs["servers"]): hostname = server["hostname"] port = server["port"] username = server["username"] password = server["password"] ssh_alias = str(self.bi.get_time('epoch')) # создаем ssh-соединение - alias = epoch timestamp self.ssh.open_connection(hostname, ssh_alias, port) self.ssh.login(username, password) server["ssh_alias"] = ssh_alias def prepare_logs(self): """ Подготовка логов. В результате для каждого лога, удовлетворяющего настройке, записывается номер послнедней строки. """ # структура с описанием серверов, подсистем и логов self.prepared_logs["servers"] = [] # перебираем сервера из конфигурации for server in self.logs["servers"]: processed_server = dict() hostname = server["hostname"] port = server["port"] username = server["username"] password = server["password"] ssh_alias = server["ssh_alias"] # заполняем словарь, описывающий обработанный сервер processed_server["hostname"] = hostname processed_server["port"] = port processed_server["username"] = username processed_server["password"] = password processed_server["ssh_alias"] = ssh_alias processed_server["subsystems"] = [] # переключаемся на соединение с alias = ssh_alias из словаря настройки self.ssh.switch_connection(ssh_alias) # для каждого сервера обрабатываем набор подсистем for subsystem in server["subsystems"]: # словарь обработанных подсистем processed_subsys = dict() processed_subsys["name"] = subsystem["name"] # список обработанных логов processed_logs = [] # обрабатываем логи для текущей подсистемы for subsys_log in subsystem["logs"]: path_to_log = subsys_log["path_to_log"] log_name_regexp = subsys_log["log_name"] # получаем список лог-файлов по regexp log_name_list_text = self.ssh.execute_command( "find {}{}{} -printf '%f\n'".format( path_to_log, self.nix_separator, log_name_regexp), True, True, True) # если список не пуст и код возврата команды 0 (success) if ((len(log_name_list_text[0]) > 0) & (log_name_list_text[2] == 0)): # формируем массив имен лог-файлов log_name_array = string.split(log_name_list_text[0], '\n') # для каждого файла получаем номер последней строки for log_name in log_name_array: line_number = self.ssh.execute_command( "cat {}{}{} | wc -l".format( path_to_log, self.nix_separator, log_name)) processed_logs.append({ "path_to_log": path_to_log, "log_name": log_name, "line_number": line_number }) # проверка для исключения "мусора" processed_subsys if (len(processed_logs) > 0): processed_subsys["logs"] = processed_logs processed_server["subsystems"].append(processed_subsys) # проверка - есть ли для сервера обработанные подсистемы с логами if (len(processed_server["subsystems"]) > 0): self.prepared_logs["servers"].append(processed_server) def download_logs(self): """ Формирование и загрузка логов. В результате в директории теста, созданной AdvancedLogging, получаем архив с логами [TIMESTAMP]_logs.zip """ timestamp = self.bi.get_time('epoch') # базовая директория теста base_dir = self.adv_log.Create_Advanced_Logdir() # имя результирующего архива с логами res_arc_name = os.path.join(base_dir, "{}_logs".format(timestamp)) # результирующая директория для логов logs_dir = os.path.join(base_dir, "logs") # временная директория на целевом сервере temp_dir = self.logs['tmpdir'] # обрабатыаем сервера, с подготовленными логами for server in self.prepared_logs["servers"]: # параметры подключения к серверу ssh_alias = server["ssh_alias"] # переключаемся на соединение с alias = ssh_alias из словаря self.ssh.switch_connection(ssh_alias) # обрабатываем подсистемы с подготовленными логами for subsystem in server["subsystems"]: # структура в которую скачиваются логи [Advanced_Logdir]/logs/<подсистема>/ subsys_dir = os.path.join(logs_dir, subsystem["name"]) for log in subsystem["logs"]: abs_log_name = "{}{}{}".format(log["path_to_log"], self.nix_separator, log["log_name"]) # имя файла содержащего интересующую нас часть, лога cut_log_name = "{}_{}".format(timestamp, log["log_name"]) # абсолютный пусть с именем файла (cut_[имя_лога]) - для интересующего нас куска лога cut_abs_log_name = "{}{}{}".format(temp_dir, self.nix_separator, cut_log_name) # текущий номер строки в логе cur_line_number = self.ssh.execute_command( "cat {} | wc -l".format(abs_log_name)) # проверяем, появились ли строки в логе с момента подготовки логов if (cur_line_number > log["line_number"]): # вырезаем часть лога, начиная с сохраненного номера строки self.ssh.execute_command("tail -n +{} {} > {}".format( log["line_number"], abs_log_name, cut_abs_log_name)) # gzip self.ssh.execute_command("gzip {}.gz {}".format( cut_abs_log_name, cut_abs_log_name)) # скачиваем файл self.ssh.get_file( "{}.gz".format(cut_abs_log_name), "{}{}{}.gz".format(subsys_dir, self.sys_separator, cut_log_name)) # удаляем вырезанную чыасть лога и gz-архив этой части self.ssh.execute_command( "rm {}".format(cut_abs_log_name)) self.ssh.execute_command( "rm {}.gz".format(cut_abs_log_name)) # если есть результат - упаковываем в единый zip-архив и удаляем папку с логами if (os.path.exists(logs_dir)): self._zip(logs_dir, res_arc_name) shutil.rmtree(logs_dir) def close_connections(self): """ Закрытие ssh-соединений с удаленными серверами """ self.ssh.close_all_connections() def _zip(self, src, dst): """ Упаковка логов единый zip-архив *Args:*\n _src_ - директория для упаковки _dst_ - имя лога """ zf = zipfile.ZipFile("%s.zip" % (dst), "w", zipfile.ZIP_DEFLATED) abs_src = os.path.abspath(src) for root, _, files in os.walk(src): for filename in files: abs_name = os.path.abspath(os.path.join(root, filename)) arc_name = abs_name[len(abs_src) + 1:] zf.write(abs_name, arc_name) zf.close()
class PackageLibrary(object): """ @summary:安装包管理类,实现安装包的扫描、拷贝、卸载、安装等操作. """ def __init__(self, ci_addr="10.10.17.49", ci_user="******", ci_passwd="passw0rd"): self.ci_addr = ci_addr self.ci_user = ci_user self.ci_passwd = ci_passwd from SSHLibrary import SSHLibrary self.sshLib = SSHLibrary() @add_logs_for_functions def execute_command_and_verify_results(self, command): command_list = command.split(";") for item in command_list: print "execute command: {0}".format(item) out, error = self.sshLib.execute_command(command, True, True) if out: print "@@@@@@@@output start:@@@@@@@@@@ \n {0} \n@@@@@@@@@output ends@@@@@@".format(out) if error: print error # raise AssertionError("execute command: {0} failed".format(command)) else: return out def ssh_login(self, server_ip, username, passwd): print "***python*** start login server:{0}".format(server_ip) self.sshLib.open_connection(server_ip, server_ip, timeout='3 minute') content = self.sshLib.login(username, passwd, delay='3 seconds') print content if "Last login" not in content: if '#' not in content: raise AssertionError("Fail to login host {0}".format(server_ip)) def _write(self, command): print "***python*** writing command or text to the terminal: {0}".format(command) return self.sshLib.write(command) @add_logs_for_functions def write_cmd(self, command): print "***python*** writing command or text to the terminal: {0}".format(command) return self.sshLib.write(command) @add_logs_for_functions def check_packages_is_update(self, mount_path, log_path): """ @summary:检查安装包更新通用方法 :param mount_path:成果管理处自动编译结果挂载点 :param log_path:存放日志的路径 :return:有更新返回True,没有更新返回False """ history_number = [] with open(log_path, "r+") as fp: read_data = fp.readlines() for date in read_data: history_number = history_number + date.split() print "history version:", history_number history_number.sort() self.ssh_login(self.ci_addr, self.ci_user, self.ci_passwd) # while True: latest_number = [] output = self.execute_command_and_verify_results("ls %s" % mount_path) for item in output.split(): # print item temp_version = filter(str.isdigit, item.encode('utf8')) if temp_version is not None and temp_version != '': latest_number.append(int(temp_version)) print "latest version:", latest_number latest_number.sort() if latest_number: print "latest: {0}, compile history:{1}".format(latest_number[-1], history_number[-1]) if int(latest_number[-1]) > int(history_number[-1]): # with open(log_path, "r+") as fp: # print "open {0} and write value {1}".format(log_path, latest_number[-1]) # fp.seek(0, 2) # fp.write(" " + str(latest_number[-1])) return latest_number[-1] return False @add_logs_and_check_result def check_building_status(self, mount_path, log_path): """ 检查是否编译成功 :param mount_path: 成果管理处自动编译结果挂载点 :param log_path: 存放日志的路径 :return:True/False """ building_number = self.check_packages_is_update(mount_path, log_path) if not building_number: return False else: building_number = str(building_number) sub_dir = building_number[:8] + '-' + building_number[8:] build_scene_path = '{0}/{1}/build_scene.log'.format(mount_path, sub_dir) with open(log_path, "r+") as fp: fp.seek(0, 2) fp.write(" " + str(building_number)) with open(build_scene_path, "r") as rfp: rfp.seek(-50, 2) for lines in rfp.readlines(): print lines if 'successfully' in lines: cp_cmd = 'cp {0}/{1}/result.txt case.html'.format(mount_path, sub_dir) os.system(cp_cmd) # os.system("sed -i 's/代码路径/<p\/>代码路径/g' case.html") os.system("sed -i 's/tmp/128.255.125.71/g' case.html") return True return False @add_logs_for_functions def _check_packages_update_status_common(self, mount_path, ci_local_package_path, log_path): """ @summary:检查安装包更新通用方法 :param mount_path:成果管理处自动编译结果挂载点 :param ci_local_package_path:ci服务器上存放更新的包路径 :param log_path:存放日志的路径 :return:有更新返回True,没有更新返回False """ rtn = False latest_number = self.check_packages_is_update(mount_path, log_path) if latest_number: # sas相关操作 if 'sas' in ci_local_package_path: self.execute_command_and_verify_results("rm -rf {0}/*".format(ci_local_package_path)) cp_cmd = 'cp %s/SERVER_%s/*.sh %s;ls -l %s' % (mount_path, str(latest_number), ci_local_package_path, ci_local_package_path) info = self.execute_command_and_verify_results(cp_cmd) if 'sas' in info: with open(log_path, "r+") as fp: print "open {0} and write value {1}".format(log_path, latest_number) fp.seek(0, 2) fp.write(" " + str(latest_number)) rtn = True # v3r2c01操作 else: self.execute_command_and_verify_results("rm -rf {0}/*".format(ci_local_package_path)) # 需要将日期时间戳处理成为目录 number_str = str(latest_number) sub_dir = number_str[:8] + '-' + number_str[8:] cp_all_cmd = 'cp %s/%s/Maipu-AASV4-CMPPortal*.sh %s;ls %s' % (mount_path, sub_dir, ci_local_package_path, ci_local_package_path) cmp_info = self.execute_command_and_verify_results(cp_all_cmd) print cmp_info if 'Maipu' in cmp_info: with open(log_path, "r+") as fp: print "open {0} and write value {1}".format(log_path, latest_number) fp.seek(0, 2) fp.write(" " + str(latest_number)) rtn = True else: print "The latest build number{0} has recorded in installation log, " \ "enter the next loop scanning".format(str(latest_number)) self.sshLib.close_all_connections() return rtn @add_logs_for_functions def check_muti_packages_update(self, check_update_status_arg_list): """ :summary:同时检查多个包的更新状态 :param check_update_status_arg_list:更新包状态参数列表,将多个包检查参数组装成为一个list,每个元素 :为_check_packages_update_status_common方法中参数的一个字典,字典的key分别为mount_path,ci_local_package_path,log_path :如:[{"字典的key分别为mount_path":"xxx","ci_local_package_path":"xxx","log_path":"xxx"}] :return:无返回值,出错抛出异常 """ check_result = False for arg in check_update_status_arg_list: if self._check_packages_update_status_common(arg["mount_path"], arg["ci_local_package_path"], arg["log_path"]): check_result = True continue if not check_result: raise AssertionError(u"未检测到更新的软件包") @add_logs_for_functions def _copy_pacakges_to_target_srever(self, ci_local_package_path, target_server, target_username, target_passwd, target_path): """ :summary: 拷贝软件包到指定路径 :param ci_local_package_path: ci服务器本地存放包的路径 :param target_server: 目标服务器的地址 :param target_username: 目标服务器的用户名 :param target_passwd: 目标服务器的渺茫 :param target_path: 目标服务器的存放包的路径 :return:返回None """ self.ssh_login(target_server, target_username, target_passwd) self.sshLib.set_client_configuration(prompt="#", timeout="10 minute") self.execute_command_and_verify_results("rm -rf {0}/*".format(target_path)) # empty the old package # self.execute_command_and_verify_results("mkdir {0}".format(om_server_package_path)) # empty the old package self._write( "scp -r {0}@{1}:{2}/* {3}".format(self.ci_user, self.ci_addr, ci_local_package_path, target_path)) output = self.sshLib.read(delay="10s") print output if "yes" in output: print "yes/no" self.sshLib.write('yes') print '-------===================----------' time.sleep(2) self.sshLib.write(self.ci_passwd) print self.sshLib.read_until_prompt("DEBUG") elif "assword" in output: print "enter your password" self.sshLib.write(self.ci_passwd) print '------------------------------------' print self.sshLib.read_until_prompt("DEBUG") elif '#' in output[-10:]: print '====================================' else: print '++++++++++++++++++++++++++++++' print self.sshLib.read_until_prompt("DEBUG") print '++++++++++++++++++++++++++++++' self.sshLib.close_all_connections() @add_logs_for_functions def copy_to_muti_target_server(self, copy_package_arg_list): """ :拷贝软件包到多个服务器的指定目录 :param copy_package_arg_list:_copy_pacakges_to_target_srever方法的参数的为元素组成的一个list :return: """ for arg in copy_package_arg_list: if len(arg.keys()) == 5: for key in arg.keys(): if key not in ["ci_local_package_path", "target_server", "target_username", "target_passwd", "target_path"]: raise AssertionError(u"copy_package_arg_list中的参数的key %s 不正确" % str(key)) else: raise AssertionError(u"copy_package_arg_list中的参数的键值对个数不正确") for arg in copy_package_arg_list: self._copy_pacakges_to_target_srever(arg["ci_local_package_path"], arg["target_server"], arg["target_username"], arg["target_passwd"], arg["target_path"]) @add_logs_for_functions def _common_install_param_input(self, install_param): """ :summary:通用安装参数匹配输入方法 :param install_param: 安装参数,为空表示不需要任何手工输入的参数,否则以[("regexp1","input1"),("regexp2","input2")] 这种形式传入参数 :return:无返回,执行出错抛出异常 """ if install_param: for regexp, input_ in install_param: print regexp, input_ print self.sshLib.read_until(regexp) print self.sshLib.write(input_) print self.sshLib.read_until_regexp(".*#$", "DEBUG") else: print self.sshLib.read_until_regexp(".*#$", "DEBUG") @add_logs_for_functions def send_cmd_by_expect(self, cmd_param): """ :summary:根据顺序匹配输入命令 :param cmd_param: 安装参数,为空表示不需要任何手工输入的参数,否则以[("regexp1","input1"),("regexp2","input2")]这种形式传入参数 :return:无返回,执行出错抛出异常 """ if cmd_param: for regexp, input_ in cmd_param: print regexp, input_ print self.sshLib.read_until(regexp) print self.sshLib.write(input_) print self.sshLib.read_until_regexp(".*#$", "DEBUG") else: print self.sshLib.read_until_regexp(".*#$", "DEBUG") @add_logs_for_functions def _install_pkg_common(self, target_server, target_username, target_passwd, pkg_path, app_name, shell_args): """ :summary: 安装包通用方法 :param target_server: 安装包的目标服务器地址 :param target_username: 安装包的目标服务器用户名 :param target_passwd: 安装包的目标服务器的密码 :param pkg_path: 安装包在目标服务器的路径 :param app_name: 组件名称 :return:成功返回True,否则抛出异常 """ self.ssh_login(target_server, target_username, target_passwd) self.sshLib.set_client_configuration(prompt="#", timeout="15 minute") # self.check_command_results("service srvmgt stop") get_package_cmd = "cd {0};chmod 444 *;pwd;ll |grep {1}".format(pkg_path, app_name) self._write(get_package_cmd) output = self.sshLib.read_until_prompt("DEBUG") print output pattern = "(\S+.sh)" m = re.search(pattern, output) if m: pkg_name = m.group(1) install_command = "cd %s;pwd;ls;sh %s %s" % (pkg_path, pkg_name, shell_args) else: raise AssertionError(u"未匹配到正确的安装包") print self.sshLib.write(install_command) info = '' last_info = '' repeat_time = 1 while True: temp = self.sshLib.read(delay='10s') print temp info += temp if 'Confirm install [y/n]?' in info: print self.sshLib.write('y') info = '' continue if 'Confirm install [Y/n]?' in info: print self.sshLib.write('y') info = '' continue if 'Confirm install? [Y/n]' in info: print self.sshLib.write('y') info = '' continue if 'Confirm uninstall old ver [y/n]?' in info: print self.sshLib.write('y') info = '' continue if 'Input setup dest path: [/opt/mpup]' in info: print self.sshLib.write('') info = '' continue if 'Input setup dest path: [/home/mpup/mpup]' in info: print self.sshLib.write('') info = '' continue if 'please confirm enable Maipu Security (y/n)?' in info: print self.sshLib.write('n') info = '' continue if 'Confirm continue [y/n]?' in info: print self.sshLib.write('y') info = '' continue if re.search(':~/\w+ #', info): return True if repeat_time < 150: if last_info == temp: repeat_time += 1 else: repeat_time = 1 last_info = temp else: print info raise AssertionError(u'已经连续150次,1500s相同输出了,please check!') @add_logs_for_functions def install_muti_package(self, install_package_args): """ :批量安装多个安装包 :param install_package_args:安装多个APP的参数列表:列表中每个元素为字典,字典的键为_install_pkg_common方法 :的参数名,如:[{"target_server":"xx","target_user_name":"xx","target_passwd":"x","pkg_path","xx","app":"xx" :"install_param":[install_param]}] :return:无返回值,出错抛出异常 """ for arg in install_package_args: self._install_pkg_common(arg["target_server"], arg["target_username"], arg["target_passwd"], arg["pkg_path"], arg["app_name"], arg["shell_args"]) @add_logs_for_functions def _uninstall_pacakge_common(self, target_server, target_username, target_passwd): """ :summary:卸载安装包通用方法 :param target_server:卸载包的目标服务器地址 :param target_username:卸载包的目标服务器用户名 :param target_passwd:卸载包的目标服务器密码 :return: """ self.ssh_login(target_server, target_username, target_passwd) self.sshLib.set_client_configuration(prompt="#", timeout="20 minute") self._write('/opt/mpup/bin/mpsetup show') output = self.sshLib.read_until_prompt("DEBUG") service_list = output.split('\n') for service in service_list[:-1]: # 会将最后的匹配算进去,这个去除掉 print 'current info is %s' % service m = re.search('\s+(\w+.*?)\s', service) if not m: print "dot not match any sercice" return True print 'service is %s' % m.group(1) if 'MPUP' in m.group(1): continue self._write('/opt/mpup/bin/mpsetup uninstall ' + m.group(1)) # self._write('mpsetup uninstall ' + m.group(1)) output = self.sshLib.read_until_prompt("DEBUG") print output # uninstall mpup mpupcore self._write('/opt/mpup/bin/mpsetup uninstall MPUPCore') # self._write('mpsetup uninstall MPUPCore') output = self.sshLib.read_until_prompt("DEBUG") print output self._write('/opt/mpup/bin/mpsetup uninstall MPUP') # self._write('mpsetup uninstall MPUP') output = self.sshLib.read_until_prompt("DEBUG") print output self.sshLib.close_all_connections() return True @add_logs_for_functions def uninstall_muti_packages(self, pkg_uninstall_arg): """ :批量卸载多个app的通用方法 :param pkg_uninstall_arg:卸载多个APP的卸载参数列表:列表中每个元素为字典,字典的键为_uninstall_pacakge_common方法 :的参数名,如:[{"target_server":"xx","target_user_name":"xx","target_passwd":"x","pkg_path","xx","app":"xx"}] :return:无返回值,出错抛出异常 """ for arg in pkg_uninstall_arg: if len(arg.keys()) == 3: for key in arg.keys(): if key not in ["target_server", "target_username", "target_passwd"]: raise AssertionError(u"pkg_uninstall_arg中的参数的key %s 不正确" % str(key)) else: raise AssertionError(u"pkg_uninstall_arg中的参数的键值对个数不正确") for arg in pkg_uninstall_arg: self._uninstall_pacakge_common(arg["target_server"], arg["target_username"], arg["target_passwd"]) @add_logs_for_functions def use_install_package_uninstall(self, pkg_uninstall_arg): """ 用安装包卸载程序 :param pkg_uninstall_arg: :return: """ for arg in pkg_uninstall_arg: self.ssh_login(arg["target_server"], arg["target_username"], arg["target_passwd"]) self.sshLib.set_client_configuration(prompt="#", timeout="15 minute") self.sshLib.execute_command('service srvmgt stop') get_package_cmd = "cd {0};chmod 444 *;pwd;ll |grep {1}".format(arg["pkg_path"], arg["app_name"]) self._write(get_package_cmd) output = self.sshLib.read_until_prompt("DEBUG") print output pattern = "(\S+.sh)" m = re.search(pattern, output) if m: pkg_name = m.group(1) uninstall_command = "cd %s;pwd;ls;sh %s uninstall" % (arg["pkg_path"], pkg_name) else: raise AssertionError(u"未匹配到正确的安装包") print self.sshLib.write(uninstall_command) info = '' last_info = '' repeat_time = 1 while True: temp = self.sshLib.read(delay='10s') print temp info += temp if 'Confirm uninstall all installed files and datas [y/n]?' in info: print self.sshLib.write('y') info = '' continue # 匹配结束 if re.search(':~/\w+ #', info): break # 匹配长期暂停 if repeat_time < 100: if last_info == temp: repeat_time += 1 else: repeat_time = 1 last_info = temp else: print info raise AssertionError(u'已经连续100次,1000s相同输出了,please check!') @add_logs_and_check_result def init_system(self, target_server, target_username, target_passwd, init_command_dict_list): """ 初始化系统 :param target_server:服务器地址 :param target_username:用户 :param target_passwd:密码 :param init_command_dict_list:初始化参数字典列表 :return:True/False """ self.ssh_login(target_server, target_username, target_passwd) self.sshLib.set_client_configuration(prompt="#", timeout="10 minute") self._write('service srvmgt init') info = '' last_info = '' repeat_time = 1 while True: temp = self.sshLib.read(delay='10s') print temp info += temp for command_dict in init_command_dict_list: if command_dict[0] in info: print self.sshLib.write(command_dict[1]) init_command_dict_list.remove(command_dict) info = '' break else: # 这里是运行结束 if re.search(':~.*?#', info): return True # 这里判断重复次数 if repeat_time < 40: if last_info == temp: repeat_time += 1 else: repeat_time = 1 last_info = temp else: print info print u'已经连续40次,400s相同输出了,please check!' return False @add_logs_and_check_result def check_file_is_change(self, file_path, limit_time): """ 判断目标文件是否在限制时间内有更改 :param file_path:目标文件 :param limit_time:限制时间,秒为单位,一个小时为3600秒 :return: """ self.ssh_login(self.ci_addr, self.ci_user, self.ci_passwd) output = self.execute_command_and_verify_results('stat -c %Y ' + file_path + """ |awk '{printf $0" "; system("date +%s")}'|awk '{print $2-$1}'""") if int(output) > int(limit_time): return False return True
class LogGrabber(object): """ Получение логов подсистем с удаленных серверов. \n Принцип работы: - перед началом теста происходит подсчет количества строк в логах - после окончания теста, если количество строк изменилось, то будут скачены только добавленные в лог строки. Для работы библиотеки необходимо cоздать переменную server_logs в python [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#creating-variables-directly|variable file] следующего вида: | server_logs = { | "tmpdir": "/tmp/", | "servers": [ | { | "hostname": "server.com", | "port": 22, | "username": "******", | "password": "******", | "subsystems": [ | { | "name": "Apache_server", | "logs": [ | { | "path_to_log": "/var/log", | "log_name": "access.log" | }, | { | "path_to_log": "/var/log", | "log_name": "error*.log" | } | ] | } | ] | } | ] | } Где: - tmpdir - каталог для временных файлов на удаленном сервере - hostname - имя хоста удаленного сервера - port - порт подключения по ssh - username\password - логин\пароль для подключения по ssh - name - имя подсистемы, для которой собираются логи, должно быть уникальным - path_to_log - путь к логам подсистемы - log_name - имя файла лога; могут использоваться wildcards аналогичные тем, что применяются в linux-команде find. === Ограничения === Логи подсистем должны находится на Linux сервере с возможностью подключения к нему по ssh. === Использование === 1. В качестве [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#using-listener-interface|listener]:\n ``pybot --listener LogGrabber /path/to/test_suite``\n При этом нет необходимости изменять тесты. После завершения теста со статусом FAILED будет скачена лишь та часть логов, которая была записана во время его проходжения. 2. В качестве библиотеки:\n В этом случае можно скачивать логи вне зависимости от статуса теста | *** Settings *** | Documentation Пример | Library LogGrabber | Variables variables.py | Suite Setup SuiteSetup | Suite Teardown SuiteTeardown | | *** Test Cases *** | Fail_test | FAIL fail message | | Passed_test | Pass Execution passed message | | | *** Keywords *** | SuiteSetup | LogGrabber.Set connections | LogGrabber.Prepare logs | | | SuiteTeardown | LogGrabber.Download logs | LogGrabber.Close connections === Зависимости === | robot framework | http://robotframework.org | | AdvancedLogging | http://git.billing.ru/cgit/PS_RF.git/tree/library/AdvancedLogging.py | | SSHLibrary | http://robotframework.org/SSHLibrary/latest/SSHLibrary.html | """ ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LISTENER_API_VERSION = 2 def __init__(self): # загрузка встроенных библиотек self.bi=BuiltIn() self.ssh=SSHLibrary() self.adv_log=AdvancedLogging() # словарь с подготовленными логами self.prepared_logs = dict() def start_suite(self, name, attrs): self.set_connections() def start_test(self, name, attrs): self.prepare_logs() def end_test(self, name, attrs): if attrs['status'] != 'PASS': self.download_logs() def end_suite(self, name, attrs): self.close_connections() def set_connections(self): """ SSH-соединение с удаленными серверами """ # Получаем информацию о логах подсистем self.logs=self.bi.get_variable_value('${server_logs}') # Системный разделитель для платформы запуска тестов self.sys_separator=self.bi.get_variable_value('${/}') # Разделитель в unix self.nix_separator = '/' # перебираем сервера из словаря настройки # для каждого сервера создаем одтельное ssh-соединение, # которое будет жить в течение всего suite # дописываем alias в словарь для каждого сервера for _, server in enumerate(self.logs["servers"]): hostname = server["hostname"] port = server["port"] username = server["username"] password = server["password"] ssh_alias = str(self.bi.get_time('epoch')) # создаем ssh-соединение - alias = epoch timestamp self.ssh.open_connection(hostname, ssh_alias, port) self.ssh.login(username, password) server["ssh_alias"] = ssh_alias def prepare_logs(self): """ Подготовка логов. В результате для каждого лога, удовлетворяющего настройке, записывается номер послнедней строки. """ # структура с описанием серверов, подсистем и логов self.prepared_logs["servers"] = [] # перебираем сервера из конфигурации for server in self.logs["servers"]: processed_server = dict() hostname = server["hostname"] port = server["port"] username = server["username"] password = server["password"] ssh_alias = server["ssh_alias"] # заполняем словарь, описывающий обработанный сервер processed_server["hostname"] = hostname processed_server["port"] = port processed_server["username"] = username processed_server["password"] = password processed_server["ssh_alias"] = ssh_alias processed_server["subsystems"] = [] # переключаемся на соединение с alias = ssh_alias из словаря настройки self.ssh.switch_connection(ssh_alias) # для каждого сервера обрабатываем набор подсистем for subsystem in server["subsystems"]: # словарь обработанных подсистем processed_subsys = dict() processed_subsys["name"] = subsystem["name"] # список обработанных логов processed_logs = [] # обрабатываем логи для текущей подсистемы for subsys_log in subsystem["logs"]: path_to_log = subsys_log["path_to_log"] log_name_regexp = subsys_log["log_name"] # получаем список лог-файлов по regexp log_name_list_text = self.ssh.execute_command("find {}{}{} -printf '%f\n'".format(path_to_log, self.nix_separator, log_name_regexp), True, True, True) # если список не пуст и код возврата команды 0 (success) if ((len(log_name_list_text[0]) > 0) & (log_name_list_text[2] == 0) ): # формируем массив имен лог-файлов log_name_array = string.split(log_name_list_text[0], '\n') # для каждого файла получаем номер последней строки for log_name in log_name_array: line_number = self.ssh.execute_command("cat {}{}{} | wc -l".format(path_to_log, self.nix_separator, log_name)) processed_logs.append({"path_to_log": path_to_log, "log_name": log_name, "line_number": line_number}) # проверка для исключения "мусора" processed_subsys if (len(processed_logs)>0): processed_subsys["logs"] = processed_logs processed_server["subsystems"].append(processed_subsys) # проверка - есть ли для сервера обработанные подсистемы с логами if (len(processed_server["subsystems"])>0): self.prepared_logs["servers"].append(processed_server) def download_logs(self): """ Формирование и загрузка логов. В результате в директории теста, созданной AdvancedLogging, получаем архив с логами [TIMESTAMP]_logs.zip """ timestamp = self.bi.get_time('epoch') # базовая директория теста base_dir = self.adv_log.Create_Advanced_Logdir() # имя результирующего архива с логами res_arc_name = os.path.join(base_dir, "{}_logs".format(timestamp)) # результирующая директория для логов logs_dir = os.path.join(base_dir, "logs") # временная директория на целевом сервере temp_dir = self.logs['tmpdir'] # обрабатыаем сервера, с подготовленными логами for server in self.prepared_logs["servers"]: # параметры подключения к серверу ssh_alias = server["ssh_alias"] # переключаемся на соединение с alias = ssh_alias из словаря self.ssh.switch_connection(ssh_alias) # обрабатываем подсистемы с подготовленными логами for subsystem in server["subsystems"]: # структура в которую скачиваются логи [Advanced_Logdir]/logs/<подсистема>/ subsys_dir = os.path.join(logs_dir, subsystem["name"]) for log in subsystem["logs"]: abs_log_name = "{}{}{}".format(log["path_to_log"], self.nix_separator, log["log_name"]) # имя файла содержащего интересующую нас часть, лога cut_log_name = "{}_{}".format(timestamp, log["log_name"]) # абсолютный пусть с именем файла (cut_[имя_лога]) - для интересующего нас куска лога cut_abs_log_name = "{}{}{}".format(temp_dir, self.nix_separator, cut_log_name) # текущий номер строки в логе cur_line_number = self.ssh.execute_command("cat {} | wc -l".format(abs_log_name)) # проверяем, появились ли строки в логе с момента подготовки логов if (cur_line_number > log["line_number"]): # вырезаем часть лога, начиная с сохраненного номера строки self.ssh.execute_command("tail -n +{} {} > {}".format(log["line_number"], abs_log_name, cut_abs_log_name)) # gzip self.ssh.execute_command("gzip {}.gz {}".format(cut_abs_log_name, cut_abs_log_name)) # скачиваем файл self.ssh.get_file("{}.gz".format(cut_abs_log_name), "{}{}{}.gz".format(subsys_dir, self.sys_separator, cut_log_name)) # удаляем вырезанную чыасть лога и gz-архив этой части self.ssh.execute_command("rm {}".format(cut_abs_log_name)) self.ssh.execute_command("rm {}.gz".format(cut_abs_log_name )) # если есть результат - упаковываем в единый zip-архив и удаляем папку с логами if (os.path.exists(logs_dir)): self._zip(logs_dir, res_arc_name) shutil.rmtree(logs_dir) def close_connections(self): """ Закрытие ssh-соединений с удаленными серверами """ self.ssh.close_all_connections() def _zip(self, src, dst): """ Упаковка логов единый zip-архив *Args:*\n _src_ - директория для упаковки _dst_ - имя лога """ zf = zipfile.ZipFile("%s.zip" % (dst), "w", zipfile.ZIP_DEFLATED) abs_src = os.path.abspath(src) for root, _, files in os.walk(src): for filename in files: abs_name = os.path.abspath(os.path.join(root, filename)) arc_name = abs_name[len(abs_src) + 1:] zf.write(abs_name, arc_name) zf.close()
password = input("Password: "******"\033c") ssh.login(username, password) def restartNetAdapter(): p = subprocess.Popen( 'powershell.exe Invoke-Command -Computername JJ-PSRV1 -ScriptBlock {Restart-NetAdapter -Name "Ethernet0"}' ) time.sleep(7) p.terminate() #The loop while True: PSRV1 = ssh.execute_command("vim-cmd vmsvc/power.getstate 20", return_stdout=True) PSRV2 = ssh.execute_command("vim-cmd vmsvc/power.getstate 24", return_stdout=True) time.sleep(2) print("\033c") if "Powered on" in PSRV1: print("Virtual Machine: PSRV1 is Online") else: print("Virtual Machine: PSRV1 is Offline") if "Powered on" in PSRV2: print("Virtual Machine: PSRV2 is Online") else: print("Virtual Machine: PSRV2 is Offline") print(" ") # If PSRV1 is ON: if "Powered on" in PSRV1: