1、EasyXMS簡介python
EasyXMS是一個用於批量管理Linux/Unix服務器的簡易系統,如:多線程批量執行命令、多線程批量上傳文件等功能,EasyXMS使用Python2.7編寫,並使用paramiko模塊來實現SSH鏈接和SFTP功能。linux
關注項目主頁以獲取最新版本git
http://git.oschina.net/leedays/EasyXMS算法
有什麼優點?shell
簡單易用vim
輕量級,只須要一個腳本文件,不須要安裝Client端bash
安裝部署快,一鍵安裝所需的環境服務器
2、主要功能多線程
批量執行命令
app
批量上傳文件
記錄每次輸入的命令到文件
記錄每次執行命令的結果到文件
3、環境安裝
1.Python2.7
下載地址:http://www.python.org
2.easy_install 工具(是Python安裝模塊的一個工具,像yum,能夠自動解決依賴)
下載地址: http://peak.telecommunity.com/dist/ez_setup.py
3.PyCrypto 2.1+ 模塊(PyCrypto是使用Python編寫的加密工具包)
下載地址:https://www.dlitz.net/software/pycrypto/
4.paramiko 模塊(是用Python編寫的支持SSH協議的模塊)
使用easy_install 進行安裝
具體的安裝過程,參看上一篇文章:
http://linux5588.blog.51cto.com/65280/1275180
想省事? 可下載博客附件的一鍵安裝腳本,來安裝paramiko模塊須要的環境
請使用 < source 腳本名 > 的方式執行腳本
四.運行過程當中產生的文件
在腳本的執行過程當中,默認是會在當前目錄下生成如下文件
1. server.conf
該文件是用於存儲各個服務器的鏈接信息如 IP地址:端口:用戶名:密碼,存儲的信息是通過簡單的加密,固然使用的可逆的加密算法,以下所示每行表明一個IP信息:
MTkyLjE2OC4xMDAuMjAxOjIyOnJvb3Q6MTIzNDU2 MTkyLjE2OC4xMDAuMjAyOjIyOnJvb3Q6MTIzNDU2
2.paramiko.log
該文件是paramiko模塊在運行過程當中產生的日誌文件
3.command_history.log
該文件是記錄執行過的命令
4.command_result_history.log
該文件是記錄執行過的命令的結果
5、怎麼執行腳本?
1. 使用 python 腳本名 方式來執行
python EasyXMS.py
2. 使用 腳本的絕對路徑來執行,注意加上可執行權限
./EasyXMS.py
若是出現如下狀況,使用dos2unix轉換一下便可
若是沒有dos2unix,那麼在Windows上使用像EmEditor這類的文本編輯器(不要使用Windows自帶的記事本),把換行符修改成僅Unix,便可
6、操做演示
1.主菜單
輸入? 便可得到主菜單幫助
功能描述:
0 增長服務器的IP信息到配置文件(server.conf 如下都指的是該配置文件) 1 加載一個包含多個服務器IP信息的文件,用於批量添加服務器到 2 列出當前配置文件中存在的服務器 3 從配置文件中刪除指定的服務器信息 4 清空配置文件 5 批量執行命令 6 批量上傳文件 7 清屏
2.選項 0 增長服務器的IP信息到配置文件
3.選項1 加載一個包含多個服務器IP信息的文件
準備一個文本文件,裏面的包含這些內容 IP地址:端口(22端口能夠不寫):用戶名:密碼 每行一個 例如:
192.168.100.204:root:123456
192.168.100.205:root:123
192.168.100.206:root:123
4.選項 2 列出當前配置文件中存在的服務器
5.選項 3 從配置文件中刪除指定的服務器信息
6.選項 4 清空配置文件
7.選項 5 批量執行命令
8.選項 6 批量上傳文件
9.選項 7 清屏
7、注意事項
1.不能使用vi或者vim編輯遠程文件
2.目前不支持tab鍵補全命令
3.在按 Backspace 鍵出現如下狀況時
可用以下方式來解決:
若是你使用的SecureCRT,能夠這樣解決
若是使用的是Xshell,能夠這樣解決:
putty 使用下來沒這個問題,可是若是有這個問題,找到相似設置的地方 設置一下便可
八.腳本內容
#!/usr/bin/env python # coding=utf-8 #--------------------------------------------------------------------- # Name: EasyXMS # Purpose: Multithreading Batch Execution Commands and Upload Files # Version: 1.0 # Author: LEO # BLOG: http://linux5588.blog.51cto.com # EMAIL: chanyipiaomiao@163.com # Created: 2013-7-29 # Copyright: (c) LEO 2013 #--------------------------------------------------------------------- from base64 import encodestring,decodestring from os.path import basename,exists,isfile from sys import exit,platform from time import strftime from Queue import Queue from os import system import threading import paramiko # 控制輸出類 class PrintHelp(object): def __init__(self): """定義命令列表""" self.cmd_list = [' Add Server ', ' Load File', ' List Servers ', ' Delete Server ', ' Empty File', ' Execute Command ', ' Upload File ', ' Clear Screen', ] self.name = 'EasyXMS' self.example = '*** Example *** : ' self.example_ip = '%s192.168.1.1:22:root:123456 192.168.1.3:test:123456 ' % self.example self.example_delete = '%s192.168.1.1 192.168.1.2 ' % self.example self.example_filepath = '%s: /tmp/1.txt /tmp' % self.example def printPrompt(self): """腳本運行提示符""" return "%s( ? Help ) >>> " % self.name def loopPrintCmd(self): """循環打印命令列表""" print print "Please Choose A Number : " print for i, v in enumerate(self.cmd_list): print "%5s %-5s" % (i, v) print def printHead(self): """輸出頭部提示信息""" print_string = 'Welcome to Use < %s > , Please Input < ? > ' \ 'Get Help Or Direct < Enter > Exit' % self.name string_temp = "-" * len(print_string) print string_temp print print print_string print print string_temp print def wantQuit(self): """退出提示""" return "Want Quit? (y/n) [y|Enter]: " def printInputWrong(self): """提示命令輸入有誤""" print print "Wrong Input,Enter < ? > Get Help Or Enter Exit ! " print def printFileError(self, e): """提示讀取主機配置文件出錯""" print "Oops! Read File Occur Wrong : %s" % e def printFileNotExistOrEmpty(self): """提示主機配置文件不存在或者爲空,請建立或者添加IP地址到文件""" print print "The File [ %s ] Not Exist Or Empty," \ " Enter < 0/1 > to Create Or Add Server IP." % configfile print def printInvalidIP(self,info): """提示無效的信息""" print "Invalid Format [ %s ] !!! " % info print def printIPFormat(self): """添加服務器IP地址的格式""" print print self.example_ip def printDeleteIPFormat(self): """刪除服務器IP時的格式""" print print self.example_delete print def printInputIP(self): """提示輸入IP地址以空格分開""" return "Enter Server Info : " def printInputIPForDelete(self): """提示輸入服務器的IP地址""" return "Enter Server IP (Only IP Address) : " def printAddIPSuccessful(self, ip): """提示增長服務器IP到配置文件成功""" print "Add Server [ %s ] to [ %s ] Successful !!! " % (ip, configfile) print def printRemoveIPSuccessful(self,ip): """提示從配置文件中刪除IP地址成功""" print "Remove [ %s ] From [ %s ] Successful !!!" % (ip,configfile) print def printIPNotInFile(self, ip): """輸出IP地址不在配置文件中""" print "%s is Not in [ %s ]" % (ip, configfile) print def printIPAlreadyInFile(self, ip): """輸出IP地址已經在配置文件中""" print "IP [ %s ] Already in [ %s ] !!!" % (ip, configfile) print def youSureEmptyFile(self): """提示用戶是否確認清空文件""" return "Are You Sure Empty [ %s ] (y/n) : " % configfile def emptySuccessful(self): """輸出清空文件成功""" print "Empty Config File [ %s ] Successful !!!" % configfile print def emptyFileFailure(self, e): """輸出清空文件失敗""" print "Empyt File %s Failure !!! ( %s )" % (configfile, e) def enterCommand(self): """提示輸入命令""" return "Enter Command( q|Q Quit ) : " def enterFilePath(self): """提示輸入文件的路徑""" return "Enter [ Local ] Path And < Remote > Path : " def enterFilePath2(self): """加載文件時,提示輸入文件路徑""" return "Enter File Path( File Include ip:username:password ) : " def invaildCommand(self): """輸出無效的命令""" print "Invaild Command !!!" print def invaildFilePath(self,filepath): """無效的文件路徑""" print "Invaild File Path ' %s ' !!!" % filepath print def printSSHBePatient(self): """輸出正在初始化SSH鏈接,請耐心等待""" print "Initializing < SSH > Connections, Please Be Patient ..." print def printSFTPBePatient(self): """輸出正在初始化SFTP鏈接,請耐心等待""" print "Initializing < SFTP > Connections, Please Be Patient ..." print def printCommandResult(self,ip,cmd,out,error): """輸出命令執行的結果""" result = "======== [ %s ] Execute Command: ' %s ',The Result is : \n\n%s%s" % (ip,cmd,out,error) print result return result def printCanNotConnectIP(self,ip): """輸出不能鏈接到該IP地址""" print "[ Error ] ... Can't Connect This IP ' %s ', Please Check !!!" % ip print def uploadFileOK(self, src, ip): """上傳文件成功""" print "Upload File < %s > to %s ... OK " % (src, ip) print def uploadFileError(self, src, ip, e): """上傳文件出現錯誤""" print "Upload File < %s > to %s ... Error: %s " % (src, ip, e) print def printFilePath(self): """輸出上傳文件時路徑的例子""" print print self.example_filepath def printArgsNotenough(self): """輸出參數不夠提示""" print "Arguments Not Enough !!!" print def getDateTime(self): """獲取日期和時間""" return strftime('%Y-%m-%d %H:%M:%S') def returnDateString(self): """返回一個日期加星號的字符串用於標識命令執行的結果""" return '\n********** %s **********\n\n' % self.getDateTime() def printCannotCreateFile(self,filename,error): """提示沒法建立文件""" print print "Can't Create < %s > !!! : %s" % (filename, error) print # 控制輸入類 class InputValue(object): def __init__(self): self.inputValue = None def setInputvalue(self): """提示輸入命令""" self.inputValue = raw_input(printhelp_obj.printPrompt()) return self.inputValue def closeConnections(self): """關閉鏈接函數""" ssh_connect_pool_dict = connectSSH_obj.getConnectionPool() sftp_connect_pool_dict = connectSFTP_obj.getConnectionPool() if ssh_connect_pool_dict: for i in ssh_connect_pool_dict: ssh_connect_pool_dict[i].close() if sftp_connect_pool_dict: for j in sftp_connect_pool_dict: sftp_connect_pool_dict[j].close() def exitFunction(self): """退出本程序""" try: print quitp = raw_input(printhelp_obj.wantQuit()) print except EOFError: self.closeConnections() print print exit(0) if not quitp or quitp == 'y' or quitp == 'Y': self.closeConnections() exit(0) else: self.loopGetValue() def loopGetValue(self): """循環獲得用戶的輸入,並判斷""" try: while self.setInputvalue(): if self.inputValue == '?': printhelp_obj.loopPrintCmd() elif self.inputValue == '0': readwriteconfigFile_obj.writeIPToFile() elif self.inputValue == '1': readwriteconfigFile_obj.loadFile() elif self.inputValue == '2': readwriteconfigFile_obj.readIPFromFile() elif self.inputValue == '3': readwriteconfigFile_obj.deleteIPFromFile() elif self.inputValue == '4': readwriteconfigFile_obj.emptyConfigFile() elif self.inputValue == '5': startExecAction_obj.startExecCommand() elif self.inputValue == '6': startExecAction_obj.startSFTP() elif self.inputValue == '7': startExecAction_obj.clearScreen() else: printhelp_obj.printInputWrong() else: self.exitFunction() except EOFError : print self.exitFunction() except KeyboardInterrupt: print self.exitFunction() # 操做配置文件類 class ReadWriteConfigFile(object): def readIPAsDict(self): """讀取全部的IP地址爲一個字典 像這樣: {'192.168.1.1':{'port':22,'user':root,'pwd':123456}, } """ ip_dict = {} try: data = open(configfile,'r') config_file = data.readlines() if config_file: for i in config_file: i = decodestring(i) tempstring = i.split(':') ip = tempstring[0] if ip not in ip_dict: ip_dict[ip] = {'port':int(tempstring[1]), 'user':tempstring[2], 'pwd':tempstring[3]} except: pass # 若是文件句柄被打開過,則關閉 try: data.close() except: pass return ip_dict def readIPFromFile(self): """返回一個IP地址列表""" ip_list = self.readIPAsDict().keys() if ip_list: print for i in ip_list: print i print else: printhelp_obj.printFileNotExistOrEmpty() def fileAppendObject(self,filename): """以追加方式打開文件並返回對象""" data = None error = None try: data = open(filename,'a') except IOError,e: error = e return data,error def writeFile(self,data,server_list): """寫文件函數,用於寫服務器的配置信息""" ip_list = self.readIPAsDict().keys() for ip_info in server_list: ip,info = self.checkIPInfo(ip_info) if ip and info: if ip not in ip_list: data.write(encodestring(info)) printhelp_obj.printAddIPSuccessful(ip) else: printhelp_obj.printIPAlreadyInFile(ip) def loadFile(self): """加載一個包含 IP地址:端口:用戶名:密碼 的文件批量進行添加""" data2,error = self.fileAppendObject(configfile) if data2: print filename = raw_input(printhelp_obj.enterFilePath2()) print if filename: try: data = open(filename,'r') file_ip_list = data.readlines() data.close() self.writeFile(data2, file_ip_list) except IOError,e: printhelp_obj.printFileError(e) print data2.close() else: printhelp_obj.printArgsNotenough() else: printhelp_obj.printCannotCreateFile(configfile,error) def checkIPInfo(self,ipinfo): """簡單的檢查輸入的IP等信息格式是否正確""" ip,info = None,None maohao_num = ipinfo.count(':') if maohao_num in (2,3): if ipinfo.count('.') == 3: info_list = ipinfo.split(':') if maohao_num == 3: ip, port, user, passwd = info_list if maohao_num == 2: ip, user, passwd = info_list port = 22 if passwd: info = '%s:%s:%s:%s' % (ip, port, user, passwd) else: printhelp_obj.printInvalidIP(ipinfo) else: printhelp_obj.printInvalidIP(ipinfo) else: printhelp_obj.printInvalidIP(ipinfo) return ip,info def writeIPToFile(self): """寫入IP地址到配置文件""" data,error = self.fileAppendObject(configfile) if data: printhelp_obj.printIPFormat() print hosts = raw_input(printhelp_obj.printInputIP()) print if not hosts: printhelp_obj.printArgsNotenough() else: server_list = hosts.split() self.writeFile(data,server_list) data.close() else: printhelp_obj.printCannotCreateFile(configfile, error) def deleteIPFromFile(self): """從配置文件中刪除指定的IP地址""" ip_from_configfile_dict = self.readIPAsDict() if ip_from_configfile_dict: printhelp_obj.printDeleteIPFormat() hosts = raw_input(printhelp_obj.printInputIPForDelete()) print if len(hosts) == 0: printhelp_obj.printArgsNotenough() else: delete_ip_list = hosts.split() for i in delete_ip_list: if i in ip_from_configfile_dict: del ip_from_configfile_dict[i] printhelp_obj.printRemoveIPSuccessful(i) else: printhelp_obj.printIPNotInFile(i) if ip_from_configfile_dict: try: data = open(configfile, 'w') for key,value in ip_from_configfile_dict.items(): tempstring = "%s:%s:%s:%s" % (key, value['port'], value['user'], value['pwd']) data.write(encodestring(tempstring)) except IOError, e: print printhelp_obj.printFileError(e) print else: data = open(configfile, 'w') try: data.close() except: pass else: printhelp_obj.printFileNotExistOrEmpty() def emptyConfigFile(self): """清空整個主機配置文件""" if self.readIPAsDict(): print ok = raw_input(printhelp_obj.youSureEmptyFile()) print if ok == 'y' or ok == 'Y': try: data = open(configfile, 'w') except IOError,e: printhelp_obj.emptyFileFailure(e) # 若是文件句柄被打開過,則關閉 try: data.close() printhelp_obj.emptySuccessful() except: pass else: printhelp_obj.printFileNotExistOrEmpty() def readCommandHistory(self): """讀取歷史執行命令""" command_history_list = [] try: data = open(command_history,'r') for cmd in data.readlines(): command_history_list.append(cmd.strip('\n')) data.close() except IOError: pass return command_history_list def writeCommandToFile(self,cmd): """命令寫入到文件""" command_history_list = self.readCommandHistory() data,error = self.fileAppendObject(command_history) if data: if cmd not in command_history_list: data.write(cmd+'\n') data.close() else: printhelp_obj.printCannotCreateFile(command_history, error) def writeCommandResultToFile(self, result_list): """命令輸出結果寫入到文件""" data,error = self.fileAppendObject(command_result_history) if data: data.write(printhelp_obj.returnDateString()) data.write(''.join(result_list)) data.write('\n') data.close() else: printhelp_obj.printCannotCreateFile(command_result_history,error) # 鏈接SSH和SFTP類的父類 class Connect(object): def __init__(self): self.connect_pool_dict = {} self.ip_list = [] self.thread_num = None def getIPList(self): """獲取SSH/SFTP鏈接成功的IP地址""" return self.ip_list def getThreadNum(self): """獲取到線程數量,是經過計算IP地址數量獲得""" return self.thread_num def getConnectionPool(self): """獲取SFTP/SSH鏈接池信息""" return self.connect_pool_dict # 鏈接SSH類 class ConnectSSH(Connect): def connectSSH(self): """鏈接到SSH服務""" paramiko.util.log_to_file(EasyXMS_log) server_dict = readwriteconfigFile_obj.readIPAsDict() # 首先得到ServerIP地址列表 if server_dict: # 這一段就是判斷,在ssh的connect_pool_dict這裏面現存的IP地址列表跟從配置文件讀取出來的列表是否一致 # 若是不一致,那麼以配置文件讀取出來的IP地址列表爲準,刪除多餘的鏈接 if self.connect_pool_dict: connect_pool_list = self.connect_pool_dict.keys() for i in connect_pool_list: if i not in server_dict: del self.connect_pool_dict[i] self.ip_list.remove(i) # 這一段是增長 成功進行SSH鏈接的IP地址到self.connect_pool_dict中去,同時也增長到成功IP地址列表去 for ip,value in server_dict.items(): if ip not in self.connect_pool_dict: conn = paramiko.SSHClient() conn.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: conn.connect(ip,value['port'],value['user'],value['pwd'], timeout=0.8) self.connect_pool_dict[ip] = conn self.ip_list.append(ip) except: printhelp_obj.printCanNotConnectIP(ip) self.thread_num = len(self.ip_list) else: printhelp_obj.printFileNotExistOrEmpty() def setCommand(self,command): """設定命令行輸入的命令""" self.command = command def getCommand(self): """返回命令""" return self.command # 鏈接到SFTP類 class ConnectSFTP(Connect): def connectSFTP(self): """鏈接到SFTP服務""" paramiko.util.log_to_file(EasyXMS_log) server_dict = readwriteconfigFile_obj.readIPAsDict() # 首先得到ServerIP地址列表 if server_dict: # 這一段就是判斷,在sftp的connect_pool_dict這裏面現存的IP地址列表跟從配置文件讀取出來的列表是否一致 # 若是不一致,那麼以配置文件讀取出來的IP地址列表爲準,刪除多餘的鏈接 if self.connect_pool_dict: connect_pool_list = self.connect_pool_dict.keys() for i in connect_pool_list: if i not in server_dict: del self.connect_pool_dict[i] self.ip_list.remove(i) # 這一段是增長 成功進行SFTP鏈接的IP地址到self.connect_pool_dict中去,同時也增長到成功IP地址列表去 for ip,value in server_dict.items(): if ip not in self.connect_pool_dict: try: conn = paramiko.Transport((ip, value['port'])) conn.connect(username=value['user'], password=value['pwd']) sftp = paramiko.SFTPClient.from_transport(conn) self.connect_pool_dict[ip] = sftp self.ip_list.append(ip) except: printhelp_obj.printCanNotConnectIP(ip) self.thread_num = len(self.ip_list) else: printhelp_obj.printFileNotExistOrEmpty() def getDirectoryName(self, filename, dest): """拼接目標文件名""" if dest.endswith('/'): dest = '%s/%s' % (dest.rstrip('/'), filename) else: dest = '%s/%s' % (dest, filename) return dest def setFilePath(self,filepath): """設定當前命令行輸入的文件路徑""" self.filepath = filepath def getFilePath(self): """返回命令行輸入的文件路徑""" return self.filepath # 多線程類 class MutilThreadControl(threading.Thread): def setConnectionPool(self, pool): """設置使用那個鏈接池 有SSH和SFTP鏈接池""" self.init_pool = pool def initIPQueueAndtConnectionPool(self): """初始一個隊列並把IP地址放入隊列,返回IP地址列表的長度(指定產生線程的數量)和隊列""" if self.init_pool == 'ssh': ip_list = connectSSH_obj.getIPList() connect_pool_dict = connectSSH_obj.getConnectionPool() elif self.init_pool == 'sftp': ip_list = connectSFTP_obj.getIPList() connect_pool_dict = connectSFTP_obj.getConnectionPool() thread_num = len(ip_list) ip_queue = Queue(thread_num) for ip in ip_list: ip_queue.put(ip) return ip_queue, thread_num, connect_pool_dict def run(self): """開始多線程執行命令和SFTP上傳文件""" ip_queue, threads_num, connect_pool_dict = self.initIPQueueAndtConnectionPool() result_list = [] if self.init_pool == 'ssh': cmd = connectSSH_obj.getCommand() for i in xrange(threads_num): ip = ip_queue.get() stdin,stdout,stderr = connect_pool_dict[ip].exec_command(cmd) result = printhelp_obj.printCommandResult(ip, cmd, stdout.read(), stderr.read()) result_list.append(result) readwriteconfigFile_obj.writeCommandResultToFile(result_list) elif self.init_pool == 'sftp': file_path = connectSFTP_obj.getFilePath().split() if len(file_path) == 2: src,dest = file_path if exists(src) and isfile(src): filename = basename(src) dest = connectSFTP_obj.getDirectoryName(filename, dest) for i in xrange(threads_num): ip = ip_queue.get() try: connect_pool_dict[ip].put(src, dest) printhelp_obj.uploadFileOK(src, ip) except IOError, e: printhelp_obj.uploadFileError(src, ip, e) else: printhelp_obj.invaildFilePath(src) else: printhelp_obj.printArgsNotenough() # 執行指定的動做(執行命令 上傳文件 清屏) class StartExecAction(object): def startExecCommand(self): """開始多線程執行命令""" mutilThreadControl_ssh_obj = MutilThreadControl() mutilThreadControl_ssh_obj.setConnectionPool('ssh') if readwriteconfigFile_obj.readIPAsDict(): print cmd = raw_input(printhelp_obj.enterCommand()) print if cmd: if cmd == 'q' or cmd == 'Q': pass else: readwriteconfigFile_obj.writeCommandToFile(cmd) connectSSH_obj.setCommand(cmd) if not connectSSH_obj.getConnectionPool(): printhelp_obj.printSSHBePatient() connectSSH_obj.connectSSH() mutilThreadControl_ssh_obj.start() mutilThreadControl_ssh_obj.join() self.startExecCommand() else: printhelp_obj.invaildCommand() else: printhelp_obj.printFileNotExistOrEmpty() def startSFTP(self): """開始上傳文件""" mutilThreadControl_sftp_obj = MutilThreadControl() mutilThreadControl_sftp_obj.setConnectionPool('sftp') if readwriteconfigFile_obj.readIPAsDict(): printhelp_obj.printFilePath() print filepath = raw_input(printhelp_obj.enterFilePath()) print if filepath: connectSFTP_obj.setFilePath(filepath) if not connectSFTP_obj.getConnectionPool(): printhelp_obj.printSFTPBePatient() connectSFTP_obj.connectSFTP() mutilThreadControl_sftp_obj.start() mutilThreadControl_sftp_obj.join() else: printhelp_obj.printArgsNotenough() else: printhelp_obj.printFileNotExistOrEmpty() def clearScreen(self): """清屏""" clear_screen = {'win32':'cls','linux2':'clear', 'linux':'clear','darwin':'clear'}[platform] system(clear_screen) if __name__ == '__main__': configfile = 'server.conf' EasyXMS_log = 'paramiko.log' command_history = 'command_history.log' command_result_history = 'command_result_history.log' printhelp_obj = PrintHelp() printhelp_obj.printHead() readwriteconfigFile_obj = ReadWriteConfigFile() connectSSH_obj = ConnectSSH() connectSFTP_obj = ConnectSFTP() startExecAction_obj = StartExecAction() input_obj = InputValue() input_obj.loopGetValue()