R اور PowerShell استعمال کرنے والی ورچوئل مشینوں کی صحت سے متعلق روزانہ کی رپورٹس

R اور PowerShell استعمال کرنے والی ورچوئل مشینوں کی صحت سے متعلق روزانہ کی رپورٹس

داخلہ

صبح بخیر اب آدھے سال سے ہم ایک اسکرپٹ (یا اسکرپٹ کا ایک سیٹ) چلا رہے ہیں جو ورچوئل مشینوں (اور نہ صرف) کی حیثیت کے بارے میں رپورٹس تیار کرتا ہے۔ میں نے اپنے تخلیق کے تجربے اور خود کوڈ کا اشتراک کرنے کا فیصلہ کیا۔ مجھے تنقید کی امید ہے اور یہ مواد کسی کے لیے مفید ہو سکتا ہے۔

ضرورت کی تشکیل

ہمارے پاس بہت ساری ورچوئل مشینیں ہیں (تقریباً 1500 VMs 3 vCenters میں تقسیم کیے گئے ہیں)۔ نئے بنائے جاتے ہیں اور پرانے کو اکثر حذف کر دیا جاتا ہے۔ ترتیب کو برقرار رکھنے کے لیے، VMs کو سب سسٹمز میں تقسیم کرنے کے لیے vCenter میں متعدد حسب ضرورت فیلڈز شامل کیے گئے، یہ بتانے کے لیے کہ آیا وہ ٹیسٹ والے ہیں، اور کس کے ذریعے اور کب بنائے گئے تھے۔ انسانی عنصر نے اس حقیقت کو جنم دیا کہ آدھے سے زیادہ مشینیں خالی کھیتوں کے ساتھ رہ گئیں، جس نے کام کو پیچیدہ بنا دیا۔ ہر چھ ماہ میں ایک بار، کوئی نہ کوئی گھبرا گیا اور اس ڈیٹا کو اپ ڈیٹ کرنے پر کام شروع کر دیا، لیکن ڈیڑھ ہفتے کے بعد نتیجہ متعلقہ ہونا بند ہو گیا۔
میں فوراً واضح کر دوں کہ ہر کوئی یہ سمجھتا ہے کہ مشینوں کی تخلیق کے لیے درخواستیں، ان کی تخلیق کا عمل وغیرہ ہونا چاہیے۔ اور اسی طرح. اور ایک ہی وقت میں، ہر کوئی اس عمل کی سختی سے پیروی کرتا ہے اور سب کچھ ترتیب میں ہے۔ بدقسمتی سے، یہاں ایسا نہیں ہے، لیکن یہ مضمون کا موضوع نہیں ہے :)

عام طور پر، کھیتوں میں بھرنے کی درستگی کو خودکار جانچنے کا فیصلہ کیا گیا تھا۔
ہم نے فیصلہ کیا کہ غلط طریقے سے بھری ہوئی مشینوں کی فہرست کے ساتھ تمام ذمہ دار انجینئرز اور ان کے مالکان کو روزانہ ایک خط ایک اچھی شروعات ہوگی۔

اس موقع پر، میرے ایک ساتھی نے پہلے ہی پاور شیل میں ایک اسکرپٹ نافذ کر دیا تھا، جو ہر روز، ایک شیڈول کے مطابق، تمام vCenters کی تمام مشینوں پر معلومات اکٹھی کرتا تھا اور 3 csv دستاویزات (ہر ایک اپنے vCenter کے لیے) تیار کرتا تھا، جو کہ اس پر اپ لوڈ کیے جاتے تھے۔ ایک عام ڈسک. اس اسکرپٹ کو ایک بنیاد کے طور پر لینے اور R زبان کا استعمال کرتے ہوئے جانچ کے ساتھ اس کی تکمیل کرنے کا فیصلہ کیا گیا، جس کا ہمیں کچھ تجربہ تھا۔

حتمی شکل دینے کے عمل میں، حل نے بذریعہ ڈاک معلومات حاصل کیں، ایک ڈیٹا بیس جس میں ایک اہم اور تاریخی جدول ہے (اس کے بارے میں مزید بعد میں)، نیز vSphere لاگز کا تجزیہ تاکہ vm کے حقیقی تخلیق کاروں اور ان کی تخلیق کے وقت کو تلاش کیا جا سکے۔

IDE RStudio Desktop اور PowerShell ISE کو ترقی کے لیے استعمال کیا گیا۔

اسکرپٹ کو باقاعدہ ونڈوز ورچوئل مشین سے لانچ کیا گیا ہے۔

عمومی منطق کی تفصیل۔

اسکرپٹ کی عمومی منطق مندرجہ ذیل ہے۔

  • ہم پاور شیل اسکرپٹ کا استعمال کرتے ہوئے ورچوئل مشینوں پر ڈیٹا اکٹھا کرتے ہیں، جسے ہم R کے ذریعے کال کرتے ہیں، اور نتیجہ کو ایک csv میں جوڑ دیتے ہیں۔ زبانوں کے درمیان الٹا تعامل اسی طرح کیا جاتا ہے۔ (متغیر کی شکل میں ڈیٹا کو براہ راست R سے PowerShell تک پہنچانا ممکن تھا، لیکن یہ مشکل ہے، اور انٹرمیڈیٹ csvs ہونے سے کسی کے ساتھ انٹرمیڈیٹ نتائج کو ڈیبگ کرنا اور شیئر کرنا آسان ہوجاتا ہے)۔
  • R کا استعمال کرتے ہوئے، ہم ان فیلڈز کے لیے درست پیرامیٹرز بناتے ہیں جن کی قدروں کو ہم چیک کر رہے ہیں۔ - ہم ایک لفظی دستاویز بنا رہے ہیں جس میں معلوماتی خط میں داخل کرنے کے لیے ان فیلڈز کی قدریں ہوں گی، جو ساتھیوں کے سوالوں کا جواب ہوگا "نہیں، لیکن میں اسے کیسے پُر کروں؟"
  • ہم R کا استعمال کرتے ہوئے csv سے تمام VMs کے لیے ڈیٹا لوڈ کرتے ہیں، ایک ڈیٹا فریم بناتے ہیں، غیر ضروری فیلڈز کو ہٹاتے ہیں اور ایک معلوماتی xlsx دستاویز بناتے ہیں جس میں تمام VMs کے لیے خلاصہ معلومات شامل ہوں گی، جسے ہم مشترکہ وسائل پر اپ لوڈ کرتے ہیں۔
  • ہم تمام VMs کے لیے ڈیٹا فریم میں فیلڈز کو بھرنے کی درستگی کے لیے تمام چیک اپلائی کرتے ہیں اور غلط طریقے سے بھرے ہوئے فیلڈز (اور صرف یہ فیلڈز) کے ساتھ صرف VMs پر مشتمل ٹیبل بناتے ہیں۔
  • ہم VM کی نتیجے میں آنے والی فہرست کو ایک اور PowerShell اسکرپٹ کو بھیجتے ہیں، جو VM تخلیق کے واقعات کے لیے vCenter لاگز کو دیکھے گی، جو ہمیں VM کی تخلیق کے تخمینی وقت اور مطلوبہ تخلیق کار کی نشاندہی کرنے کی اجازت دے گی۔ یہ اس معاملے کے لیے ہے جب کوئی یہ تسلیم نہیں کرتا کہ یہ کس کی گاڑی ہے۔ یہ اسکرپٹ تیزی سے کام نہیں کرتا، خاص طور پر اگر بہت سارے لاگ ہیں، لہذا ہم صرف پچھلے 2 ہفتوں کو دیکھتے ہیں، اور ایک ورک فلو بھی استعمال کرتے ہیں جو آپ کو ایک ہی وقت میں کئی VMs پر معلومات تلاش کرنے کی اجازت دیتا ہے۔ مثال کے اسکرپٹ میں اس طریقہ کار پر تفصیلی تبصرے ہیں۔ ہم نتیجہ کو csv میں شامل کرتے ہیں، جسے ہم دوبارہ R میں لوڈ کرتے ہیں۔
  • ہم ایک خوبصورت فارمیٹ شدہ xlsx دستاویز تیار کرتے ہیں جس میں غلط طریقے سے بھرے ہوئے فیلڈز کو سرخ رنگ میں نمایاں کیا جائے گا، کچھ کالموں پر فلٹرز لاگو کیے جائیں گے، اور مطلوبہ تخلیق کاروں پر مشتمل اضافی کالم اور VM کی تخلیق کے وقت کی نشاندہی کی جائے گی۔
  • ہم ایک ای میل بناتے ہیں، جہاں ہم ایک دستاویز منسلک کرتے ہیں جس میں فیلڈ کی درست قدروں کو بیان کیا جاتا ہے، ساتھ ہی ساتھ ایک ٹیبل جس میں فیلڈز میں غلط طریقے سے بھرا گیا ہو۔ متن میں ہم غلط طریقے سے بنائے گئے VMs کی کل تعداد، مشترکہ وسائل کا لنک اور ایک تحریکی تصویر کی نشاندہی کرتے ہیں۔ اگر کوئی غلط طریقے سے بھرے ہوئے VMs نہیں ہیں، تو ہم ایک اور خط بھیجتے ہیں جس میں ایک خوش کن تحریکی تصویر ہے۔
  • ہم تاریخی جدولوں کے نافذ کردہ میکانزم کو مدنظر رکھتے ہوئے SQL سرور ڈیٹا بیس میں تمام VMs کے لیے ڈیٹا ریکارڈ کرتے ہیں (ایک بہت ہی دلچسپ طریقہ کار - جس کے بارے میں مزید بعد میں)

دراصل اسکرپٹس

مین آر کوڈ فائل

# Путь к рабочей директории (нужно для корректной работы через виндовый планировщик заданий)
setwd("C:ScriptsgetVm")
#### Подгружаем необходимые пакеты ####
library(tidyverse)
library(xlsx)
library(mailR)
library(rmarkdown)
##### Определяем пути к исходным файлам и другие переменные #####
source(file = "const.R", local = T, encoding = "utf-8")
# Проверяем существование файла со всеми ВМ и удаляем, если есть.
if (file.exists(filenameVmCreationRules)) {file.remove(filenameVmCreationRules)}
#### Создаём вордовский документ с допустимыми полями
render("VM_name_rules.Rmd",
output_format = word_document(),
output_file = filenameVmCreationRules)
# Проверяем существование файла со всеми ВМ и удаляем, если есть
if (file.exists(allVmXlsxPath)) {file.remove(allVmXlsxPath)}
#### Забираем данные по всем машинам через PowerShell скрипт. На выходе получим csv.
system(paste0("powershell -File ", getVmPsPath))
# Полный df
fullXslx_df <- allVmXlsxPath %>% 
read.csv2(stringsAsFactors = FALSE)
# Проверяем корректность заполненных полей
full_df <- fullXslx_df %>%
mutate(
# Сначала убираем все лишние пробелы и табуляции, потом учитываем разделитель запятую, потом проверяем вхождение в допустимые значения,
isSubsystemCorrect = Subsystem %>% 
gsub("[[:space:]]", "", .) %>% 
str_split(., ",") %>% 
map(function(x) (all(x %in% AllowedValues$Subsystem))) %>%
as.logical(),
isOwnerCorrect = Owner %in% AllowedValues$Owner,
isCategoryCorrect = Category %in% AllowedValues$Category,
isCreatorCorrect = (!is.na(Creator) & Creator != ''),
isCreation.DateCorrect = map(Creation.Date, IsDate)
)
# Проверяем существование файла со всеми ВМ и удаляем, если есть.
if (file.exists(filenameAll)) {file.remove(filenameAll)}
#### Формируем xslx файл с отчётом ####
# Общие данные на отдельный лист
full_df %>% write.xlsx(file=filenameAll,
sheetName=names[1],
col.names=TRUE,
row.names=FALSE,
append=FALSE)
#### Формируем xslx файл с неправильно заполненными полями ####
# Формируем df
incorrect_df <- full_df %>%
select(VM.Name, 
IP.s, 
Owner,
Subsystem,
Creator,
Category,
Creation.Date,
isOwnerCorrect, 
isSubsystemCorrect, 
isCategoryCorrect,
isCreatorCorrect,
vCenter.Name) %>%
filter(isSubsystemCorrect == F | 
isOwnerCorrect == F |
isCategoryCorrect == F |
isCreatorCorrect == F)
# Проверяем существование файла со всеми ВМ и удаляем, если есть.
if (file.exists(filenameIncVM)) {file.remove(filenameIncVM)}
# Сохраняем список VM с незаполненными полями в csv
incorrect_df %>%
select(VM.Name) %>%
write_csv2(path = filenameIncVM, append = FALSE)
# Фильтруем для вставки в почту
incorrect_df_filtered <- incorrect_df %>% 
select(VM.Name, 
IP.s, 
Owner, 
Subsystem, 
Category,
Creator,
vCenter.Name,
Creation.Date
)
# Считаем количество строк
numberOfRows <- nrow(incorrect_df)
#### Начало условия ####
# Дальше либо у нас есть неправильно заполненные поля, либо нет.
# Если есть - запускаем ещё один скрипт
if (numberOfRows > 0) {
# Проверяем существование файла с создателями и удаляем, если есть.
if (file.exists(creatorsFilePath)) {file.remove(creatorsFilePath)}
# Запускаем PowerShell скрипт, который найдёт создателей найденных VM. На выходе получим csv.
system(paste0("powershell -File ", getCreatorsPath))
# Читаем файл с создателями
creators_df <- creatorsFilePath %>%
read.csv2(stringsAsFactors = FALSE)
# Фильтруем для вставки в почту, добавляем данные из таблицы с создателями
incorrect_df_filtered <- incorrect_df_filtered %>% 
select(VM.Name, 
IP.s, 
Owner, 
Subsystem, 
Category,
Creator,
vCenter.Name,
Creation.Date
) %>% 
left_join(creators_df, by = "VM.Name") %>% 
rename(`Предполагаемый создатель` = CreatedBy, 
`Предполагаемая дата создания` = CreatedOn)  
# Формируем тело письма
emailBody <- paste0(
'<html>
<h3>Добрый день, уважаемые коллеги.</h3>
<p>Полную актуальную информацию по виртуальным машинам вы можете посмотреть на диске H: вот тут:<p>
<p>\server.ruVM', sourceFileFormat, '</p>
<p>Также во вложении список ВМ с <strong>некорректно заполненными</strong> полями. Всего их <strong>', numberOfRows,'</strong>.</p>
<p>В таблице появилось 2 дополнительные колонки. <strong>Предполагаемый создатель</strong> и <strong>Предполагаемая дата создания</strong>, которые достаются из логов vCenter за последние 2 недели</p>
<p>Просьба создателей машин уточнить данные и заполнить поля корректно. Правила заполнения полей также во вложении</p>
<p><img src="data/meme.jpg"></p>
</html>'
)
# Проверяем существование файла
if (file.exists(filenameIncorrect)) {file.remove(filenameIncorrect)}
# Формируем красивую таблицу с форматами и т.д.
source(file = "email.R", local = T, encoding = "utf-8")
#### Формируем письмо с плохо подписанными машинами ####
send.mail(from = emailParams$from,
to = emailParams$to,
subject = "ВМ с некорректно заполненными полями",
body = emailBody,
encoding = "utf-8",
html = TRUE,
inline = TRUE,
smtp = emailParams$smtpParams,
authenticate = TRUE,
send = TRUE,
attach.files = c(filenameIncorrect, filenameVmCreationRules),
debug = FALSE)
#### Дальше пойдёт блок, если нет проблем с ВМ ####
} else {
# Формируем тело письма
emailBody <- paste0(
'<html>
<h3>Добрый день, уважаемые коллеги</h3>
<p>Полную актуальную информацию по виртуальным машинам вы можете посмотреть на диске H: вот тут:<p>
<p>\server.ruVM', sourceFileFormat, '</p>
<p>Также, на текущий момент, все поля ВМ корректно заполнены</p>
<p><img src="data/meme_correct.jpg"></p>
</html>'
)
#### Формируем письмо без плохо заполненных VM ####
send.mail(from = emailParams$from,
to = emailParams$to,
subject = "Сводная информация",
body = emailBody,
encoding = "utf-8",
html = TRUE,
inline = TRUE,
smtp = emailParams$smtpParams,
authenticate = TRUE,
send = TRUE,
debug = FALSE)
}
####### Записываем данные в БД #####
source(file = "DB.R", local = T, encoding = "utf-8")

پاور شیل میں vm کی فہرست حاصل کرنے کے لیے اسکرپٹ

# Данные для подключения и другие переменные
$vCenterNames = @(
"vcenter01", 
"vcenter02", 
"vcenter03"
)
$vCenterUsername = "myusername"
$vCenterPassword = "mypassword"
$filename = "C:ScriptsgetVmdataallvmall-vm-$(get-date -f yyyy-MM-dd).csv"
$destinationSMB = "server.rumyfolder$vm"
$IP0=""
$IP1=""
$IP2=""
$IP3=""
$IP4=""
$IP5=""
# Подключение ко всем vCenter, что содержатся в переменной. Будет работать, если логин и пароль одинаковые (например, доменные)
Connect-VIServer -Server $vCenterNames -User $vCenterUsername -Password $vCenterPassword
write-host ""
# Создаём функцию с циклом по всем vCenter-ам
function Get-VMinventory {
# В этой переменной будет списко всех ВМ, как объектов
$AllVM = Get-VM | Sort Name
$cnt = $AllVM.Count
$count = 1
# Начинаем цикл по всем ВМ и собираем необходимые параметры каждого объекта
foreach ($vm in $AllVM) {
$StartTime = $(get-date)
$IP0 = $vm.Guest.IPAddress[0]
$IP1 = $vm.Guest.IPAddress[1]
$IP2 = $vm.Guest.IPAddress[2]
$IP3 = $vm.Guest.IPAddress[3]
$IP4 = $vm.Guest.IPAddress[4]
$IP5 = $vm.Guest.IPAddress[5]
If ($IP0 -ne $null) {If ($IP0.Contains(":") -ne 0) {$IP0=""}}
If ($IP1 -ne $null) {If ($IP1.Contains(":") -ne 0) {$IP1=""}}
If ($IP2 -ne $null) {If ($IP2.Contains(":") -ne 0) {$IP2=""}}
If ($IP3 -ne $null) {If ($IP3.Contains(":") -ne 0) {$IP3=""}}
If ($IP4 -ne $null) {If ($IP4.Contains(":") -ne 0) {$IP4=""}}
If ($IP5 -ne $null) {If ($IP5.Contains(":") -ne 0) {$IP5=""}}
$cluster = $vm | Get-Cluster | Select-Object -ExpandProperty name  
$Bootime = $vm.ExtensionData.Runtime.BootTime
$TotalHDDs = $vm.ProvisionedSpaceGB -as [int]
$CreationDate = $vm.CustomFields.Item("CreationDate") -as [string]
$Creator = $vm.CustomFields.Item("Creator") -as [string]
$Category = $vm.CustomFields.Item("Category") -as [string]
$Owner = $vm.CustomFields.Item("Owner") -as [string]
$Subsystem = $vm.CustomFields.Item("Subsystem") -as [string]
$IPS = $vm.CustomFields.Item("IP") -as [string]
$vCPU = $vm.NumCpu
$CorePerSocket = $vm.ExtensionData.config.hardware.NumCoresPerSocket
$Sockets = $vCPU/$CorePerSocket
$Id = $vm.Id.Split('-')[2] -as [int]
# Собираем все параметры в один объект
$Vmresult = New-Object PSObject
$Vmresult | add-member -MemberType NoteProperty -Name "Id" -Value $Id   
$Vmresult | add-member -MemberType NoteProperty -Name "VM Name" -Value $vm.Name  
$Vmresult | add-member -MemberType NoteProperty -Name "Cluster" -Value $cluster  
$Vmresult | add-member -MemberType NoteProperty -Name "Esxi Host" -Value $VM.VMHost  
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 1" -Value $IP0
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 2" -Value $IP1
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 3" -Value $IP2
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 4" -Value $IP3
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 5" -Value $IP4
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 6" -Value $IP5
$Vmresult | add-member -MemberType NoteProperty -Name "vCPU" -Value $vCPU
$Vmresult | Add-Member -MemberType NoteProperty -Name "CPU Sockets" -Value $Sockets
$Vmresult | Add-Member -MemberType NoteProperty -Name "Core per Socket" -Value $CorePerSocket
$Vmresult | add-member -MemberType NoteProperty -Name "RAM (GB)" -Value $vm.MemoryGB
$Vmresult | add-member -MemberType NoteProperty -Name "Total-HDD (GB)" -Value $TotalHDDs
$Vmresult | add-member -MemberType NoteProperty -Name "Power State" -Value $vm.PowerState
$Vmresult | add-member -MemberType NoteProperty -Name "OS" -Value $VM.ExtensionData.summary.config.guestfullname  
$Vmresult | Add-Member -MemberType NoteProperty -Name "Boot Time" -Value $Bootime
$Vmresult | add-member -MemberType NoteProperty -Name "VMTools Status" -Value $vm.ExtensionData.Guest.ToolsStatus  
$Vmresult | add-member -MemberType NoteProperty -Name "VMTools Version" -Value $vm.ExtensionData.Guest.ToolsVersion  
$Vmresult | add-member -MemberType NoteProperty -Name "VMTools Version Status" -Value $vm.ExtensionData.Guest.ToolsVersionStatus  
$Vmresult | add-member -MemberType NoteProperty -Name "VMTools Running Status" -Value $vm.ExtensionData.Guest.ToolsRunningStatus  
$Vmresult | add-member -MemberType NoteProperty -Name "Creation Date" -Value $CreationDate
$Vmresult | add-member -MemberType NoteProperty -Name "Creator" -Value $Creator
$Vmresult | add-member -MemberType NoteProperty -Name "Category" -Value $Category
$Vmresult | add-member -MemberType NoteProperty -Name "Owner" -Value $Owner
$Vmresult | add-member -MemberType NoteProperty -Name "Subsystem" -Value $Subsystem
$Vmresult | add-member -MemberType NoteProperty -Name "IP's" -Value $IPS
$Vmresult | add-member -MemberType NoteProperty -Name "vCenter Name" -Value $vm.Uid.Split('@')[1].Split(':')[0]  
# Считаем общее и оставшееся время выполнения и выводим на экран результаты. Использовалось для тестирования, но по факту оказалось очень удобно.
$elapsedTime = $(get-date) - $StartTime
$totalTime = "{0:HH:mm:ss}" -f ([datetime]($elapsedTime.Ticks*($cnt - $count)))
clear-host
Write-Host "Processing" $count "from" $cnt 
Write-host "Progress:" ([math]::Round($count/$cnt*100, 2)) "%" 
Write-host "You have about " $totalTime "for cofee"
Write-host ""
$count++
# Выводим результат, чтобы цикл "знал" что является результатом выполнения одного прохода
$Vmresult
}
}
# Вызываем получившуюся функцию и сразу выгружаем результат в csv
$allVm = Get-VMinventory | Export-CSV -Path $filename -NoTypeInformation -UseCulture -Force
# Пытаемся выложить полученный файл в нужное нам место и, в случае ошибки, пишем лог.
try
{
Copy-Item $filename -Destination $destinationSMB -Force -ErrorAction SilentlyContinue
}
catch
{
$error | Export-CSV -Path $filename".error" -NoTypeInformation -UseCulture -Force
}

ایک PowerShell اسکرپٹ جو ورچوئل مشینوں کے تخلیق کاروں اور ان کی تخلیق کی تاریخ کو لاگ سے نکالتا ہے

# Путь к файлу, из которого будем доставать список VM
$VMfilePath = "C:ScriptsgetVmcreators_VMcreators_VM_$(get-date -f yyyy-MM-dd).csv"
# Путь к файлу, в который будем записывать результат
$filePath = "C:ScriptsgetVmdatacreatorscreators-$(get-date -f yyyy-MM-dd).csv"
# Создаём вокрфлоу
Workflow GetCreators-Wf
{
# Параметры, которые можно будет передать при вызове скрипта
param([string[]]$VMfilePath)
# Параметры, которые доступны только внутри workflow
$vCenterUsername = "myusername"
$vCenterPassword = "mypassword"
$daysToLook = 14
$start = (get-date).AddDays(-$daysToLook)
$finish = get-date
# Значения, которые будут вписаны в csv для машин, по которым не будет ничего найдено
$UnknownUser = "UNKNOWN"
$UnknownCreatedTime = "0000-00-00"
# Определяем параметры подключения и выводной файл, которые будут доступны во всём скрипте.
$vCenterNames = @(
"vcenter01", 
"vcenter02", 
"vcenter03"
)
# Получаем список VM из csv и загружаем соответствующие объекты
$list = Import-Csv $VMfilePath -UseCulture | select -ExpandProperty VM.Name
# Цикл, который будет выполняться параллельно (по 5 машин за раз)
foreach -parallel ($row in $list)
{
# Это скрипт, который видит только свои переменные и те, которые ему переданы через $Using
InlineScript {
# Время начала выполнения отдельного блока
$StartTime = $(get-date)
Write-Host ""
Write-Host "Processing $Using:row started at $StartTime"
Write-Host ""
# Подключение оборачиваем в переменную, чтобы информация о нём не мешалась в консоли
$con = Connect-VIServer -Server $Using:vCenterNames -User $Using:vCenterUsername -Password $Using:vCenterPassword
# Получаем объект vm
$vm = Get-VM -Name $Using:row
# Ниже 2 одинаковые команды. Одна с фильтром по времени, вторая - без. Можно пользоваться тем,
$Event = $vm | Get-VIEvent -Start $Using:start -Finish $Using:finish -Types Info | Where { $_.Gettype().Name -eq "VmBeingDeployedEvent" -or $_.Gettype().Name -eq "VmCreatedEvent" -or $_.Gettype().Name -eq "VmRegisteredEvent" -or $_.Gettype().Name -eq "VmClonedEvent"}
# $Event = $vm | Get-VIEvent -Types Info | Where { $_.Gettype().Name -eq "VmBeingDeployedEvent" -or $_.Gettype().Name -eq "VmCreatedEvent" -or $_.Gettype().Name -eq "VmRegisteredEvent" -or $_.Gettype().Name -eq "VmClonedEvent"}
# Заполняем параметры в зависимости от того, удалось ли в логах найти что-то
If (($Event | Measure-Object).Count -eq 0){
$User = $Using:UnknownUser
$Created = $Using:UnknownCreatedTime
$CreatedFormat = $Using:UnknownCreatedTime
} Else {
If ($Event.Username -eq "" -or $Event.Username -eq $null) {
$User = $Using:UnknownUser
} Else {
$User = $Event.Username
} # Else
$CreatedFormat = $Event.CreatedTime
# Один из коллег отдельно просил, чтобы время было в таком формате, поэтому дублируем его. А в БД пойдёт нормальный формат.
$Created = $Event.CreatedTime.ToString('yyyy-MM-dd')
} # Else
Write-Host "Creator for $vm is $User. Creating object."
# Создаём объект. Добавляем параметры.
$Vmresult = New-Object PSObject
$Vmresult | add-member -MemberType NoteProperty -Name "VM Name" -Value $vm.Name  
$Vmresult | add-member -MemberType NoteProperty -Name "CreatedBy" -Value $User
$Vmresult | add-member -MemberType NoteProperty -Name "CreatedOn" -Value $CreatedFormat
$Vmresult | add-member -MemberType NoteProperty -Name "CreatedOnFormat" -Value $Created           
# Выводим результаты
$Vmresult
} # Inline
} # ForEach
}
$Creators = GetCreators-Wf $VMfilePath
# Записываем результат в файл
$Creators | select 'VM Name', CreatedBy, CreatedOn | Export-Csv -Path $filePath -NoTypeInformation -UseCulture -Force
Write-Host "CSV generetion finisghed at $(get-date). PROFIT"

لائبریری خصوصی توجہ کی مستحق ہے۔ xlsx، جس نے خط کے ساتھ منسلکہ کو واضح طور پر فارمیٹ کرنا ممکن بنایا (جیسا کہ انتظامیہ کی پسند ہے)، نہ کہ صرف ایک CSV ٹیبل۔

مشینوں میں غلط طریقے سے بھری ہوئی فہرست کے ساتھ ایک خوبصورت xlsx دستاویز تیار کرنا

# Создаём новую книгу
# Возможные значения : "xls" и "xlsx"
wb<-createWorkbook(type="xlsx")
# Стили для имён рядов и колонок в таблицах
TABLE_ROWNAMES_STYLE <- CellStyle(wb) + Font(wb, isBold=TRUE)
TABLE_COLNAMES_STYLE <- CellStyle(wb) + Font(wb, isBold=TRUE) +
Alignment(wrapText=TRUE, horizontal="ALIGN_CENTER") +
Border(color="black", position=c("TOP", "BOTTOM"), 
pen=c("BORDER_THIN", "BORDER_THICK"))
# Создаём новый лист
sheet <- createSheet(wb, sheetName = names[2])
# Добавляем таблицу
addDataFrame(incorrect_df_filtered, 
sheet, startRow=1, startColumn=1,  row.names=FALSE, byrow=FALSE,
colnamesStyle = TABLE_COLNAMES_STYLE,
rownamesStyle = TABLE_ROWNAMES_STYLE)
# Меняем ширину, чтобы форматирование было автоматическим
autoSizeColumn(sheet = sheet, colIndex=c(1:ncol(incorrect_df)))
# Добавляем фильтры
addAutoFilter(sheet, cellRange = "C1:G1")
# Определяем стиль
fo2 <- Fill(foregroundColor="red")
cs2 <- CellStyle(wb, 
fill = fo2, 
dataFormat = DataFormat("@"))
# Находим ряды с неверно заполненным полем Владельца и применяем к ним определённый стиль
rowsOwner <- getRows(sheet, rowIndex = (which(!incorrect_df$isOwnerCorrect) + 1))
cellsOwner <- getCells(rowsOwner, colIndex = which( colnames(incorrect_df_filtered) == "Owner" )) 
lapply(names(cellsOwner), function(x) setCellStyle(cellsOwner[[x]], cs2))
# Находим ряды с неверно заполненным полем Подсистемы и применяем к ним определённый стиль
rowsSubsystem <- getRows(sheet, rowIndex = (which(!incorrect_df$isSubsystemCorrect) + 1))
cellsSubsystem <- getCells(rowsSubsystem, colIndex = which( colnames(incorrect_df_filtered) == "Subsystem" )) 
lapply(names(cellsSubsystem), function(x) setCellStyle(cellsSubsystem[[x]], cs2))
# Аналогично по Категории
rowsCategory <- getRows(sheet, rowIndex = (which(!incorrect_df$isCategoryCorrect) + 1))
cellsCategory <- getCells(rowsCategory, colIndex = which( colnames(incorrect_df_filtered) == "Category" )) 
lapply(names(cellsCategory), function(x) setCellStyle(cellsCategory[[x]], cs2))
# Создатель
rowsCreator <- getRows(sheet, rowIndex = (which(!incorrect_df$isCreatorCorrect) + 1))
cellsCreator <- getCells(rowsCreator, colIndex = which( colnames(incorrect_df_filtered) == "Creator" )) 
lapply(names(cellsCreator), function(x) setCellStyle(cellsCreator[[x]], cs2))
# Сохраняем файл
saveWorkbook(wb, filenameIncorrect)

آؤٹ پٹ کچھ اس طرح نظر آتا ہے:

R اور PowerShell استعمال کرنے والی ورچوئل مشینوں کی صحت سے متعلق روزانہ کی رپورٹس

ونڈوز شیڈیولر کو ترتیب دینے کے بارے میں ایک دلچسپ بات بھی تھی۔ صحیح حقوق اور ترتیبات کو تلاش کرنا ناممکن تھا تاکہ سب کچھ اسی طرح شروع ہوجائے جیسا کہ ہونا چاہئے۔ نتیجے کے طور پر، R لائبریری مل گئی، جو خود ایک R سکرپٹ کو شروع کرنے کے لئے ایک کام بناتی ہے اور لاگ فائل کے بارے میں بھی نہیں بھولتی ہے. پھر آپ کام کو دستی طور پر درست کر سکتے ہیں۔

دو مثالوں کے ساتھ R کوڈ کا ایک ٹکڑا جو ونڈوز شیڈیولر میں ایک کام تخلیق کرتا ہے۔

library(taskscheduleR)
myscript <- file.path(getwd(), "all_vm.R")
## запускаем скрипт через 62 секунды
taskscheduler_create(taskname = "getAllVm", rscript = myscript, 
schedule = "ONCE", starttime = format(Sys.time() + 62, "%H:%M"))
## запускаем скрипт каждый день в 09:10
taskscheduler_create(taskname = "getAllVmDaily", rscript = myscript, 
schedule = "WEEKLY", 
days = c("MON", "TUE", "WED", "THU", "FRI"),
starttime = "02:00")
## удаляем задачи
taskscheduler_delete(taskname = "getAllVm")
taskscheduler_delete(taskname = "getAllVmDaily")
# Смотрим логи (последние 4 строчки)
tail(readLines("all_vm.log"), sep ="n", n = 4)

علیحدہ علیحدہ ڈیٹا بیس کے بارے میں

سکرپٹ ترتیب دینے کے بعد دیگر مسائل سامنے آنے لگے۔ مثال کے طور پر، میں وہ تاریخ تلاش کرنا چاہتا تھا جب VM کو حذف کیا گیا تھا، لیکن vCenter میں لاگز پہلے ہی ختم ہو چکے تھے۔ چونکہ اسکرپٹ ہر روز فائلوں کو فولڈر میں رکھتا ہے اور اسے صاف نہیں کرتا ہے (جب ہمیں یاد ہے تو ہم اسے اپنے ہاتھوں سے صاف کرتے ہیں)، آپ پرانی فائلوں کو دیکھ کر پہلی فائل تلاش کرسکتے ہیں جس میں یہ VM موجود نہیں ہے۔ لیکن یہ ٹھنڈا نہیں ہے۔

میں ایک تاریخی ڈیٹا بیس بنانا چاہتا تھا۔

ایم ایس ایس کیو ایل سرور کی فعالیت - سسٹم کے ورژن میں عارضی ٹیبل - بچاؤ کے لیے آیا۔ اس کا ترجمہ عام طور پر عارضی (عارضی نہیں) جدولوں کے طور پر کیا جاتا ہے۔

آپ پر تفصیل سے پڑھ سکتے ہیں۔ مائیکروسافٹ کی سرکاری دستاویزات.

مختصراً، ہم ایک ٹیبل بناتے ہیں، کہتے ہیں کہ یہ ہمارے پاس ورژننگ کے ساتھ ہوگا، اور ایس کیو ایل سرور اس ٹیبل میں 2 اضافی ڈیٹ ٹائم کالم بناتا ہے (ریکارڈ بنانے کی تاریخ اور ریکارڈ کی میعاد ختم ہونے کی تاریخ) اور ایک اضافی ٹیبل جس میں تبدیل ہوتا ہے۔ لکھا جائے گا. نتیجے کے طور پر، ہمیں تازہ ترین معلومات ملتی ہیں اور، سادہ سوالات کے ذریعے، جن کی مثالیں دستاویزات میں دی گئی ہیں، ہم یا تو ایک مخصوص ورچوئل مشین کا لائف سائیکل، یا کسی خاص مقام پر تمام VMs کی حالت دیکھ سکتے ہیں۔ وقت میں

کارکردگی کے نقطہ نظر سے، مین ٹیبل پر تحریری لین دین اس وقت تک مکمل نہیں ہوگا جب تک کہ عارضی ٹیبل پر تحریری لین دین مکمل نہ ہوجائے۔ وہ. بڑی تعداد میں تحریری کارروائیوں کے ساتھ میزوں پر، اس فعالیت کو احتیاط کے ساتھ لاگو کیا جانا چاہئے، لیکن ہمارے معاملے میں یہ واقعی ایک عمدہ چیز ہے۔

میکانزم کے صحیح طریقے سے کام کرنے کے لیے، مجھے R میں کوڈ کا ایک چھوٹا سا ٹکڑا شامل کرنا پڑا جو نئے ٹیبل کا موازنہ تمام VMs کے ڈیٹا کے ساتھ ڈیٹا بیس میں محفوظ کردہ کے ساتھ کرے گا اور اس میں صرف تبدیل شدہ قطاریں لکھے گا۔ کوڈ خاص طور پر ہوشیار نہیں ہے؛ یہ compareDF لائبریری کا استعمال کرتا ہے، لیکن میں اسے ذیل میں بھی پیش کروں گا۔

ڈیٹا بیس میں ڈیٹا لکھنے کے لیے R کوڈ

# Подцепляем пакеты
library(odbc)
library(compareDF)
# Формируем коннект
con <- dbConnect(odbc(),
Driver = "ODBC Driver 13 for SQL Server",
Server = DBParams$server,
Database = DBParams$database,
UID = DBParams$UID,
PWD = DBParams$PWD,
Port = 1433)
#### Проверяем есть ли таблица. Если нет - создаём. ####
if (!dbExistsTable(con, DBParams$TblName)) {
#### Создаём таблицу ####
create <- dbSendStatement(
con,
paste0(
'CREATE TABLE ',
DBParams$TblName,
'(
[Id] [int] NOT NULL PRIMARY KEY CLUSTERED,
[VM.Name] [varchar](255) NULL,
[Cluster] [varchar](255) NULL,
[Esxi.Host] [varchar](255) NULL,
[IP.Address.1] [varchar](255) NULL,
[IP.Address.2] [varchar](255) NULL,
[IP.Address.3] [varchar](255) NULL,
[IP.Address.4] [varchar](255) NULL,
[IP.Address.5] [varchar](255) NULL,
[IP.Address.6] [varchar](255) NULL,
[vCPU] [int] NULL,
[CPU.Sockets] [int] NULL,
[Core.per.Socket] [int] NULL,
[RAM..GB.] [int] NULL,
[Total.HDD..GB.] [int] NULL,
[Power.State] [varchar](255) NULL,
[OS] [varchar](255) NULL,
[Boot.Time] [varchar](255) NULL,
[VMTools.Status] [varchar](255) NULL,
[VMTools.Version] [int] NULL,
[VMTools.Version.Status] [varchar](255) NULL,
[VMTools.Running.Status] [varchar](255) NULL,
[Creation.Date] [varchar](255) NULL,
[Creator] [varchar](255) NULL,
[Category] [varchar](255) NULL,
[Owner] [varchar](255) NULL,
[Subsystem] [varchar](255) NULL,
[IP.s] [varchar](255) NULL,
[vCenter.Name] [varchar](255) NULL,
DateFrom datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
DateTo datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (DateFrom, DateTo)
) ON [PRIMARY]
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = ', DBParams$TblHistName,'));'
)
)
# Отправляем подготовленный запрос
dbClearResult(create)
} # if
#### Начало работы с таблицей ####
# Обозначаем таблицу, с которой будем работать
allVM_db_con <- tbl(con, DBParams$TblName) 
#### Сравниваем таблицы ####
# Собираем данные с таблицы (убираем служебные временные поля)
allVM_db <- allVM_db_con %>% 
select(c(-"DateTo", -"DateFrom")) %>% 
collect()
# Создаём таблицу со сравнением объектов. Сравниваем по Id
# Удалённые объекты там будут помечены через -, созданные через +, изменённые через - и +
ctable_VM <- fullXslx_df %>% 
compare_df(allVM_db, 
c("Id"))
#### Удаление строк ####
# Выдираем Id виртуалок, записи о которых надо удалить 
remove_Id <- ctable_VM$comparison_df %>% 
filter(chng_type == "-") %>%
select(Id)
# Проверяем, что есть записи (если записей нет - и удалять ничего не нужно)
if (remove_Id %>% nrow() > 0) {
# Конструируем шаблон для запроса на удаление данных
delete <- dbSendStatement(con, 
paste0('
DELETE 
FROM ',
DBParams$TblName,
' WHERE "Id"=?
') # paste
) # send
# Создаём запрос на удаление данных
dbBind(delete, remove_Id)
# Отправляем подготовленный запрос
dbClearResult(delete)
} # if
#### Добавление строк ####
# Выделяем таблицу, содержащую строки, которые нужно добавить.
allVM_add <- ctable_VM$comparison_df %>% 
filter(chng_type == "+") %>% 
select(-chng_type)
# Проверяем, есть ли строки, которые нужно добавить и добавляем (если нет - не добавляем)
if (allVM_add %>% nrow() > 0) {
# Пишем таблицу со всеми необходимыми данными
dbWriteTable(con,
DBParams$TblName,
allVM_add,
overwrite = FALSE,
append = TRUE)
} # if
#### Не забываем сделать дисконнект ####
dbDisconnect(con)

مجموعی طور پر

اسکرپٹ کے نفاذ کے نتیجے میں چند مہینوں میں آرڈر بحال اور برقرار رکھا گیا۔ بعض اوقات غلط طریقے سے بھرے ہوئے VM ظاہر ہوتے ہیں، لیکن اسکرپٹ ایک اچھی یاد دہانی کا کام کرتا ہے اور ایک نایاب VM لگاتار 2 دن تک فہرست میں آتا ہے۔

تاریخی اعداد و شمار کے تجزیہ کے لیے بھی گراؤنڈ ورک بنایا گیا۔

یہ واضح ہے کہ اس میں سے زیادہ تر کو گھٹنے پر نہیں بلکہ خصوصی سافٹ ویئر کے ذریعے لاگو کیا جا سکتا ہے، لیکن یہ کام دلچسپ تھا اور، کوئی کہہ سکتا ہے، اختیاری تھا۔

R نے ایک بار پھر اپنے آپ کو ایک بہترین عالمگیر زبان کے طور پر دکھایا ہے، جو نہ صرف شماریاتی مسائل کو حل کرنے کے لیے بہترین ہے، بلکہ ڈیٹا کے دوسرے ذرائع کے درمیان ایک بہترین "پرت" کے طور پر بھی کام کرتی ہے۔

R اور PowerShell استعمال کرنے والی ورچوئل مشینوں کی صحت سے متعلق روزانہ کی رپورٹس

ماخذ: www.habr.com

نیا تبصرہ شامل کریں