標籤:powershell python 遠端管理 linux osx
問題
今天同事的AD帳號頻繁被鎖,在DC的日誌上顯示源於無線網ISE的驗證失敗,ISE伺服器的日誌顯示某個IP的裝置的AD密碼錯誤。
我把無線AP所在的範圍,這個裝置的Mac地址給同事,問題來了,他轉了半個小時,愣是找不到對應的裝置,也沒有人報告連網不通。這個就很尷尬了~~
思路
怎麼找到對應的裝置呢,因為無線網AD驗證沒通過,DHCP伺服器沒有任何記錄。很多人同時開啟WiFi和有線網,也許我可以通過有線網去找這個無線網卡的地址。
豆子想了一個笨辦法,首先通過Mac地址可以判斷出這個是蘋果的網卡,那麼我遠程連到所有的蘋果系統上查詢對應的網卡應該有可能擷取到這個地址。
想到了就做做吧。
純屬練手,我用Python和Powershell都試了試。
Python 指令碼
Python我曾經寫過一個模仿fabric的程式,可以遠端對多台Linux或者OSX機器執行遠程操作,上傳和下載。基本上就是調用threading, paramiko,queue幾個模組。
#!/usr/bin/env python# -*- coding:utf-8 -*-# Author Yuan Li""" 本程式類比Fabric,遠端批量進行SSH串連,可以執行下載,上傳和shell命令執行。 遠程命令的執行,使用了線程池的技術,因為執行的時間比較少,而線程本身執行的時間占的比重比較大; 對於下載和上傳,因為本身就是比較消耗時間的操作,因此每個串連單獨使用了線程建立和銷毀,因為時間比較久,線程的時間可以忽略了"""import threadingimport queueimport timeimport paramikoimport os#找到相對路徑parent_path = os.path.abspath(os.pardir)db_path=os.path.join(parent_path,‘db‘)#一個管理類,基本思路是把任務和相關的參數填充到隊列(任務池)中,然後建立一個進程池,裡面的進程迴圈地讀取任務池裡面的內容,任何執行其中的內容,直到所有任務全部實現。class workmanager(object): #建構函式 def __init__(self,cmd,username,password,work_num=1000,thread_num=2,): """ :param cmd:遠程命令 :param username: 使用者名稱 :param password: 密碼 :param work_num: 任務池(隊列大小) :param thread_num: 線程池大小 """ self.cmd=cmd self.work_num=work_num self.thread_num=thread_num self.queue=queue.Queue() self.threads=[] self.init_task(work_num,cmd,username,password) self.init_threadpool(thread_num) #初始化任務池 def init_task(self,num,inp,username,password): for i in range(num): self.add_job(do_job,i,inp,username,password) #新增工作到任務池 def add_job(self,job,*args): #填充任務到任務池,每一個任務是一個元祖(任務,參數列表) self.queue.put((job,list(args))) #初始化線程池 def init_threadpool(self,num): for i in range(num): self.threads.append(work(self.queue)) #等待掛起主線程 def wait_allcomplete(self): for item in self.threads: if item.isAlive(): item.join()#線程類,每個線程迴圈地去任務池取任務class work(threading.Thread): def __init__(self,que): super(work, self).__init__() self.queue=que self.start() def run(self): while True: try: #當任務池為空白的時候,強制報錯,退出 do,args=self.queue.get(block=False) # print(do,args) do(args[0],args[1],args[2],args[3]) #確保隊列裡面的任務都完成了 self.queue.task_done() except: break#初始化的一個主機群組,測試用的hosts=[‘anoble-ise‘,‘bberry-ise‘,‘blackbr-ise‘,‘jlau-ise‘,‘kwood-ise‘,‘marwa-ise‘,‘smaroo-ise‘,‘psekarwin-ise‘,‘spare2-ise‘]#遠端連線SSH並且執行命令def do_job(args,inp,username,password): """ :param args: hosts列表的索引 :param inp: 遠程命令 :param username: 使用者名稱 :param password: 密碼 :return: """ # time.sleep(0.1) ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hosts[args], 22, username, password) # 執行命令測試 stdin, stdout, stderr = ssh.exec_command(inp) for line in stdout.readlines(): print(line.strip()) print(("\x1b[5;19;32m %s \x1b[0m" % hosts[args]).center(40,‘*‘)) print("\n")#下載測試def download(args,user,pwd,remote,local): """ :param args: hosts列表的索引 :param inp: 遠程命令 :param username: 使用者名稱 :param password: 密碼 :return: """ try: # print(hosts[args]) t = paramiko.Transport((hosts[args],22)) t.connect(username=user, password=pwd) sftp = paramiko.SFTPClient.from_transport(t) # remotepath=‘/tmp/test2‘ if not os.path.isdir(local): os.makedirs(local) remotepath=remote localpath=os.path.join(local,hosts[args]) sftp.get(remotepath, localpath) print("下載檔案從%s成功" % hosts[args]) except Exception as ex: print("下載檔案從%s失敗"%hosts[args])# 上傳測試def upload(args,user,pwd,remote,local): try: # print(hosts[args]) t = paramiko.Transport((hosts[args], 22)) t.connect(username=user, password=pwd) sftp = paramiko.SFTPClient.from_transport(t) # remotepath=‘/tmp/test2‘ remotepath=remote localpath=local # localpath=‘c:/temp/aaa.txt‘ sftp.put(localpath, remotepath) print(‘上傳檔案到%s成功‘ % hosts[args]) t.close() except Exception as ex: print(‘上傳檔案到%s失敗‘%hosts[args])#選擇主機群組def hostinfo(): global hosts print("可供選擇的主機群組包括:") from os import listdir from os.path import isfile, join # mypath=os.getcwd() onlyfiles = [f for f in listdir(db_path) if isfile(join(db_path, f))] print(onlyfiles) for file in onlyfiles: file_path=os.path.join(db_path,file) with open(file_path,‘r‘) as fp: print(("\x1b[5;19;32m %s 主機列表 \x1b[0m" %file).center(40, ‘*‘)) for line in fp: print(line.strip()) name=input("請選擇你要操作的主機群組名稱(hostgroup1,hostgroup2,hostgroup3..)") if name in onlyfiles: hosts=[] file_path=os.path.join(db_path,name) with open(file_path,‘r‘) as fp: for line in fp: hosts.append(line.strip()) else: print("該主機群組不存在")username=""password=""#入口檔案def display(): global hosts,username,password msg=""" 歡迎使用Fabric類比程式,您可以執行以下操作 1.顯示主機群組 2.批量執行遠程命令 3.批量上傳 4.批量下載 5.輸入管理員帳號 6.退出 """ msg2 = """ 1.選擇主機群組 2.列出當前主機列表 3.返回上一級目錄 """ while True: print(msg) inpt=input("請輸入選項") #輸出主機群組的相關資訊 if inpt==‘1‘: while True: print(msg2) opt=input("請輸入選項") if opt==‘1‘: hostinfo() elif opt==‘2‘: for item in hosts: print(item) elif opt==‘3‘:break else:print("非法輸入") #遠程大量操作 elif inpt==‘2‘: # username=input("使用者名稱") # password=input("密碼") if not username: print("請先配置登入帳號資訊") else: while True: inp = input("輸入指令(q返回上級目錄)\n>>>") if inp ==‘q‘:break if not inp: print("不能輸入空命令") else: start = time.time() #指定命令,使用者名稱,密碼,任務池(隊列)的大小,和線程的個數) work_manager = workmanager(inp,username,password, len(hosts), 20) work_manager.wait_allcomplete() end = time.time() print("Cost time is %s" % (end - start)) #建立批量上傳的多線程 elif inpt==‘3‘: if not username: print("請先配置登入帳號資訊") else: remote_path=input("遠程路徑") local_path=input("當前路徑") threads = [] for item in range(len(hosts)): t = threading.Thread(target=upload, args=(item,username,password,remote_path,local_path)) t.start() threads.append(t) for t in threads: t.join() #建立批量下載的多線程 elif inpt==‘4‘: if not username: print("請先配置登入帳號資訊") else: remote_path = input("遠程檔案路徑") local_path = input("當前檔案夾路徑") threads=[] for item in range(len(hosts)): t = threading.Thread(target=download, args=(item,username,password,remote_path,local_path)) t.start() threads.append(t) for t in threads: t.join() elif inpt==‘5‘: username = input("使用者名稱") password = input("密碼") elif inpt==‘6‘: exit("退出程式") else: print("無效輸入,請重試")if __name__ == ‘__main__‘: display()
跑起來介面大概是這樣的
PowerShell 指令碼
然後純屬無聊,我用Powershell也簡單的實現了一次。Powershell主要是用第三方模組 posh-ssh進行串連。這個模組本身沒有提供多線程的功能,這裡偷懶,豆子也沒用runspace(多線程),也沒寫異常處理,就想看看是否工作。因此他串連session的時候不是並行作業,而是挨個執行,花了可能10分鐘才和幾百個蘋果用戶端建立了ssh session,比起上面的Python指令碼慢很多。
#先找到所有的蘋果系統,排除ping不通的$sydmac=Get-ADComputer -Filter {operatingsystem -like ‘*MAC*‘} -Properties ipv4address | Where-Object {$_.ipv4address -notlike ‘‘}$sydmac| select -expand dnshostname |Invoke-Parallel -ScriptBlock {Test-Connection -ComputerName "$_" -Count 1 -ErrorAction SilentlyContinue -ErrorVariable err | select Ipv4address, @{n=‘DNS‘;e={[System.Net.Dns]::gethostentry($_.ipv4address).hostname}}} -Throttle 20 | select -ExpandProperty dns| out-file c:\temp\mac.txt$list=gc C:\temp\mac.txtImport-Module posh-ssh#密碼帳號 $username = "administrator"$secureStringPwd = ConvertTo-SecureString -AsPlainText "111222333" -Force$creds = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd#這裡迴圈寫的很low 很慢,和每個用戶端分別建立session,稍後有空改成隊列+runspace的多線程試試foreach($line in $list){New-SSHSession -ComputerName $line -Credential ( get-credential $creds ) -AcceptKey}$sessions=Get-SSHSessionfor($i=0;$i -lt $sessions.Length;$i++){Invoke-SSHCommand -Command ‘ifconfig | grep ether‘ -Sessionid $i -ErrorAction SilentlyContinue}``
上面的指令碼都能工作,不過代碼品質比較挫,之後有時間再慢慢最佳化。
Windows 下指令碼遠端管理數百台蘋果系統