#encoding=UTF-8 # # 通过expect执行远程命令或传输文件 # #python zo.py action=local cmds=? #python zo.py action=remote host=? username=? password=? cmds=? #python zo.py action=fget host=? username=? password=? local=? remote=? #python zo.py action=fput host=? username=? password=? local=? remote=? # import os import sys import re import csv import subprocess import time import datetime import threading #this file zo_py=os.path.realpath(__file__) #获取一个新的ID HOSTIP = "127.0.0.1" if not "SSH_CONNECTION" in os.environ else os.environ["SSH_CONNECTION"].split(" ")[2] #zo_dir="/tmp/zo%s"%("%s%03d"%(time.strftime("%Y%m%d%H%M%S", time.localtime(time.time())), datetime.datetime.now().microsecond/1000)) zo_dir="/tmp/zo" os.system('''if [[ -e "'''+zo_dir+'''" ]]; then ret=0; else mkdir -p '''+zo_dir+'''; chmod a+w '''+zo_dir+'''; ret=$?; fi; exit $ret''') __tid = 0 def ntid(): global __tid __tid += 1 nid = "%d%d"%(os.getpid(), 100000000 + __tid) return nid #输出调试信息 tl_none='' #level 0 不输出任何信息 tl_fatal='F' #level 1 输出严重错误信息 tl_error='E' #level 2 输出一般错误信息 tl_warn='W' #level 3 输出警告信息 tl_status='S' #level 4 状态信息 tl_info='I' #level 5 输出提示信息 tl_proc='P' #level 6 输出处理流程信息 tl_response='R' #level 7 输出处理过程反馈信息 tl_detail='D' #level 8 输出数据处理信息 tl_debug='X' #level 9 输出详细调试信息,仅用于开发期间 tl_any='A' #level a 输出任何信息,仅用于开发期间 TRACE="FEWSI" #默认调试信息跟踪级别,输出到console #调试信息输出格式 pt_trace="^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3} \[.+\] \[(.*)\] (.*)" pt_status_msg="(.*)\[([0-9]+)\]" #返回YYYY-mm-dd HH:MM:SS.sss格式的当前时间 def tracetime(): return "%s.%03d"%(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())), datetime.datetime.now().microsecond/1000) class setting_item: def __init__(self, filename, lineno, sid, skey, sfcmd, svalue, sdesc): idkey = re.sub("\W", "_", sid) if idkey != sid: raise Exception("键名称不合法(invalid key name) %s"%(sid)) self.fn = filename self.ln = lineno self.id = sid self.key = skey self.fcmd = sfcmd self.orig_value = svalue self.script = self.value_format(svalue) self.value = self.value_format(svalue) self.desc = sdesc #生成环境变量导出脚本 def str_export(self): script = "" script += "export {0}_key=\"{1}\"\n".format(self.id, self.key) script += 'export {0}_name=`if [[ "${0}_name" == "" ]]; then echo \'{1}\'; else echo "${0}_name"; fi`\n'.format(self.id, self.key if self.desc == "" else self.desc.replace("'", "'\"'\"'")) script += "export {0}_filename=\"{1}\"\n".format(self.id, self.fn if self.fn is not None else "") script += "export {0}_lineno=\"{1}\"\n".format(self.id, self.ln) script += "export {0}_fcmd=\"{1}\"\n".format(self.id, self.fcmd) script += "export {0}_script='{1}'\n".format(self.id, self.value.replace("'", "'\"'\"'")) if self.key == "": #非格式化内容,直接执行或显示 if self.value.strip() != "": if self.fcmd == "~": script += "eval \"${{{0}_script}}\"\n".format(self.id) else: script += "echo \"${{{0}_script}}\"\n".format(self.id) else: if self.fcmd == "~": script += "eval \"${{{0}_script}}\"\n".format(self.id) elif self.fcmd == "@": script += "{0}() {{\n eval \"${{{1}_script}}\" \n}}\n".format(self.key, self.id) script += "export {0}=`{1}`\n".format(self.id, self.key) else: script += "export {0}=`echo \"${{{0}_script}}\"`\n".format(self.id) return script #引号处理 def value_format(self, value): value = value.strip() if self.key == "": return value if len(value)>1 and value[0] == '"' and value[-1] == '"': value = value[1:-1] return value class settings: base_script = "#zo script built on " + tracetime() + "\n%s"%( ''' export EVAL='`' export OSNAME=`uname` export HOSTNAME=`hostname` export USERNAME=`whoami` if [[ ${OSNAME} == 'AIX' ]]; then export LANG=ZH_CN.UTF-8; else export LANG=zh_CN.utf8; fi if [[ ${OSNAME} == 'AIX' ]]; then export LC_ALL=ZH_CN.UTF-8; else export LC_ALL=zh_CN.utf8; fi export ZO_DIR='''+zo_dir+''' echo "#通用设置完成" #系统差别设置 if [[ ${OSNAME} == "AIX" || ${OSNAME} == "Darwin" ]]; then usleep() { python -c "import time; time.sleep($1.0/1000000)" } fi #改变工作目录到当前脚本所在路径 CUR_DIR=`pwd` echo "${CUR_DIR}" #仅支持通过SSH连接 export HOSTIP=`echo ${SSH_CONNECTION} | awk -F ' ' {'print $3'}` if [[ "${HOSTIP}" == "" ]]; then HOSTIP="127.0.0.1" fi #输出信息时间,aix系统没有微秒时间,所以用python输出微秒级时间 #`date "+%Y-%m-%d %H:%M:%S.%N"` p_logtime() { python -c 'import time,datetime; print("%s.%03d"%(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())), datetime.datetime.now().microsecond/1000))' } replace() { local regx="$1" local rstr="$2" local gflg="$3" } #通过meval控制执行的脚本,前面发生错误,禁止后续带错运行 export last_meval_script_return=0 #全局错误处理 export ZO_COMMAND_RUNNING=0 export errline=0 export errcode=0 errproc() { export errline=$1 export errcode=$2 #只处理不在meval控制范围的脚本错误,即当前zo script自身的错误 if [[ ${last_meval_script_return} == 0 ]]; then cmdscript="`echo "${ZO_SCRIPT}" | cat -n 2>&1 | head -n $errline | tail -n 1`" echo "`p_logtime` [$HOSTIP.$$] [F] error occur when eval script [$errline]: ${cmdscript} --> return $errcode" fi } trap 'errproc "$LINENO" "$?"' ERR #禁止中断 trap '' INT decode_string() { python -c " import base64 import sys s = sys.stdin.readline().strip() try: if s[0] == '=': print(s[1:]) else: if sys.version_info.major == 3: s = bytes(s, encoding='utf8') s = base64.b64decode(s) if sys.version_info.major == 3: s = str(s, encoding='utf8') print(s) except TypeError as e: #print(e) print(s) " } encode_string() { python -c " import base64; import sys; s = sys.stdin.readline().strip() try: if s[0] == '=': print(s[1:]) else: if sys.version_info.major == 3: s = bytes(s, encoding='utf8') s = base64.b64encode(s) if sys.version_info.major == 3: s = str(s, encoding='utf8') print(s) except TypeError as e: #print(e) print(s) " } decode_password() { export EXP_PASSWORD="`printf "${EXP_PASSWORD}" | decode_string`" trace X "EXP_PASSWORD=${EXP_PASSWORD}" } #特殊字符常量 export newline=`printf "\\n"` export tab=" " export space=" " export quote='"' export single_quote="'" export point_quote='`' export slash='\/' export backslash='\\' export symbol_at='@' export symbol_star='*' export symbol_question='?' export semicolon=';' export dollar='$' export brace='{' export dash='-' export return_char=`printf "\\r"` #输出信息分类定义 tl_fatal='F' #level 1 输出严重错误信息 tl_error='E' #level 2 输出一般错误信息 tl_warn='W' #level 3 输出警告信息 tl_status='S' #level 4 状态信息 tl_info='I' #level 5 输出提示信息 tl_proc='P' #level 6 输出处理流程信息 tl_response='R' #level 7 输出处理过程反馈信息 tl_detail='D' #level 8 输出数据处理信息 tl_debug='X' #level 9 输出详细调试信息,仅用于开发期间 tl_any='A' #level a 输出任何信息,仅用于开发期间 pt_trace="^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3} \[.+\] \[.*\] .*" pt_status_msg=".*\[[0-9]+\]$" pt_number="^[0-9]+$" is_trace_msg() { echo "$1" | grep -E "${pt_trace}" } is_number() { echo "$1" | grep -E "${pt_number}" } is_status_msg() { echo "$1" | grep -E "${pt_status_msg}" } #状态码 export status_code=0 #输出信息 trace() { local level="$1" local msg="$2" local flag="$3" if [[ "${level}" == "S" ]]; then #验证&修正状态信息格式 if [[ "`is_status_msg "${msg}"`" == "" ]]; then if [[ "`is_number "${status_code}"`" == "" ]]; then status_code=0 fi msg="${msg}[${status_code}]" fi fi if [[ "`is_trace_msg "${msg}"`" == "" ]]; then dtms=`p_logtime` #逐行格式化 additioninfo="" if [[ "${flag}" != "" ]]; then additioninfo="${flag} " fi msg="`echo "${msg}" | sed -e "s/^/${dtms} [${HOSTIP}.$$] [${level}] ${additioninfo}/"`" else #截取已格式化的日志level level=`echo "${msg}" | sed -e "s/.*\[.*\] \[//" -e "s/\].*$//"` fi if [[ "${level}" == "S" ]]; then #截取状态码 export status_code=`echo "${msg}" | sed -e "s/.*\[//" -e "s/\].*$//"` fi #输出所有级别信息,由python程序决定是否显示输出内容 echo "${msg}" } #执行脚本,确保脚本中的语句错误不影响整体的运行 evalscriptfunction() { export BASELINENO=$LINENO eval "`echo "$1"`" local ret=$? return $ret } #执行脚本,截取脚本错误信息写入指定文件 evalscript() { local zo_seq_id="$1" local scmdscript="$2" local tmperrfile="$3" trap '' INT exit_in_evalscript=0 #if [[ "$BASH" != "" ]]; then # #只有bash能重定义exit命令 # exit() { # trace E "部署过程脚本中不能使用exit,请用return代替" # exit_in_evalscript=$1 # return $1 # } #fi evalscriptfunction "${scmdscript}" local ret=$? if [[ $ret == 0 && `is_number "${exit_in_evalscript}"` != "" ]]; then ret=${exit_in_evalscript} fi return $ret } #检查脚本执行中是否发生错误 check_evalscript_error() { local zo_seq_id="$1" local scmdscript="$2" local tmperrfile="$3" if [[ -e "${tmperrfile}" ]]; then local errorinfo=(`cat "${tmperrfile}"`) if [[ "${errorinfo}" != "" && "${errorinfo[3]}" == "${zo_seq_id}" ]]; then trace X "errorinfo=${errorinfo[*]}" local errorline=`expr ${errorinfo[1]} - ${errorinfo[0]} - 1` local errorno="${errorinfo[2]}" if [[ "`is_number "${errorno}"`" != "" ]]; then linecode="`echo "${scmdscript}" | cat -n 2>&1 | head -n ${errorline} | tail -n 1`" trace W "命令执行中返回码不为0:[line ${errorline}: return ${errorno}] (${linecode})" local errorlinepl=`expr ${errorline} + 2` trace D "`echo "${scmdscript}" | cat -n 2>&1 | head -n ${errorlinepl} | tail -n 5 | sed -e "s/^/~ /"`" return ${errorno} else trace W "命令执行中返回错误信息:${errorinfo[*]}" trace E "返回码不是数" return 128 fi fi fi return 0 } #执行脚本,如果脚本执行中发生错误,后续将无法再次调用meval,除非通过ignore_previous_error忽略错误对后续脚本的影响 export ZO_SEQ_ID=10000 export on_error="" meval() { if [[ ${last_meval_script_return} != 0 ]]; then trace X "meval last_meval_script_return=${last_meval_script_return}" return ${last_meval_script_return} fi local scmdscript="$*" trace X "---- meval script ----" trace X "`echo "${scmdscript}" | cat -n 2>&1 | sed -e "s/^/~ /"`" trace X "---- meval run ----" export ZO_SEQ_ID=`expr ${ZO_SEQ_ID} + 1` local zo_seq_id=${ZO_SEQ_ID} local tmperrfile="'''+zo_dir+'''/zerone.errcat.$$.${zo_seq_id}.txt" rm -f "${tmperrfile}" #忽略脚本中可能发生的错误 on() { if [[ "$1" == "error" ]]; then shift if [[ "$1" == "continue" || "$1" == "break" ]]; then on_error="$1" elif [[ "$1" == "" || "$1" == "report" ]]; then on_error="" elif [[ "$1" == "occur" ]]; then shift if [[ "${on_error}" == "" ]]; then echo "$*" fi else trace W "unkown command 'on error $1'" fi else trace W "unkown command 'on $*'" fi } ignore_previous_error() { export last_meval_script_return=0 echo > "${tmperrfile}" } split() { awk -F "$1" "{print $"$2"}" } margs() { script=`echo "$*" | sed -e "s/$1 //"` while read LINE do s=`echo "${script}" | sed -e "s/$1/${LINE}/g"` eval "${s}" done } #保存递归运行计数 local keep_running_count=${ZO_COMMAND_RUNNING} local running_count=`expr ${ZO_COMMAND_RUNNING} + 1` export ZO_COMMAND_RUNNING=${running_count} #执行脚本 local on_error_pack="${on_error}" on_error="" xscmdscript="trap 'on error occur $""BASELINENO $""LINENO $""? "${zo_seq_id}" >>"${tmperrfile}"' ERR\n${scmdscript}" evalscript "${zo_seq_id}" "${xscmdscript}" "${tmperrfile}" local evalscript_return=$? on_error="${on_error_pack}" #处理错误记录 #错误记录只处理一次 if [[ ${last_meval_script_return} == 0 ]]; then check_evalscript_error "${zo_seq_id}" "${scmdscript}" "${tmperrfile}" local evalscript_error=$? if [[ ${evalscript_return} == 0 ]]; then evalscript_return=${evalscript_error} fi export last_meval_script_return=${evalscript_return} fi #恢复递归运行计数 if [[ ${last_meval_script_return} == 0 ]]; then export ZO_COMMAND_RUNNING=${keep_running_count} fi rm -f "${tmperrfile}" trace X "---- meval end ----" trace X "return ${last_meval_script_return}" return ${last_meval_script_return} } #get script mapped by id mscript() { local scmdid="$1" local scmdscript='${'"${scmdid}"'_script}' local cmds='echo "'${scmdscript}'"' eval echo "`${cmds}`" } #call executable key,读取参数key对应脚本,并通过meval执行 mcall() { local scmdid="$1" if [[ ${last_meval_script_return} != 0 ]]; then trace X "mcall ${scmdid} last_meval_script_return=${last_meval_script_return}" return ${last_meval_script_return} fi trace X "==== mcall ${scmdid} script ====" local scmdscript="`mscript "${scmdid}"`" local scmdname_key="$""{${scmdid}_name}" local scmdname=`eval echo "${scmdname_key}"` if [[ "${scmdname}" == "" ]]; then scmdname="${scmdid}" fi trace S "${scmdname}" meval "${scmdscript}" local eval_script_return=$? trace X "==== mcall ${scmdid} end ====" return ${eval_script_return} } #重定向输出到日志格式输出 zo_command_log() { local scmdscript="$1" local tmpoutfile="$2" local tmpretfile="$3" local linecount=1 local charcount=0 local running="." local trace_level="${ZO_TRACE}" if [[ "${trace_level}" == "" ]]; then trace_level="${tl_response}" fi touch "${tmpoutfile}" local outputtime=`date +%s` while [[ "${running}" != "" ]]; do if [[ -e "${tmpretfile}" ]]; then running="-" fi #tail -n +从1开始的行号,至少会有一行内容 local lines=(`tail -n +${linecount} "${tmpoutfile}" | sed -e "s/${symbol_at}/@{symbol_at}/g" | sed -e "s/${symbol_star}/@{symbol_star}/g" | sed -e "s/${symbol_question}/@{symbol_question}/g" | sed -e "s/${space}/@{space}/g" | sed -e "s/${tab}/@{tab}/g" | sed -e "s/${return_char}/@{return_char}/g" | sed -e "s/^/@{start}/g"`) local n=1 for line in ${lines[@]} do line=`printf "%s" "${line}" | sed -e "s/@{start}//g" | sed -e "s/@{space}/${space}/g" | sed -e "s/@{tab}/${tab}/g" | sed -e "s/@{return_char}//g" | sed -e "s/@{symbol_star}/${symbol_star}/g" | sed -e "s/@{symbol_question}/${symbol_question}/g" | sed -e "s/@{symbol_at}/${symbol_at}/g"` xline=${line:${charcount}} if [[ ${n} == ${#lines[@]} ]]; then #最后一行,可能尚未输出完成 local currenttime=`date +%s` local waittime=`expr ${currenttime} - ${outputtime}` if [[ ${waittime} -gt 1 && ${#xline} -gt 0 ]]; then trace X "等1秒未完行输出" # 输出行数不增加 trace "${trace_level}" "${xline}" ">" # 输出部分信息,继续输出剩余内容 charcount=`expr ${#xline} + ${charcount}` outputtime=`date +%s` fi else if [[ ${#xline} -gt 0 || ${charcount} -eq 0 ]]; then trace "${trace_level}" "${xline}" ">" fi linecount=`expr ${linecount} + 1` n=`expr ${n} + 1` charcount=0 outputtime=`date +%s` fi done if [[ "${running}" == "-" ]]; then #结束 running="" if [[ ${#xline} -gt 0 ]]; then #输出最后一行剩余内容 trace "${trace_level}" "${xline}" ">" fi elif [[ ${n} == 1 ]]; then #没有输出 usleep 50000 fi done local eval_script_return="" if [[ -e "${tmpretfile}" ]]; then eval_script_return=`cat "${tmpretfile}"` else trace W "命令执行中断" eval_script_return=128 fi trace X "zo_command return ${eval_script_return}" return ${eval_script_return} } #后台执行命令,重定向输出到日志格式输出 export zo_command="" zo_command_eval() { if [[ "${zo_command}" != "" ]]; then #后台执行命令,环境变量无法共享输出 trace F "zo_command_eval不允许嵌套" fi zo_command="$*" local tmpoutfile="'''+zo_dir+'''/zerone.stdout.$$.txt" local tmpretfile="'''+zo_dir+'''/zerone.retcode.$$.txt" rm -f "${tmpoutfile}" rm -f "${tmpretfile}" #后台执行脚本,变量变化不能共享 zo_command_eval_function() { meval "${zo_command}" echo $? > "${tmpretfile}" } if [[ ${#ZO_SCRIPT} == 0 ]]; then if [[ -e "${ZO_FILE}" ]]; then export ZO_SCRIPT=`cat "${ZO_FILE}"`; else trace W "没有定义ZO_SCRIPT或ZO_FILE" fi fi #后台执行命令 zo_command_eval_function 1>${tmpoutfile} 2>&1 <&0 & #处理日志输出 zo_command_log "${zo_command}" "${tmpoutfile}" "${tmpretfile}" local ret=$? rm -f "${tmpoutfile}" rm -f "${tmpretfile}" zo_command="" return ${ret} } #expect script export EXP_TIMEOUT=20 export EXP_PROMPT='(%|#|\\$|>)' export EXP_SCRIPT=' expect { timeout { send_user "\n连接超时\n"; exit 233 } "yes/no" { send "yes\n"; exp_continue } "assword:" { send "$env(EXP_PASSWORD)\n" } eof { send_user "\n连接错误\n"; exit 232 } } expect { timeout { exp_continue } "assword:" { send_user "\n密码错误**$env(EXP_PASSWORD)**\n"; exit 231 } " --:-- ETA" { send_user "\n开始传输\n" expect { timeout { exp_continue } "100%" { send_user "\n传输完成\n" } ":" { send_user "\n正在传输\n"; exp_continue } } expect { timeout { exp_continue } eof { catch wait retval; send_user "\n退出传输连接[lindex $retval 3]\n"; exit [lindex $retval 3] } } } -re $env(EXP_PROMPT) { send "$env(REMOTE_COMMANDS)\nexit $?\n" expect { timeout { exp_continue } expect $env(EXP_INTERACT) eof { catch wait retval; send_user "\n退出命令连接[lindex $retval 3]\n"; exit [lindex $retval 3] } } } eof { catch wait retval; send_user "\n退出连接[lindex $retval 3]\n"; exit [lindex $retval 3] } } expect { timeout { exp_continue } eof { catch wait retval; send_user "\n退出不能处理的连接[lindex $retval 3]\n"; exit [lindex $retval 3] } } ' exp_run() { local exp_command="$1" trace X "EXP_PROMPT='${EXP_PROMPT}'" trace X "REMOTE_COMMANDS='${REMOTE_COMMANDS}'" expect -c " #trap {} {SIGINT SIGTERM} set timeout ${EXP_TIMEOUT} spawn ${exp_command} ${EXP_SCRIPT}" ret=$? return $ret } exp_fget() { remote_file="$1" local_file="$2" if [[ "$PIPE_HOST" == "" ]]; then trace E "没有指定通道" return 22 fi #上传文件 export EXP_INTERACT="" export REMOTE_COMMANDS="" export EXP_HOST="${PIPE_HOST%% *}" export EXP_USERNAME="${PIPE_USERNAME%% *}" export EXP_PASSWORD="${PIPE_PASSWORD%% *}" decode_password exp_run "scp \"${EXP_USERNAME}@${EXP_HOST}:${remote_file}\" \"${local_file}\"" ret=$? return $ret } exp_fput() { local_file="$1" remote_file="$2" if [[ "$PIPE_HOST" == "" ]]; then trace E "没有指定通道" return 22 fi #上传文件 export EXP_INTERACT="" export REMOTE_COMMANDS="" export EXP_HOST="${PIPE_HOST%% *}" export EXP_USERNAME="${PIPE_USERNAME%% *}" export EXP_PASSWORD="${PIPE_PASSWORD%% *}" decode_password exp_run "scp \"${local_file}\" \"${EXP_USERNAME}@${EXP_HOST}:${remote_file}\"" ret=$? return $ret } exp_remote() { local cmds="$@" if [[ "$PIPE_HOST" == "" ]]; then trace E "没有指定通道" return 22 fi #执行预处理脚本,创建工作目录 trace D "远程命令预处理" if [[ "${EXP_REMOTE_SCRIPT_FILE}" == "" ]]; then EXP_REMOTE_SCRIPT_FILE="${ZO_FILE}" fi export EXP_HOST="${PIPE_HOST%% *}" export EXP_USERNAME="${PIPE_USERNAME%% *}" export EXP_PASSWORD="${PIPE_PASSWORD%% *}" local NEXT_PIPE_HOST="${PIPE_HOST:${#EXP_HOST}}" local NEXT_PIPE_USERNAME="${PIPE_USERNAME:${#EXP_USERNAME}}" local NEXT_PIPE_PASSWORD="${PIPE_PASSWORD:${#EXP_PASSWORD}}" NEXT_PIPE_HOST="${NEXT_PIPE_HOST# }" NEXT_PIPE_USERNAME="${NEXT_PIPE_USERNAME# }" NEXT_PIPE_PASSWORD="${NEXT_PIPE_PASSWORD# }" export EXP_INTERACT="" export REMOTE_COMMANDS='if [[ -e "'''+zo_dir+'''" ]]; then ret=0; else mkdir -p '''+zo_dir+'''; chmod a+w '''+zo_dir+'''; ret=$?; fi; exit $ret' decode_password exp_run "ssh ${EXP_USERNAME}@${EXP_HOST}" ret=$? if [[ $ret != 0 ]]; then return $ret fi trace D "远程命令预处理完毕" #上传文件 if [[ "$PIPE_UPLOAD_FILE" != "" ]]; then if [[ "$PIPE_UPLOAD_DEST" == "" ]]; then PIPE_UPLOAD_DEST="$PIPE_UPLOAD_FILE" fi trace D "开始上传文件 {PIPE_UPLOAD_FILE} -> ${PIPE_UPLOAD_DEST}" exp_fput "${PIPE_UPLOAD_FILE}" "${PIPE_UPLOAD_DEST}" ret=$? if [[ $ret != 0 ]]; then return $ret fi trace D "文件上传完毕" fi #上传脚本 trace D "上传远程脚本" exp_fput "${ZO_FILE}" "${EXP_REMOTE_SCRIPT_FILE}" ret=$? if [[ $ret != 0 ]]; then return $ret fi #执行脚本 trace D "执行远程脚本,${EXP_USERNAME}@${EXP_HOST}" if [[ "${NEXT_PIPE_HOST}" == "" ]]; then export REMOTE_COMMANDS=" export OSNAME=${EVAL}uname${EVAL} if [[ $""{OSNAME} == 'AIX' ]]; then export LANG=ZH_CN.UTF-8; else export LANG=zh_CN.utf8; fi if [[ $""{OSNAME} == 'AIX' ]]; then export LC_ALL=ZH_CN.UTF-8; else export LC_ALL=zh_CN.utf8; fi export PIPE_UPLOAD_FILE='"${PIPE_UPLOAD_FILE}"' export PIPE_DOWNLOAD_FILE='"${PIPE_DOWNLOAD_FILE}"' export ZO_FILE='"${EXP_REMOTE_SCRIPT_FILE}"' export CUR_DIR='''+zo_dir+''' cd $""CUR_DIR export ZO_COMMAND='${cmds//\\\'/\\\'\\\"\\\'\\\"\\\'}' export PATH=/tmp/zo:$""{PATH} bash -c 'bash "${EXP_REMOTE_SCRIPT_FILE}"' ret=$""? rm -f '"${EXP_REMOTE_SCRIPT_FILE}"' exit $""ret" else export REMOTE_COMMANDS=" export OSNAME=${EVAL}uname${EVAL} if [[ $""{OSNAME} == 'AIX' ]]; then export LANG=ZH_CN.UTF-8; else export LANG=zh_CN.utf8; fi if [[ $""{OSNAME} == 'AIX' ]]; then export LC_ALL=ZH_CN.UTF-8; else export LC_ALL=zh_CN.utf8; fi export PIPE_UPLOAD_FILE='"${PIPE_UPLOAD_FILE}"' export PIPE_DOWNLOAD_FILE='"${PIPE_DOWNLOAD_FILE}"' export PIPE_HOST='"${NEXT_PIPE_HOST}"' export PIPE_USERNAME='"${NEXT_PIPE_USERNAME}"' export PIPE_PASSWORD='"${NEXT_PIPE_PASSWORD}"' export ZO_FILE='"${EXP_REMOTE_SCRIPT_FILE}"' export CUR_DIR='''+zo_dir+''' cd $""CUR_DIR export ZO_COMMAND='exp_remote ${cmds//\\\'/\\\'\\\"\\\'\\\"\\\'}' export PATH=/tmp/zo:$""{PATH} bash -c 'bash "${EXP_REMOTE_SCRIPT_FILE}"' ret=$""? rm -f '"${EXP_REMOTE_SCRIPT_FILE}"' exit $""ret" fi exp_run "ssh ${EXP_USERNAME}@${EXP_HOST}" ret=$? if [[ $ret != 0 ]]; then return $ret fi trace D "远程脚本执行完毕" trace X "PIPE_DOWNLOAD_FILE=$PIPE_DOWNLOAD_FILE" #下载文件 if [[ "$PIPE_DOWNLOAD_FILE" != "" ]]; then if [[ "$PIPE_DOWNLOAD_DEST" == "" ]]; then PIPE_DOWNLOAD_DEST="$PIPE_DOWNLOAD_FILE" fi trace D "开始下载文件 ${PIPE_DOWNLOAD_FILE} -> ${PIPE_DOWNLOAD_DEST}" exp_fget "${PIPE_DOWNLOAD_FILE}" "${PIPE_DOWNLOAD_DEST}" ret=$? if [[ $ret != 0 ]]; then return $ret fi trace D "文件下载完毕" fi #执行完毕,清理临时文件(临时目录还在使用,现在还不能清理整个临时目录) return 0 } fget() { remote_file="$1" local_file="$2" trace P "文件下载" export PIPE_DOWNLOAD_FILE="${remote_file}" export PIPE_DOWNLOAD_DEST="${local_file}" remote ls -l "${PIPE_DOWNLOAD_FILE}" ret=$? ls -l "${PIPE_DOWNLOAD_DEST}" if [[ $ret == 0 ]]; then trace P "文件下载完成" else trace P "文件下载失败[$ret]" fi return $ret } fput() { local_file="$1" remote_file="$2" trace P "文件上传" export PIPE_UPLOAD_FILE="${local_file}" export PIPE_UPLOAD_DEST="${remote_file}" ls -l "${PIPE_UPLOAD_FILE}" remote ls -l "${PIPE_UPLOAD_DEST}" ret=$? if [[ $ret == 0 ]]; then trace P "文件上传完成" else trace P "文件上传失败[$ret]" fi return $ret } #命令参数: # 要远程执行的命令 #环境变量参数: #完整的脚本 ZO_SCRIPT #目标服务器,可以顺序指定跳板机信息,用空格分割,password中的空格引号等特殊字符需要转码 # PIPE_HOST PIPE_USERNAME PIPE_PASSWORD #执行命令前上传指定文件 # PIPE_UPLOAD_FILE PIPE_UPLOAD_DEST #执行命令后下载指定文件 # PIPE_DOWNLOAD_FILE PIPE_DOWNLOAD_DEST remote() { local cmds="$@" trace D "远程命令执行开始" if [[ "$ZO_FILE" == "" ]]; then #本地生成脚本文件 export ZO_FILE="'''+zo_dir+'''/zerone.script.$$.txt" export EXP_REMOTE_SCRIPT_FILE="'''+zo_dir+'''/zerone.script.$HOSTIP.$$.txt" echo "${ZO_SCRIPT}" > "${ZO_FILE}" else export EXP_REMOTE_SCRIPT_FILE="${ZO_FILE}" fi exp_remote "${cmds}" ret=$? #清理 rm -f "${ZO_FILE}" #保留远程命令设定状态 if [[ $ret == 0 ]]; then trace D "远程命令执行完成" else trace D "远程命令执行失败[$ret]" fi return $ret } ''') def __init__(self, base=None): self.items = [] self.id_items = {} self.script = "" if base is not None: self.items += base.items self.base_script = settings.base_script def check_value_self_refference(self, sid, svalue): #键值引用仅支持一种形式 "${"+sid+"}" kn = svalue.find("${"+sid+"}") if kn >= 0: if sid in self.id_items: #变量引用前已经定义,修改已定义键名,原键值将被覆盖,修正键值自引用 sitem = self.id_items[sid] sitem.id = "zerone_" + ntid() self.id_items[sitem.id] = sitem svalue = svalue.replace("${"+sid+"}", "${"+sitem.id+"}") else: #变量引用前没有定义 svalue = svalue.replace("${"+sid+"}", "") return svalue def keep_setting(self, filename, lineno, skey, sfcmd, svalue, scomments): if skey != "": #标准键值格式 sid = skey svalue = self.check_value_self_refference(sid, svalue) else: #非标准键值 sid = "zerone_" + ntid() #关键字映射为 export id #runner.trace(tl_debug, skey+"='"+svalue+"'") sitem = setting_item(filename, lineno, sid, skey, sfcmd, svalue, scomments) self.items += [sitem] #覆盖保存sid对应的sitem self.id_items[sitem.id] = sitem #配置文件解析 def analyze(self, filename): with open(filename, "r") as f: #读取文件 st = f.read() stlines = st.splitlines() return self.analyze_lines(stlines, filename=filename) def analyze_lines(self, stlines, filename=None): #runner.trace(tl_debug, "{}".format(stlines)) if len(stlines) > 0: #重置已生成脚本 self.script = "" sln = 0 skey = "" sflag = "" sfcmd = "" svalue = "" scomments = "" lineno = 0 for line in stlines: lineno += 1 if sflag == ":": #多行处理标记(以!!开始的行,!!后面的内容会被丢弃,内容中出现的!!可以通过变量定义解决) #多行累加 if line.strip()[:2] == "!!": #多行结束标记 self.keep_setting(filename, sln, skey, sfcmd, svalue, scomments) skey = "" sflag = "" sfcmd = "" svalue = "" scomments = "" sln = lineno + 1 else: svalue += "\n" + line elif len(line.strip()) == 0: #跳过空行 pass elif line.strip()[0] == '#': #注释行,作为下一键值对行的名称 scomments = line.strip()[1:].strip() else: remo = re.match("\s*([^:=]*)\s*(:=?|=)([@~]?)\s*(.*)", line) if remo is not None: if svalue != "": #非格式化内容 #skey == "" sfcmd = svalue[0] if len(svalue)>0 and (svalue[0] == "@" or svalue[0] == "~") else "" svalue = svalue[1:] if sfcmd != "" else svalue self.keep_setting(filename, sln, skey, sfcmd, svalue, scomments) #标准键值格式 sln = lineno skey = remo.group(1) sflag = remo.group(2)[0] sfcmd = remo.group(3) svalue = remo.group(4) if sflag != ":": self.keep_setting(filename, sln, skey, sfcmd, svalue, scomments) skey = "" sflag = "" sfcmd = "" svalue = "" scomments = "" sln = lineno + 1 else: #非格式化内容 #与上一非格式化行合并 svalue += line + "\n" if svalue != "": #非格式化内容,包括多行没有结束标记 sfcmd = svalue[0] if len(svalue)>0 and (svalue[0] == "@" or svalue[0] == "~") else "" svalue = svalue[1:] if sfcmd != "" else svalue self.keep_setting(filename, sln, skey, sfcmd, svalue, scomments) #返回对象自身 return self #生成环境变量导出脚本 def str_export(self, exports, item, nokey_item_proc_method): if item.key == "": #非格式化内容,没有键名 if nokey_item_proc_method is None: #不做任何处理 return "" else: #按普通item处理 pass else: #赋值脚本只保留最后一次结果 if item.id in exports: return "# ${{{0}_key}}=\"{1}\"\n".format(item.id, item.key) #用最后键值设置输出 item = self.id_items[item.id] #记录已导出标记 exports[item.id] = item #生成导出脚本 script = "" value = item.value for sid in self.id_items: if value.find("${"+sid+"}") >= 0: sitem = self.id_items[sid] script += self.str_export(exports, sitem, nokey_item_proc_method) script += item.str_export() return script #生成脚本 def __str__(self, nokey_item_proc_method=None): if self.script != "": return self.script #基础脚本 self.script = self.base_script self.script += '''trace X "base script done"\n''' #定义脚本 exports = {} for item in self.items: self.script += self.str_export(exports, item, nokey_item_proc_method) self.script += '''trace X "extend settings done"\n''' return self.script #命令执行状态 class CommandStatus: def __init__(self): self.connected = False self.code = 0 self.info = "" self.warning_message="" #进程内线程间锁,日志输出与进程相关 trace_stdout_lock = threading.Lock() #日志信息跟踪解析输出 class CommandTracer: trace_file_locks = {} def __init__(self, status, logfile="", logfile_renew=True): self.status = status self.logfile = logfile if self.logfile is not None and self.logfile != "": if logfile_renew: with open(self.logfile, 'w') as f: f.write("") if not self.logfile in CommandTracer.trace_file_locks: CommandTracer.trace_file_locks[self.logfile] = threading.Lock() self.trace_file_lock = CommandTracer.trace_file_locks[self.logfile] else: self.trace_file_lock = None def log(self, level, msg, exp_host=None): #去掉多余的换行符 if len(msg)>0 and msg[-1] == "\n": msg = msg[:-1] lines = msg.splitlines() if len(lines)>1: for line in lines: self.log(level, line, exp_host) return #强制输出标记 force = level[0] == '_' if force: level = level[1:] #是否已经是trace格式 remo = re.match(pt_trace, msg) if remo is not None: #已经是trace格式,取原来的level level = remo.group(1) orig_msg = remo.group(2) else: #格式化 orig_msg = msg msg = "%s [%s] [%s] %s"%(tracetime(), ("%s.%d"%(HOSTIP,os.getpid())) if exp_host is None or exp_host == "" else exp_host, level, msg) #最后状态记录 if level == tl_status: smremo = re.match(pt_status_msg, orig_msg) if smremo is not None: self.status.info = smremo.group(1) self.status.code = int(smremo.group(2)) else: self.status.info = orig_msg self.status.code = 0 #保留警告信息 if re.match('.*[FEW].*', level) is not None: self.status.warning_message += "\n" + msg #输出到console if force or level in TRACE: trace_stdout_lock.acquire() try: print(msg) sys.stdout.flush() finally: trace_stdout_lock.release() #全输出到日志文件 if self.trace_file_lock is not None: self.trace_file_lock.acquire() try: with open(self.logfile, 'a') as f: f.write(msg+"\r\n") finally: self.trace_file_lock.release() class CommandRunner: def __init__(self, logfile="", logfile_renew=False): self.stimeout = 3600.0 #等待超时秒 self.sout = "" self.status = CommandStatus() self.tracer = CommandTracer(self.status, logfile, logfile_renew=logfile_renew) #因为状态信息只有一个,所以一个CommandRunner同时只能跑一个命令 #相当于一个执行窗口 self.subprocess_lock = threading.Lock() def trace(self, level, msg, exp_host=None): self.tracer.log(level, msg, exp_host) def output(self, subprocess_outfile, exp_host, silent): with open(subprocess_outfile, 'r') as fout: sout = fout.read() n = len(sout) - len(self.sout) if n>0: if not silent: st = sout[-n:] st = st.replace("\r", "") if len(st)>0 and st[-1] == "\n": st = st[:-1] lines = st.split("\n") for line in lines: if len(self.sout)>0: self.trace(tl_detail, line, exp_host) else: self.trace(tl_detail, line) self.sout += line + "\n" self.sout = sout return n return 0 def subprocess(self, cmd, exp_host=None, silent=False, envs=None): self.subprocess_lock.acquire() subprocess_outfile = "%s/zerone.stdout.%s.txt"%(zo_dir, ntid()) try: env = os.environ.copy() if envs is not None: env.update(envs) env["ZO_SCRIPT"] = cmd self.sout = "" #clear last sout with open(subprocess_outfile, 'w') as fout: child = subprocess.Popen('bash -c "${ZO_SCRIPT}"', shell=True, stdout=fout, stderr=fout, stdin=None, env=env) timeout = time.time() + self.stimeout while (self.stimeout<=0 or time.time()1: for skv in sys_argv: n = skv.find("=") if n >= 0 and re.match("\w+", skv[:n]): sk = skv[:n] sv = skv[n+1:] env_settings.analyze_lines("{0}:={1}\n!!".format(sk, sv).splitlines()) #传递的环境变量,zo.py的嵌套调用时,py进程之间传递环境变量 zo_env_keys=[] if "ZO_ENV_KEYS" in os.environ and os.environ["ZO_ENV_KEYS"] != "": zo_env_keys=os.environ["ZO_ENV_KEYS"].split(",") for envk in zo_env_keys: if envk != "" and envk in os.environ and os.environ[envk] != "": env_settings.analyze_lines("{0}:\n{1}\n!!".format(envk, os.environ[envk]).splitlines()) #参数指定可向下传递的环境变量 if zo_envs is not None: for envk in zo_envs: env_settings.analyze_lines("{0}:\n{1}\n!!".format(envk, zo_envs[envk]).splitlines()) #向下传递的环境变量 zo_env_keys += zo_envs.keys() zo_env_keys = dict.fromkeys(zo_env_keys) #向下传递的环境变量 env_settings.analyze_lines(["ZO_ENV_KEYS={0}".format(",".join(zo_env_keys))]) #参数指定信息 env_settings.analyze_lines([ "host=%s"%host, "username=%s"%username, "password=%s"%password]) #覆盖关键配置信息及待执行命令 scmdscript = str(env_settings) + (''' trace X "specified commands begin" trace A "${ZO_COMMAND}" zo_command_eval "${ZO_COMMAND}" export ret=$? trace X "specified commands end [${ret}]" exit $ret ''') return scmdscript def run_script(self, host, username, password, cmds, sys_argv=None, envs=None): scmdscript = self.build_script(host, username, password, cmds, sys_argv=sys_argv) renvs={} if envs is not None: renvs.update(envs); renvs["PIPE_HOST"]=host renvs["PIPE_USERNAME"]=username renvs["PIPE_PASSWORD"]=password renvs["ZO_COMMAND"]=cmds if tl_any in TRACE: ret,sout = self.command_run('echo "${SCMDS_SCRIPT}" | cat -n', silent=True, envs={"SCMDS_SCRIPT": scmdscript}) self.trace(tl_any, sout) ret,sout = self.command_run(scmdscript, envs=renvs) if tl_any in TRACE and ret != 0: with open("scmdscript.txt", 'w') as fcmds: fcmds.write(scmdscript) return ret,sout #action=local cmds=? #action=remote host=? username=? password=? cmds=? #action=fget host=? username=? password=? local=? remote=? #action=fput host=? username=? password=? local=? remote=? def run(self, action="", host="", username="", password="", cmds="", local="", remote="", sys_argv=None): self.trace(tl_debug, '''action="{0}", host="{1}", username="{2}", password="{3}", cmds="{4}", local="{5}", remote="{6}", sys_argv="{7}"'''.format(action, host, username, password, cmds, local, remote, sys_argv)) if action == "fget" and remote != "": if local == "": #缺省取当前目录下的同名文件 local = remote.split("/")[-1] self.trace(tl_proc, "下载文件 %s@%s:%s 到 %s"%(username, host, remote, local)) cmds = "fget '{0}' '{1}'".format(remote, local) ret, sout = self.run_script(host, username, password, cmds, sys_argv, envs={"ZO_TRACE": tl_detail}) elif action == "fput" and remote != "": if local == "": #缺省取当前目录下的同名文件 local = remote.split("/")[-1] self.trace(tl_proc, "上传文件 %s 到 %s@%s:%s"%(local, username, host, remote)) cmds = "fput '{0}' '{1}'".format(local, remote) ret, sout = self.run_script(host, username, password, cmds, sys_argv, envs={"ZO_TRACE": tl_detail}) else: if action == "": if username != "": action = "remote" if host == "": #缺省连接本机 host = "127.0.0.1" else: action = "local" if action == "remote": self.trace(tl_proc, "执行远程命令 %s@%s: %s"%(username, host, cmds)) cmds = "remote '%s'"%(cmds.replace("'", "'\"'\"'")) ret, sout = self.run_script(host, username, password, cmds, sys_argv, envs={"ZO_TRACE": tl_detail}) elif action == "local": self.trace(tl_proc, "执行本地命令 %s"%(cmds)) ret, sout = self.run_script(host, username, password, cmds, sys_argv, envs={"ZO_TRACE": tl_response}) else: self.trace(tl_error, "unknow action %s"%(action)) ret = 22 #Invalid argument sout = "" if ret is None: #设置状态,命令执行超时,已经强制结束 self.trace(tl_status, "操作超时[230]") ret = 230 if ret != 0: #设置状态,代替返回错误码 errmsg = ERROR_MSG[ret].strip() self.trace(tl_status, "%s[%d]"%(errmsg, ret), exp_host="%s.%d%s"%(HOSTIP,os.getpid(),"" if host == "" else ("--"+host))) #ERROR_MSG_230="操作超时 " #ERROR_MSG_231="密码错误 " #ERROR_MSG_232="远程连接错误 " #ERROR_MSG_233="远程连接超时 " #ERROR_MSG_234="运行时错误 " #ERROR_MSG_235="交互处理表达式语法错误 " self.status.connected = (self.status.code < 230 or self.status.code >235) if self.status.code == 0: self.trace(tl_status, "操作完成[0]", exp_host="%s.%d%s"%(HOSTIP,os.getpid(),"" if host == "" else ("--"+host))) return ret, sout #命令行参数 class zoargs: def __init__(self, argv): global TRACE self.host = "" self.username = "" self.password = "" self.action = "" self.cmds = "" self.local = "" self.remote = "" self.TRACE = TRACE self.logdir = "" self.debug = "" #parse argv self.parse(argv) TRACE = self.TRACE def parse(self, argv): cmds = None for s in argv: if cmds is None: n = s.find("=") if n >= 0: self.__dict__[s[:n]] = s[n+1:] elif s == "cmds": cmds = "" else: self.__dict__[s] = s elif cmds == "": cmds = s else: cmds += " " + s if cmds is not None: self.cmds = cmds if self.debug != "": self.cmds = self.debug + " \"" + self.cmds.replace("'", "").replace("\"", "") + "\"" args = zoargs(sys.argv) runner = CommandRunner(logfile=(args.logdir+"/"+args.username+"@"+args.host+".txt") if args.logdir != "" else "") #密码加密 #python -c "import base64; print(base64.b64encode('123456'));" #执行远程命令 #可以通过参数设置命令所需环境变量 #在指定节点上执行命令 #python zo.py TRACE=FEWSIPD action=remote host=17.192.94.65 password='QkpAbnBjMjAxOA==' cmds="ls" # #python zo.py action=local cmds=? #python zo.py action=remote host=? username=? password=? cmds=? #python zo.py action=fget host=? username=? password=? local=? remote=? #python zo.py action=fput host=? username=? password=? local=? remote=? def main(): #切换到当前目录的上一级目录 cur_file_dir = os.path.split(os.path.abspath(__file__))[0]; envs_work_dir = os.path.split(cur_file_dir)[0]; os.chdir(envs_work_dir); runner.trace(tl_debug, "current work dir: "+envs_work_dir) runner.trace(tl_debug, "ro: python '"+"' '".join(sys.argv)+"'") runner.trace(tl_detail, "args: "+str(args.__dict__)) if args.cmds.find("exit") >= 0: print("远程命令中不能使用exit,需要用return替代,确实需要exit可以通过变量实现!") return 1 ret, sout = runner.run(args.action, args.host, args.username, args.password, cmds=args.cmds, local=args.local, remote=args.remote, sys_argv=sys.argv) #不能清除临时目录,可能还在使用,os.system("rm -rf %s"%zo_dir) return ret ERROR_MSG = {} ERROR_MSG[1]="Operation not permitted " ERROR_MSG[2]="No such file or directory " ERROR_MSG[3]="No such process " ERROR_MSG[4]="Interrupted system call " ERROR_MSG[5]="Input/output error " ERROR_MSG[6]="No such device or address " ERROR_MSG[7]="Argument list too long " ERROR_MSG[8]="Exec format error " ERROR_MSG[9]="Bad file descriptor " ERROR_MSG[10]="No child processes " ERROR_MSG[11]="Resource temporarily unavailable " ERROR_MSG[12]="Cannot allocate memory " ERROR_MSG[13]="Permission denied " ERROR_MSG[14]="Bad address " ERROR_MSG[15]="Block device required " ERROR_MSG[16]="Device or resource busy " ERROR_MSG[17]="File exists " ERROR_MSG[18]="Invalid cross-device link " ERROR_MSG[19]="No such device " ERROR_MSG[20]="Not a directory " ERROR_MSG[21]="Is a directory " ERROR_MSG[22]="Invalid argument " ERROR_MSG[23]="Too many open files in system " ERROR_MSG[24]="Too many open files " ERROR_MSG[25]="Inappropriate ioctl for device " ERROR_MSG[26]="Text file busy " ERROR_MSG[27]="File too large " ERROR_MSG[28]="No space left on device " ERROR_MSG[29]="Illegal seek " ERROR_MSG[30]="Read-only file system " ERROR_MSG[31]="Too many links " ERROR_MSG[32]="Broken pipe " ERROR_MSG[33]="Numerical argument out of domain " ERROR_MSG[34]="Numerical result out of range " ERROR_MSG[35]="Resource deadlock avoided " ERROR_MSG[36]="File name too long " ERROR_MSG[37]="No locks available " ERROR_MSG[38]="Function not implemented " ERROR_MSG[39]="Directory not empty " ERROR_MSG[40]="Too many levels of symbolic links " ERROR_MSG[42]="No message of desired type " ERROR_MSG[43]="Identifier removed " ERROR_MSG[44]="Channel number out of range " ERROR_MSG[45]="Level 2 not synchronized " ERROR_MSG[46]="Level 3 halted " ERROR_MSG[47]="Level 3 reset " ERROR_MSG[48]="Link number out of range " ERROR_MSG[49]="Protocol driver not attached " ERROR_MSG[50]="No CSI structure available " ERROR_MSG[51]="Level 2 halted " ERROR_MSG[52]="Invalid exchange " ERROR_MSG[53]="Invalid request descriptor " ERROR_MSG[54]="Exchange full " ERROR_MSG[55]="No anode " ERROR_MSG[56]="Invalid request code " ERROR_MSG[57]="Invalid slot " ERROR_MSG[59]="Bad font file format " ERROR_MSG[60]="Device not a stream " ERROR_MSG[61]="No data available " ERROR_MSG[62]="Timer expired " ERROR_MSG[63]="Out of streams resources " ERROR_MSG[64]="Machine is not on the network " ERROR_MSG[65]="Package not installed " ERROR_MSG[66]="Object is remote " ERROR_MSG[67]="Link has been severed " ERROR_MSG[68]="Advertise error " ERROR_MSG[69]="Srmount error " ERROR_MSG[70]="Communication error on send " ERROR_MSG[71]="Protocol error " ERROR_MSG[72]="Multihop attempted " ERROR_MSG[73]="RFS specific error " ERROR_MSG[74]="Bad message " ERROR_MSG[75]="Value too large for defined data type " ERROR_MSG[76]="Name not unique on network " ERROR_MSG[77]="File descriptor in bad state " ERROR_MSG[78]="Remote address changed " ERROR_MSG[79]="Can not access a needed shared library " ERROR_MSG[80]="Accessing a corrupted shared library " ERROR_MSG[81]=".lib section in a.out corrupted " ERROR_MSG[82]="Attempting to link in too many shared libraries " ERROR_MSG[83]="Cannot exec a shared library directly " ERROR_MSG[84]="Invalid or incomplete multibyte or wide character " ERROR_MSG[85]="Interrupted system call should be restarted " ERROR_MSG[86]="Streams pipe error " ERROR_MSG[87]="Too many users " ERROR_MSG[88]="Socket operation on non-socket " ERROR_MSG[89]="Destination address required " ERROR_MSG[90]="Message too long " ERROR_MSG[91]="Protocol wrong type for socket " ERROR_MSG[92]="Protocol not available " ERROR_MSG[93]="Protocol not supported " ERROR_MSG[94]="Socket type not supported " ERROR_MSG[95]="Operation not supported " ERROR_MSG[96]="Protocol family not supported " ERROR_MSG[97]="Address family not supported by protocol " ERROR_MSG[98]="Address already in use " ERROR_MSG[99]="Cannot assign requested address " ERROR_MSG[100]="Network is down " ERROR_MSG[101]="Network is unreachable " ERROR_MSG[102]="Network dropped connection on reset " ERROR_MSG[103]="Software caused connection abort " ERROR_MSG[104]="Connection reset by peer " ERROR_MSG[105]="No buffer space available " ERROR_MSG[106]="Transport endpoint is already connected " ERROR_MSG[107]="Transport endpoint is not connected " ERROR_MSG[108]="Cannot send after transport endpoint shutdown " ERROR_MSG[109]="Too many references=cannot splice " ERROR_MSG[110]="Connection timed out " ERROR_MSG[111]="Connection refused " ERROR_MSG[112]="Host is down " ERROR_MSG[113]="No route to host " ERROR_MSG[114]="Operation already in progress " ERROR_MSG[115]="Operation now in progress " ERROR_MSG[116]="Stale NFS file handle " ERROR_MSG[117]="Structure needs cleaning " ERROR_MSG[118]="Not a XENIX named type file " ERROR_MSG[119]="No XENIX semaphores available " ERROR_MSG[120]="Is a named type file " ERROR_MSG[121]="Remote I/O error " ERROR_MSG[122]="Disk quota exceeded " ERROR_MSG[123]="No medium found " ERROR_MSG[124]="Wrong medium type " ERROR_MSG[125]="Operation canceled " ERROR_MSG[126]="权限不够(Required key not available) " ERROR_MSG[127]="Command not found(Key has expired) " ERROR_MSG[128]="Key has been revoked " ERROR_MSG[129]="Key was rejected by service " ERROR_MSG[130]="执行过程被中断(Owner died) " ERROR_MSG[131]="State not recoverable " ERROR_MSG[143]="主进程被杀 " ERROR_MSG[230]="操作超时 " ERROR_MSG[231]="密码错误 " ERROR_MSG[232]="远程连接错误 " ERROR_MSG[233]="远程连接超时 " ERROR_MSG[234]="运行时错误 " ERROR_MSG[235]="交互处理表达式语法错误 " ERROR_MSG[239]="找不到安装介质 " ERROR_MSG[240]="脚本执行过程中发生未知错误 " ERROR_MSG[241]="部署依赖不正确 " ERROR_MSG[242]="启动失败 " ERROR_MSG[243]="返回格式不正确 " ERROR_MSG[244]="服务器名称不符 " ERROR_MSG[245]="卸载失败 " ERROR_MSG[246]="参数错误 " ERROR_MSG[247]="参数错误 " ERROR_MSG[248]="参数错误 " ERROR_MSG[249]="远程脚本执行错误 " ERROR_MSG[255]="部署已存在,命令执行失败,请重试 " ERROR_MSG[254]="安装介质已分发 " ERROR_MSG[253]="部署存在未启动 " ERROR_MSG[252]="未部署 " ERROR_MSG[251]="路径存在未安装 " ERROR_MSG[250]="status250 " if __name__ == '__main__': try: exit(main()) except KeyboardInterrupt as ki: print("") exit(130) # _oo0oo_ # o8888888o # 88" . "88 # (| -_- |) # 0\ = /0 # ___/`---'\___ # .' \\| |// '. # / \\||| : |||// \ # / _||||| -:- |||||- \ # | | \\\ - /// | | # | \_| ''\---/'' |_/ | # \ .-\__ '-' ___/-. / # ___'. .' /--.--\ `. .'___ # ."" '< `.___\_<|>_/___.' >' "". # | | : `- \`.;`\ _ /`;.`/ - ` : | | # \ \ `_. \_ __\ /__ _/ .-` / / # =====`-.____`.___ \_____/___.-`___.-'===== # `=---=' # # # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # 见见之时 见非是见 见犹离见 见不能及 #