Предыстория:
Сам я работаю в техотделе одной брокерской компании в Торонто, Канаде. Так же у нас есть еще один офис в Калгари. Как-то после планового установления Windows обновлений на единственном доменном контроллере в удаленном офисе не запустился W32Time сервис, который отвечает за синхронизацию времени с внешним источником. Таким образом в течение около недели время на сервере сбилось приблизительно на 20 секунд. Наши рабочие станции на тот момент времени по умолчанию получали время с контроллера. Сами понимаете, что случилось. В торгах время очень важно, разница в секунды может решить многое. Первыми расхождение во времени, к сожалению, заметили наши брокеры. Наш отдел техподдержки, состоящий по сути из 3 человек за это распекли. Надо было срочно что-то делать. Решением было применение групповой политики, которая отсылала все машины к внутреннему NTP серверу, работающему на CentOS. Еще были проблемы с DC Barracuda Agent, сервисом, отвечающим за соединение контроллеров домена с нашим Веб фильтром, и еще парочка сервисов причиняла нам порой беспокойство. Тем не менее решили что-то придумать, чтобы следить за пару сервисами. Я немного погуглил и понял, что есть много решений, в основном коммерчиских для данной проблемы, но так как я хотел научиться какому-нибудь скриптовому языку, то вызвался написать скрипт на Питоне с помощью нашего местного линукс-гуру. В последствие это переросло в скрипт, который проверяет все сервисы, сравнивая их наличие и состояние со списком желаемых сервисов, которые к сожалению надо делать вручную отдельно для каждой машины.
Решение:
На одном из Windows серверов я создал PowerShell скрипт такого вида:
echo "Servername" > C:\Software\Services\Servername.txt get-date >> C:\Software\Services\Servername.txt Get-Service -ComputerName Servername | Format-Table -Property status, name >> C:\Software\Services\Servername.txt
В моем случае таких кусков получилось 10 для каждого сервера
В Task Scheduler добавил следующий батник (мне это показлось легче, чем пытаться запусить оттуда PowerShell скрипт напрямую):
powershell.exe C:\Software\Services\cal01script.ps1
Теперь каждый день я получал список со всеми сервисами в отдельном файле для каждого сервера в подобном формате:
Servername Friday, October 26, 2012 1:24:03 PM Status Name ------ ---- Stopped Acronis VSS Provider Running AcronisAgent Running AcronisFS Running AcronisPXE Running AcrSch2Svc Running ADWS Running AeLookupSvc Stopped ALG Stopped AppIDSvc Running Appinfo Running AppMgmt Stopped aspnet_state Stopped AudioEndpointBuilder Stopped AudioSrv Running Barracuda DC Agent Running BFE Stopped BITS Stopped Browser Running CertPropSvc Running WinRM Stopped wmiApSrv Stopped WPDBusEnum Running wuauserv Stopped wudfsvc
Теперь самая главная часть. На отдельной машине с CentOS на борту я написал сей скрипт:
import sys import smtplib import string from sys import argv import os, time import optparse import glob # function message that defines the email we get about the status def message(subjectMessage,msg): SUBJECT = subjectMessage FROM = "address@domain.com" TO = 'address@domain.com' BODY = string.join(( "From: %s" % FROM, "To: %s" % TO, "Subject: %s" % SUBJECT , "", msg ), "\r\n") s = smtplib.SMTP('mail.domain.com') #s.set_debuglevel(True) s.sendmail(FROM, TO, BODY) s.quit() sys.exit(0) def processing(runningServicesFileName,desiredServicesFileName): try: desiredServicesFile=open(desiredServicesFileName,'r') except (IOError,NameError,TypeError): print "The list with the desired state of services either does not exist or the name has been typed incorrectly. Please check it again." sys.exit(0) try: runningServicesFile=open(runningServicesFileName,'r') except (IOError,NameError,TypeError): print "The dump with services either does not exist or the name has been typed incorrectly. Please check it again." sys.exit(0) #Defining variables readtxt = desiredServicesFile.readlines() desiredServices = [] nLines = 0 nRunning = 0 nDesiredServices = len(readtxt) faultyServices = [] missingServices = [] currentServices = [] serverName = '' dumpdate='' errorCount=0 # Trimming file in order to get a list of desired services. Just readlines did not work putting \n in the end of each line for line in readtxt: line = line.rstrip() desiredServices.append(line) # Finding the number of currently running services and those that failed to start for line in runningServicesFile: nLines+=1 # 1 is the line where I append the name of each server if nLines==1: serverName = line.rstrip() # 3 is the line in the dump that contains date if nLines==3: dumpdate=line.rstrip() # 7 is the first line that contains valueable date. It is just the way we get these dumps from Microsoft servers. if nLines<7: continue # The last line in these dumps seems to have a blank character that we have to ignore while iterating. if len(line)<3: break line = line.rstrip(); serviceStatusPair = line.split(None,1) currentServices.append(serviceStatusPair[1]) if serviceStatusPair[1] in desiredServices and serviceStatusPair[0] == 'Running': nRunning+=1 if serviceStatusPair[1] in desiredServices and serviceStatusPair[0] != 'Running': faultyServices.append(serviceStatusPair[1]) if nLines==0: statusText='Dumps are empty on %s' % (serverName) detailsText='Dumps are empty' # Checking if there are any missing services for i in range(nDesiredServices): if desiredServices[i] not in currentServices: missingServices.append(desiredServices[i]) # Sending the email with results if nRunning == nDesiredServices: statusText='%s: OK' % (serverName) detailsText='%s: OK\nEverything works correctly\nLast dump of running services was taken at:\n%s\nThe list of desired services:\n%s\n' % (serverName,dumpdate,'\n'.join(desiredServices)) else: statusText='%s: Errors' % (serverName) detailsText='%s: Errors\n%s out of %s services are running.\nServices failed to start:%s\nMissing services:%s\nLast dump of the running services was taken at:\n%s\n' % (serverName,nRunning,nDesiredServices,faultyServices,missingServices,dumpdate) errorCount=errorCount+1 return (statusText,detailsText,errorCount) # Defining switches that can be passed to the script usage = "type -h or --help for help" parser = optparse.OptionParser(usage,add_help_option=False) parser.add_option("-h","--help",action="store_true", dest="help",default=False, help="this is help") parser.add_option("-d","--desired",action="store", dest="desiredServicesFileName", help="list of desired services") parser.add_option("-r","--running",action="store", dest="runningServicesFileName", help="dump of currently running services") parser.add_option("-c","--config",action="store", dest="configServicesDirectoryName", help="directory with desired services lists") (opts, args) = parser.parse_args() # Outputting a help message and exiting in case -h switch was passed if opts.help: print """ This script checks all services on selected Windows machines and sends out a report. checkServices.py [argument 1] [/argument 2] [/argument 3] Arguments: Description: -c, --config - specifies the location of the directory with desired list of services and finds dumps automatically -d, --desired - specifies the location of the file with the desired list of services. -r, --running - specifies the location of the file with a dump of running services. """ sys.exit(0) statusMessage = [] detailsMessage = [] body = [] errorCheck=0 directory='%s/*' % opts.configServicesDirectoryName if opts.configServicesDirectoryName: check=glob.glob(directory) check.sort() if len(check)==0: message('Server status check:Error','The directory has not been found. Please check its location and spelling.') sys.exit(0) for i in check: desiredServicesFileName=i runningServicesFileName=i.replace('desiredServices', 'runningServices') #print runningServicesFileName status,details,errors=processing(runningServicesFileName,desiredServicesFileName) errorCheck=errorCheck+errors statusMessage.append(status) detailsMessage.append(details) body='%s\n\n%s' % ('\n'.join(statusMessage),'\n'.join(detailsMessage)) if errorCheck==0: message('Server status check:OK',body) else: message('Server status check:Errors',body) if opts.desiredServicesFileName or opts.desiredServicesFileName: status,details,errors=processing(opts.runningServicesFileName,opts.desiredServicesFileName) message(status,details)
Файлы дампов и списков с желаемыми сервисами должны иметь одинаковые имена. Список с сервисами, за которыми мы следим (desiredServices) должен быть вот такого вида:
Acronis VSS Provider AcronisAgent AcronisFS AcrSch2Svc
Скрипт будет проверять сервисы, а потом компоновать все это в одно email сообщение, которое в зависимости от результата будет говорить, что все в порядке в теме сообщения или, что есть ошибки, а в теле сообщения раскрывать, какие это ошибки. Для нас одной проверки в день достаточно, поэтому ранним утром мы получаем уведомление о состоянии наших Windows серверов. Чтобы скопировать файлы с Windows сервера на машину с линуксом, мой коллега помог мне со следующим баш скриптом:
#!/bin/bash mkdir runningServices smbclient --user="user%password" "//ServerName.domain.com/software" -c "lcd runningServices; prompt; cd services; mget *.txt" cd runningServices for X in `ls *.txt`; do iconv -f utf16 -t ascii $X > $X.asc mv $X.asc $X done
Этот скрипт так же меняет кодировку, ибо на моей машине Linux не очень хотел работать с UTF16. Далее, чтобы отчищать папку от дампов с сервисами я добавил батник в Task Scheduler чтобы запускать PowerShell скрипт, который стирает дампы.
Батник:
powershell.exe C:\Software\Services\delete.ps1
Poweshell скрипт:
remove-item C:\Software\Services\ServerName.txt
Проект преследовал собой 2 цели — мониторинг сервисов и обучение Питону. Это мой первый пост на Хабре, поэтому я уже ожидаю наплыв критики в свой адрес. Если у Вас есть какие-либо замечания, особенно по улучшению данной системы, то милости прошу, поделитесь. Надеюсь, что это статья покажется кому-нибудь нужной, потому что подобного решения бесплатного и с уведомлением по email я не нашел. Может, что плохо искал.
ссылка на оригинал статьи http://habrahabr.ru/post/156597/
Добавить комментарий