Мониторинг доступности хостов на Powershell

от автора

Всем доброго времени суток, хочу поделиться простой инструкцией «Как можно перестать вручную пинговать десяток хостов. Без регистрации и СМС!».

С просторов Интернета

Важно: я не программист и не системный администратор, но мне приходиться работать одновременно с большим набором серверов и сервисов и данный скрипт я написал для собственного удобства.

Всех кому это может быть полезно любопытно, прошу под кат.


Почему Powershell

У меня есть опыт написания простых программ на Python, но он требует либо установленного интерпретатора, либо права на запуск .exe, что, в рамках моей работы не всегда возможно. А вот скрипт на Powershell чаще находит возможность запустить.

Не все любят консоль/терминал

Думаю в этом нет противоречия, в противном случае все бы до сих пользовались консольными почтовыми клиентами и в *nix-системах не появился GUI.

Поскольку в повседневности мне приходится использовать Powershell достаточно примитивно, то ля начала я попытался понять, а можно ли к нему прикрутить GUI. Оказывается можно, да еще и достаточно просто:

Add-Type -assembly System.Windows.Forms #Подключим библиотеку $main_form = New-Object System.Windows.Forms.Form #Создаем объект с нашим "окном"  #Задаем для него параметры $main_form.Text ='Links up' #Имя в заголовке $main_form.Width = 300 #Ширина окна $main_form.Height = 200 #Высота окна $main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости $main_form.ShowDialog() #Вызываем "окно" 

Добавляем к этому элемент вывода и получаем элементарное Windwows-окно тавтология?.

Add-Type -assembly System.Windows.Forms #Подключим библиотеку  $main_form = New-Object System.Windows.Forms.Form #Создаем переменную с нашим "окном"  #Задаем для него параметры через параметры $main_form.Text ='Links up' #Имя в заголовке $main_form.Width = 300 #Ширина окна $main_form.Height = 200 #Высота окна $main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости  $Label = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell $Label.Text = "Привет! Я элементарное Windows-окно, а чего добился ты?" #Выводимый текст $Label.Location  = New-Object System.Drawing.Point(10,65) #Расположение объекта внутри формы по горизонтали и вертикали (x,y) $Label.AutoSize = $true   $main_form.Controls.Add($Label) #добавляем созданный объект к форме, чтобы он не потерялся при выполнении скрипта  $main_form.ShowDialog() #Вызываем оскорбительно элементарное окно

Добавим динамики

Так инструмент которым я «пинговал Яндекс» и монтировал *.iso обзавёлся обособленным GUI, но этого мало. Подобным образом можно вывести информацию о доступности, но для мониторинга придется каждый раз перезапускать скрипт.

Для начала нарисуем две иконки и добавим возможность выводить изображения.

$PictureBox = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично $PictureBox.width = 10 $PictureBox.height = 10 $PictureBox.location = New-Object System.Drawing.Point(178,12)  #Вот тут мы переходим к делу привязываем выводимую картинку к результату проверки доступности узла используя командлет #<b>test-connection</b>, где: #-Count - передает кол-во запросов #-computer - IP или доменное имя для запроса #-quiet - для того чтобы нам вернулось только Boolean значение if ((test-connection -Count 1 -computer ya.ru -quiet) -eq $True) { 	$PictureBox.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке  } Else { 	$PictureBox.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке  } $PictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom  $main_form.Controls.Add($PictureBox) 

Опять наши DNS-ники шалят…

И еще добавим кнопку, чтобы была возможность обновлять показания.

$Button = New-Object System.Windows.Forms.Button $Button.Location = New-Object System.Drawing.Size(100,150)  $Button.Size = New-Object System.Drawing.Size(80,30) $Button.Text = "Reload"  # Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике $Button.Add_Click({  	if ((Test-Connection -Count 1 -computer ya.ru -quiet) -eq $True) 		{$PictureBox.imageLocation	= "C:\Test\yes.png"} 	Else {$PictureBox.imageLocation	= "C:\Test\no.png"} 	   	if ((Test-Connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True) 		{$PictureBox1.imageLocation	= "C:\Test\yes.png"} 	Else {$PictureBox1.imageLocation	= "C:\Test\no.png"}  	})  $main_form.Controls.Add($Button) 

Чудесно, теперь в продакшн!?

Вручную прописав около полудюжины хостов я вдруг понял одну вещь — а ведь этот проект закончиться, хосты поменяют и мне придется каждый раз править скрипт. С одной стороны ничего страшного, это же решение для себя любимого. С другой стороны это решение для себя любимого вы взгляните на то что получается:

Зрелище не для слабонервных

Add-Type -assembly System.Windows.Forms #Подключим библиотеку  $main_form = New-Object System.Windows.Forms.Form #Создаем переменную с нашим "окном"  #Задаем для него параметры через параметры $main_form.Text ='Links up' #Имя в заголовке $main_form.Width = 300 #Ширина окна $main_form.Height = 200 #Высота окна $main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости  $Label = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell $Label.Text = "ya.ru		............................" #Выводимый текст  $Label.Location  = New-Object System.Drawing.Point(15,10) #Расположение объекта внутри формы по горизонтали и вертикали (x,y) $Label.AutoSize = $true   $Label1 = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell $Label1.Text = "8.8.8.8		............................" #Выводимый текст googl-овский DNS-ник $Label1.Location  = New-Object System.Drawing.Point(15,30) #Расположение объекта внутри формы по горизонтали и вертикали (x,y) $Label1.AutoSize = $true   $Label2 = New-Object System.Windows.Forms.Label $Label2.Text = "192.168.x.x	............................" $Label2.Location  = New-Object System.Drawing.Point(15,50) $Label2.AutoSize = $true  $Label3 = New-Object System.Windows.Forms.Label $Label3.Text = "192.168.x.x	............................" $Label3.Location  = New-Object System.Drawing.Point(15,70) $Label3.AutoSize = $true  $Label4 = New-Object System.Windows.Forms.Label $Label4.Text = "10.0.x.x	 ............................" $Label4.Location  = New-Object System.Drawing.Point(15,90) $Label4.AutoSize = $true  $Label5 = New-Object System.Windows.Forms.Label $Label5.Text = "162.102.x.x	 ............................" $Label5.Location  = New-Object System.Drawing.Point(15,110) $Label5.AutoSize = $true  $PictureBox = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично $PictureBox.width = 10 $PictureBox.height = 10  #Вот тут мы переходим к делу привязываем выводимую картинку к результату проверки доступности узла используя командлет #<b>test-connection</b>, где: #-Count - передает кол-во запросов #-computer - IP или доменное имя для запроса #-quiet - для того чтобы нам вернулось только Boolean значение $PictureBox.location = New-Object System.Drawing.Point(235,12) if ((test-connection -Count 1 -computer ya.ru -quiet) -eq $True) { 	$PictureBox.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке  } Else { 	$PictureBox.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке  } $PictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom  $PictureBox1 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично $PictureBox1.width = 10 $PictureBox1.height = 10 $PictureBox1.location = New-Object System.Drawing.Point(235,32) if ((test-connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True) { 	$PictureBox1.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке  } Else { 	$PictureBox1.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке  } $PictureBox1.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom  $PictureBox2 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично $PictureBox2.width = 10 $PictureBox2.height = 10 $PictureBox2.location = New-Object System.Drawing.Point(235,52) if ((test-connection -Count 1 -computer 192.168.x.x -quiet) -eq $True) { 	$PictureBox2.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке  } Else { 	$PictureBox2.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке  } $PictureBox2.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom  $PictureBox3 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично $PictureBox3.width = 10 $PictureBox3.height = 10 $PictureBox3.location = New-Object System.Drawing.Point(235,72) if ((test-connection -Count 1 -computer 192.168.x.x -quiet) -eq $True) { 	$PictureBox3.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке  } Else { 	$PictureBox3.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке  } $PictureBox3.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom  $PictureBox4 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично $PictureBox4.width = 10 $PictureBox4.height = 10 $PictureBox4.location = New-Object System.Drawing.Point(235,92) if ((test-connection -Count 1 -computer 10.0.x.x -quiet) -eq $True) { 	$PictureBox4.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке  } Else { 	$PictureBox4.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке  } $PictureBox1.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom  $PictureBox5 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично $PictureBox5.width = 10 $PictureBox5.height = 10 $PictureBox5.location = New-Object System.Drawing.Point(235,112) if ((test-connection -Count 1 -computer 162.102.x.x -quiet) -eq $True) { 	$PictureBox5.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке  	} Else { 	$PictureBox5.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке  	} $PictureBox5.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom  $Button = New-Object System.Windows.Forms.Button $Button.Location = New-Object System.Drawing.Size(100,150)  $Button.Size = New-Object System.Drawing.Size(80,30) $Button.Text = "Reload"  # Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике $Button.Add_Click({  	if ((Test-Connection -Count 1 -computer ya.ru -quiet) -eq $True) 		{$PictureBox.imageLocation	= "C:\Test\yes.png"} 	Else {$PictureBox.imageLocation	= "C:\Test\no.png"} 	   	if ((Test-Connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True) 		{$PictureBox1.imageLocation	= "C:\Test\yes.png"} 	Else {$PictureBox1.imageLocation	= "C:\Test\no.png"}  	if ((Test-Connection -Count 1 -computer 192.168.x.x -quiet) -eq $True) 		{$PictureBox2.imageLocation	= "C:\Test\yes.png"} 	Else {$PictureBox2.imageLocation	= "C:\Test\no.png"}  	if ((Test-Connection -Count 1 -computer 192.168.x.x -quiet) -eq $True) 		{$PictureBox3.imageLocation	= "C:\Test\yes.png"} 	Else {$PictureBox3.imageLocation	= "C:\Test\no.png"}  	if ((Test-Connection -Count 1 -computer 10.0.x.x -quiet) -eq $True) 		{$PictureBox4.imageLocation	= "C:\Test\yes.png"} 	Else {$PictureBox4.imageLocation	= "C:\Test\no.png"}  	if ((Test-Connection -Count 1 -computer 162.102.x.x -quiet) -eq $True) 		{$PictureBox5.imageLocation	= "C:\Test\yes.png"} 	Else {$PictureBox5.imageLocation	= "C:\Test\no.png"} 	})  $main_form.Controls.Add($Label) #добавляем созданный объект к форме, чтобы он не потерялся при выполнении скрипта $main_form.Controls.Add($Label1) $main_form.Controls.Add($Label2) $main_form.Controls.Add($Label3) $main_form.Controls.Add($Label4) $main_form.Controls.Add($Label5) $main_form.Controls.Add($PictureBox) $main_form.Controls.Add($PictureBox1) $main_form.Controls.Add($PictureBox2) $main_form.Controls.Add($PictureBox3) $main_form.Controls.Add($PictureBox4) $main_form.Controls.Add($PictureBox5) $main_form.Controls.Add($Button)  $main_form.ShowDialog() #Вызываем оскорбительно элементарное окно 

Даже если соблюдать чистоту и придерживаться структуры то периодическое редактирование будет вызывать не самые приятные ощущения.

Для начала выносим проверку доступности в отдельную функцию, а путь к каталогу в отдельную переменную:

$script_path	= "C:\Test\PS ping"  #Переезжаем в финкцию уже созданный объект PictureBox и IP-адресс который нужно проверить. #На выходе оно меняет подгружаемую картинку. function get_status($PictureBox,$path_ip) { 	if ((Test-Connection -Count 1 -computer $path_ip -quiet) -eq $True) 		{$PictureBox.imageLocation	= $script_path + "\yes.png"} 	Else {$PictureBox.imageLocation	= $script_path + "\no.png"}  	} 

Вынесем данные о хосте во внешний файл path.txt, записывая в виде «ip/host-functional-name/», а в документе будем принимать их в массивы. Так же сделаем расчет положения по Y более автоматизированным. Все это дает нам возможность создавать Label`ы и checkbox`ы вызовом функции:

#создаем описательную линию, на вход идут: объект Label, IP-адресс, описание и позиция по оси Y Function Create_line($label,$path_ip,$caption, $top){  	$label.Location = New-Object System.Drawing.Point(1, $top) 	$label.text = $path_ip+$caption 	$label.font = $font 	$Label.AutoSize = $true 	} #создаем индикатор для описательной линию, на вход идут: объект PictureBox, IP-адресс и позиция по оси Y Function Create_link($PictureBox,$path_ip, $top){  	$PictureBox.width	= 10 	$PictureBox.height	= 10 	$PictureBox.location	= New-Object System.Drawing.Point(210,$top) 	get_status -PictureBox $PictureBox -path_ip $path_ip 	$PictureBox.SizeMode	= [System.Windows.Forms.PictureBoxSizeMode]::zoom 	}  $line = Get-content -LiteralPath $script_path"\path.txt" #вытаскиваем данные о необходимых хостах $len = $line.Length	#вычисляем длину, а точнее массив длин каждой строки  $i = 0  #далее объявляем массивы которые понадобятся дальше $ip = @()	#массив ip $capt = @()	#массив описания $Labels =@()	#массив Label $PictureBoxs =@()	#массив PictureBox  while($i -lt $len){ 	$f = $line[$i].IndexOf("/") #вычисляем позицию начала описания 	$l = $line[$i].LastIndexOf("/")	#вычисляем позицию конца описания 	$ip += $line[$i].Substring(0,$f)	#вытаскиваем ip 	$capt += $line[$i].Substring($f+1,$l-$f-1)	#вытаскиваем описание  	#создаем поле Label 	Create_line -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ip $ip[$i] -caption $capt[$i] -top $label_from_top 	#создаем поле PictureBox 	Create_link -PictureBox ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ip $ip[$i] -top $label_from_top  	$label_from_top += 15	#делаем "шаг" по оси Y  	$Labels	+= $label_obj	#вносим созданный Label в общий массив 	$PictureBoxs	+= $PictureBox_obj	#вносим созданный PictureBox в общий массив 	$main_form.Controls.Add($Labels[$i]) 	$main_form.Controls.Add($PictureBoxs[$i]) 	$i	+=1 	} 

Добавим место для вывода времени последней проверки, потому что это удобно, и немого дополним действие при нажатии на кнопку.

$Label0	= New-Object System.Windows.Forms.Label $Label0.Text	= Get-Date $Label0.Location	= New-Object System.Drawing.Point(80,180) $Label0.AutoSize	= $true $main_form.Controls.Add($Label0) $Button.Add_Click({  	while($i -lt $len){ 		get_status -PictureBox $PictureBoxs[$i] -path_ip $ip[$i] 		$i +=1 		} 	$Label0.Text	= Get-Date  	}) 

А так же я присоединил небольшую фитчуне баг, которая при обнаружении недоступного хоста сообщало не только изменением индикатора, но и вопило из динамиков.

Add-Type -AssemblyName System.Speech $voice	= New-Object System.Speech.Synthesis.SpeechSynthesizer $voice.Rate	= 5 $voice.Speak("Ворнинг! Ворнинг! Байз из андер аттак!") 

Так что после нескольких украшательств и приведения к читабельномуудобному мне виду, получилось вот так:

Полный код скрипта

Add-Type -AssemblyName System.Speech Add-Type -assembly System.Windows.Forms  $script_path	= "C:\PS ping" $label_from_top	= 10 $voice	= New-Object System.Speech.Synthesis.SpeechSynthesizer $voice.Rate	= 5  #Переезжаем в финкцию уже созданный объект PictureBox и IP-адресс который нужно проверить. #На выходе оно меняет подгружаемую картинку. function get_status($PictureBox,$path_ip) { 	if ((Test-Connection -Count 1 -computer $path_ip -quiet) -eq $True) { 		$PictureBox.imageLocation	= $script_path + "\yes.png" 		} 	Else { 		$PictureBox.imageLocation	= $script_path + "\no.png" 		$voice.Speak("Ошибка! Хост " + $path_ip + ", недоступен!") 		} 	}  #создаем описательную линию, на вход идут: объект Label, IP-адресс, описание и позиция по оси Y Function Create_line($label,$path_ip,$caption, $top){  	$label.Location	= New-Object System.Drawing.Point(1, $top) 	$label.text	= $path_ip+$caption 	$label.font	= $font 	$Label.AutoSize	= $true 	}  #создаем индикатор для описательной линию, на вход идут: объект PictureBox, IP-адресс и позиция по оси Y Function Create_link($PictureBox,$path_ip, $top){  	$PictureBox.width	= 10 	$PictureBox.height	= 10 	$PictureBox.location	= New-Object System.Drawing.Point(210,$top) 	get_status -PictureBox $PictureBox -path_ip $path_ip 	$PictureBox.SizeMode	= [System.Windows.Forms.PictureBoxSizeMode]::zoom 	}  $main_form	= New-Object System.Windows.Forms.Form $main_form.Text ='Links up' #Имя в заголовке $main_form.Width = 300 #Ширина окна $main_form.Height = 200 #Высота окна $main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости $main_form.TopMost	= $true #Это очень важная штука, она дает возможность закрепить окно поверх других  $line = Get-content -LiteralPath $script_path"\path.txt" #вытаскиваем данные о необходимых хостах $len = $line.Length	#вычисляем длину, а точнее массив длин каждой строки  $i = 0  #далее объявляем массивы которые понадобятся дальше $ip	= @()	#массив ip $capt	= @()	#массив описания $Labels	=@()	#массив Label $PictureBoxs	=@()	#массив PictureBox  while($i -lt $len){ 	$f	= $line[$i].IndexOf("/") #вычисляем позицию начала описания 	$l	= $line[$i].LastIndexOf("/")	#вычисляем позицию конца описания 	$ip	+= $line[$i].Substring(0,$f)	#вытаскиваем ip 	$capt	+= $line[$i].Substring($f+1,$l-$f-1)	#вытаскиваем описание  	#создаем поле Label 	Create_line -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ip $ip[$i] -caption $capt[$i] -top $label_from_top 	#создаем поле PictureBox 	Create_link -PictureBox ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ip $ip[$i] -top $label_from_top  	$label_from_top	+= 15	#делаем "шаг" по оси Y  	$Labels	+= $label_obj	#вносим созданный Label в общий массив 	$PictureBoxs	+= $PictureBox_obj	#вносим созданный PictureBox в общий массив 	$main_form.Controls.Add($Labels[$i]) 	$main_form.Controls.Add($PictureBoxs[$i]) 	$i	+=1 }  #это поле с последним временем проверки $Label0	= New-Object System.Windows.Forms.Label $Label0.Text	= Get-Date $Label0.Location	= New-Object System.Drawing.Point(80,180) $Label0.AutoSize	= $true   # Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике $Button	= New-Object System.Windows.Forms.Button $Button.Location	= New-Object System.Drawing.Size(100,200) $Button.Size	= New-Object System.Drawing.Size(80,30) $Button.Text	= "Reload"  $Button.Add_Click({  	while($i -lt $len){ 		get_status -PictureBox $PictureBoxs[$i] -path_ip $ip[$i] 		$i	+=1 		} 	$i	= 0 	$Label0.Text	= Get-Date  })  $main_form.Controls.Add($Button) $main_form.Controls.Add($Label0)  $main_form.ShowDialog() 

Послесловие

Надеюсь кому-то данный туториал будет полезен. Если же вы такое чуть-ли не с пеленок делали, то поздравляю, я — не делал. Но я готов выслушать ваши комментарии и предложения.

Для меня самого было приятно покопаться и собрать вот такую небольшую, но полезную в быту утилиту. В планах еще стоит реализовать добавления и удаления узлов из GUI, а так же сделать таки то что должно было быть реализованно в первую очередь автоматическое обновление статусов без необходимости «тыкать» кнопку и чтобы оно не вешало «powershell.exe».

Последняя версия скрипта на GitHub


ссылка на оригинал статьи https://habr.com/ru/post/463777/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *