טעגלעך ריפּאָרץ וועגן די געזונט פון ווירטואַל מאשינען ניצן R און PowerShell

טעגלעך ריפּאָרץ וועגן די געזונט פון ווירטואַל מאשינען ניצן R און PowerShell

פּאָזיציע

א גוטן מיטאג. פֿאַר אַ האַלב יאָר מיר לויפן אַ שריפט (אָדער גאַנץ אַ סכום פון סקריפּס) וואָס דזשענערייץ ריפּאָרץ אויף די סטאַטוס פון ווירטואַל מאשינען (און ניט בלויז). איך באַשלאָסן צו טיילן מיין שאַפונג דערפאַרונג און די קאָד זיך. איך האָפֿן פֿאַר קריטיק און אַז דעם מאַטעריאַל קען זיין נוציק פֿאַר עמעצער.

פאָרמירונג פון נויט

מיר האָבן אַ פּלאַץ פון ווירטואַל מאשינען (וועגן 1500 וומס פונאנדערגעטיילט איבער 3 ווצענטערס). נייַע זענען באשאפן און אַלט אָנעס זענען אויסגעמעקט גאַנץ אָפט. צו האַלטן סדר, עטלעכע מנהג פעלדער זענען צוגעגעבן צו vCenter צו טיילן VMs אין סובסיסטעמס, אָנווייַזן צי זיי זענען פּראָבע אָנעס, און דורך וועמען און ווען זיי זענען באשאפן. דע ר מענטשלעכע ר פאקטא ר הא ט געפיר ט א ז מע ר האלב ן פו ן ד י מאשינע ן זענע ן געבליב ן מי ט לײדיק ע פעלדער , װא ס הא ט קאמפליציר ט ד י ארבעט . אַמאָל יעדער זעקס חדשים, עמעצער איז געווען דערשראָקן און אנגעהויבן ארבעטן אויף דערהייַנטיקונגען פון די דאַטן, אָבער דער רעזולטאַט איז געווען באַטייַטיק נאָך אַ וואָך און אַ האַלב.
לאמיר גלייך קלאר מאכן אז יעדער פארשטייט אז עס דארף זיין אפליקאציעס פאר די שאַפונג פון מאשינען, א פראצעס פאר זייער שאַפונג א.א.וו. און אזוי ווייטער. און אין דער זעלביקער צייַט, אַלעמען שטרענג נאָכפאָלגן דעם פּראָצעס און אַלץ איז אין סדר. צום באַדויערן, דאָס איז נישט דער פאַל דאָ, אָבער דאָס איז נישט די טעמע פון ​​דעם אַרטיקל :)

אין אַלגעמיין, די באַשלוס איז געמאכט צו אָטאַמייט קאָנטראָלירונג די ריכטיק פון פילונג אין די פעלדער.
מי ר האב ן באשלאסן , א ז א גוט ן אנהויב , װע ט זײ ן א גוט ן אנהײ ב א טאג־טעגלעכ ן בריװ , מי ט א ליסטע , פו ן אומרעכט־אויסגעפילט ע מאשינען , צ ו אל ע פאראנטװארטלעכ ע אינזשעניר ן או ן זײער ע באלעבאטים .

אין דעם פונט, איינער פון מיין חברים האט שוין ימפּלאַמענאַד אַ שריפט אין PowerShell, וואָס יעדער טאָג, לויט אַ פּלאַן, געזאמלט אינפֿאָרמאַציע אויף אַלע מאשינען פון אַלע ווצענטערס און דזשענערייטאַד 3 קסוו דאָקומענטן (יעדער פֿאַר זיין אייגענע ווצענטער), וואָס זענען ופּלאָאַדעד צו אַ פּראָסט דיסק. ע ס אי ז באשלאס ן געװאר ן צ ו נעמע ן דע ם דאזיק ן שריפט , װ י א יסוד , או ן ז ײ צופילע ן מי ט טשעקס ן מי ט דע ר שפראך , מי ט װעלכע ר מי ר האב ן געהא ט עפע ס דערפארונג .

אין דעם פּראָצעס פון פינאַליזיישאַן, די לייזונג קונה אינפֿאָרמאַציע דורך פּאָסט, אַ דאַטאַבייס מיט אַ הויפּט און היסטארישן טיש (מער אויף דעם שפּעטער), ווי געזונט ווי אַן אַנאַליסיס פון vSphere לאָגס צו געפֿינען די פאַקטיש קריייטערז פון VM און די צייט פון זייער שאַפונג.

IDE RStudio Desktop און PowerShell ISE זענען געניצט פֿאַר אַנטוויקלונג.

דער שריפט איז לאָנטשט פֿון אַ רעגולער Windows ווירטואַל מאַשין.

באַשרייַבונג פון אַלגעמיין לאָגיק.

די אַלגעמיינע לאָגיק פון די סקריפּס איז ווי גייט.

  • מיר קלייַבן דאַטן אויף ווירטואַל מאשינען ניצן אַ PowerShell שריפט, וואָס מיר רופן דורך R, און פאַרבינדן די רעזולטאַט אין איין קסוו. די פאַרקערט ינטעראַקשאַן צווישן שפראַכן איז געטאן סימאַלערלי. (עס איז געווען מעגלעך צו פאָרן דאַטן גלייַך פֿון R צו PowerShell אין די פאָרעם פון וועריאַבאַלז, אָבער דאָס איז שווער, און מיט ינטערמידייט קסוו מאכט עס גרינגער צו דיבאַגינג און טיילן ינטערמידייט רעזולטאַטן מיט עמעצער).
  • ניצן R, מיר פאָרעם גילטיק פּאַראַמעטערס פֿאַר די פעלדער וועמענס וואַלועס מיר קאָנטראָלירן. - מיר שאַפֿן אַ וואָרט דאָקומענט וואָס וועט אַנטהאַלטן די וואַלועס פון די פעלדער פֿאַר ינסערשאַן אין די אינפֿאָרמאַציע בריוו, וואָס וועט זיין דער ענטפער צו די פֿראגן פון חברים "ניין, אָבער ווי זאָל איך פּלאָמבירן דעם אויס?"
  • מיר לאָדן דאַטן פֿאַר אַלע VMs פֿון CSV ניצן R, שאַפֿן אַ דאַטאַפראַמע, באַזייַטיקן ומנייטיק פעלדער און שאַפֿן אַן אינפֿאָרמאַציע xlsx דאָקומענט וואָס וועט אַנטהאַלטן קיצער אינפֿאָרמאַציע פֿאַר אַלע VMs, וואָס מיר צופֿעליקער צו אַ שערד מיטל.
  • מיר צולייגן אַלע טשעקס פֿאַר די קערעקטנאַס פון פילונג אין די פעלדער צו די דאַטאַפראַמע פֿאַר אַלע VMs און שאַפֿן אַ טיש מיט בלויז VMs מיט ינקערעקטלי אָנגעפילט פעלדער (און בלויז די פעלדער).
  • מיר שיקן די ריזאַלטינג רשימה פון VMs צו אן אנדער PowerShell שריפט, וואָס וועט קוקן אין די vCenter לאָגס פֿאַר VM שאַפונג געשעענישן, וואָס וועט לאָזן אונדז צו אָנווייַזן די עסטימאַטעד צייט פון שאַפונג פון די VM און די בדעה באשעפער. דאָס איז פֿאַר דעם פאַל ווען קיין איינער אַדמיץ וועמענס מאַשין עס איז. דער שריפט אַרבעט נישט געשווינד, ספּעציעל אויב עס זענען אַ פּלאַץ פון לאָגס, אַזוי מיר קוקן בלויז אין די לעצטע 2 וואָכן, און אויך נוצן אַ וואָרקפלאָוו וואָס אַלאַוז איר צו זוכן אינפֿאָרמאַציע אויף עטלעכע VMs אין דער זעלביקער צייט. דער ביישפּיל שריפט כּולל דיטיילד באַמערקונגען אויף דעם מעקאַניזאַם. מיר לייגן דעם רעזולטאַט אין אַ csv, וואָס מיר לאָדן ווידער אין R.
  • מיר דזשענערייט אַ ביוטאַפלי פאָרמאַטטעד קסלסקס דאָקומענט אין וואָס פאַלש אָנגעפילט פעלדער וועט זיין כיילייטיד אין רויט, פילטערס וועט זיין געווענדט צו עטלעכע שפאלטן, און נאָך שפאלטן מיט די בדעה קריייטערז און די צייט פון שאַפונג פון די VM וועט זיין אנגעוויזן.
  • מיר דזשענערייט אַ בליצפּאָסט, ווו מיר צוטשעפּען אַ דאָקומענט וואָס דיסקרייבינג די גילטיק פעלד וואַלועס, ווי געזונט ווי אַ טיש מיט פאַלש אָנגעפילט פעלדער. אין דעם טעקסט מיר אָנווייַזן די גאַנץ נומער פון ינקרעקטלי באשאפן VMs, אַ לינק צו אַ שערד מיטל און אַ מאָוטאַוויישאַנאַל בילד. אויב עס זענען קיין פאַלש אָנגעפילט VMs, מיר שיקן אן אנדער בריוו מיט אַ כאַפּיער מאָוטאַוויישאַנאַל בילד.
  • מיר רעקאָרדירן דאַטן פֿאַר אַלע VMs אין די SQL Server דאַטאַבייס, גענומען אין חשבון די ימפּלאַמענאַד מעקאַניזאַם פון היסטארישע טישן (אַ זייער טשיקאַווע מעקאַניזאַם - וועגן וואָס מער שפּעטער)

אַקטואַללי סקריפּס

הויפּט ר קאָד טעקע

# Путь к рабочей директории (нужно для корректной работы через виндовый планировщик заданий)
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 אין PowerShell

# Данные для подключения и другие переменные
$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"

די ביבליאָטעק פארדינט ספּעציעל ופמערקזאַמקייַט קסלסקס, וואָס געמאכט עס מעגלעך צו מאַכן די אַטאַטשמאַנט צו די בריוו קלאר פאָרמאַטטעד (ווי די פאַרוואַלטונג לייקס), און נישט בלויז אַ קסוו טיש.

דזשענערייטינג אַ שיין קסלסקס דאָקומענט מיט אַ רשימה פון ינקערעקטלי אָנגעפילט מאשינען

# Создаём новую книгу
# Возможные значения : "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

עס איז אויך געווען אַ טשיקאַווע נואַנס וועגן באַשטעטיקן די Windows סקעדזשולער. עס איז געווען אוממעגלעך צו געפֿינען די רעכט רעכט און סעטטינגס אַזוי אַז אַלץ וואָלט אָנהייבן ווי עס זאָל. ווי אַ רעזולטאַט, די R ביבליאָטעק איז געפונען, וואָס זיך קריייץ אַ אַרבעט צו קאַטער אַ R שריפט און טוט נישט אפילו פאַרגעסן וועגן די קלאָץ טעקע. דערנאָך איר קענען פאַרריכטן די אַרבעט מאַניואַלי.

א שטיק פון R קאָד מיט צוויי ביישפילן וואָס קריייץ אַ אַרבעט אין די Windows Scheduler

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 איז נישט פאָרשטעלן. אבער אַז ס 'נישט קיל.

איך געוואלט צו שאַפֿן אַ היסטארישע דאַטאַבייס.

די פאַנגקשאַנאַליטי פון MS SQL SERVER - סיסטעם-ווערסיאָנעד טעמפּעראַל טיש - געקומען צו ראַטעווען. עס איז יוזשאַוואַלי איבערגעזעצט ווי צייַטווייַליק (ניט צייַטווייַליק) טישן.

איר קענען לייענען אין דעטאַל אין באַאַמטער מיקראָסאָפט דאַקיומענטיישאַן.

אין קורץ, מיר מאַכן אַ טיש, זאָגן אַז מיר וועלן האָבן עס מיט ווערסיע, און SQL Server קריייץ 2 נאָך דאַטעטיים שפאלטן אין דעם טיש (די דאַטע וואָס די רעקאָרד איז באשאפן און די עקספּעריישאַן טאָג פון די רעקאָרד) און אַן נאָך טיש אין וואָס ענדערונגען. וועט זיין געשריבן. ווי אַ רעזולטאַט, מיר באַקומען דערהייַנטיקט אינפֿאָרמאַציע און, דורך פּשוט פֿראגן, ביישפילן פון וואָס זענען געגעבן אין די דאַקיומענטיישאַן, מיר קענען זען אָדער די לעבן ציקל פון אַ ספּעציפיש ווירטואַל מאַשין אָדער די שטאַט פון אַלע VMs אין אַ זיכער פונט אין צייט.

פֿון אַ פאָרשטעלונג פּערספּעקטיוו, די שרייַבן טראַנסאַקטיאָן צו די הויפּט טיש וועט נישט פאַרענדיקן ביז די שרייַבן טראַנסאַקטיאָן צו די צייַטווייַליק טיש קאַמפּליץ. יענע. אויף טישן מיט אַ גרויס נומער פון שרייַבן אַפּעריישאַנז, די פאַנגקשאַנאַליטי זאָל זיין ימפּלאַמענאַד מיט וואָרענען, אָבער אין אונדזער פאַל, דאָס איז אַ טאַקע קיל זאַך.

כּדי דער מעקאַניזאַם זאָל אַרבעטן ריכטיק, האָב איך געמוזט לייגן אַ קליין שטיק קאָד אין R וואָס וואָלט פאַרגלייכן די נייַע טיש מיט דאַטן פֿאַר אַלע VMs מיט די סטאָרד אין די דאַטאַבייס און שרייַבן בלויז טשיינדזשד ראָוז צו עס. דער קאָד איז נישט דער הויפּט קלוג; עס ניצט די CompareDF ביבליאָטעק, אָבער איך וועל אויך פאָרשטעלן עס אונטן.

ר קאָד פֿאַר שרייבן דאַטן צו אַ דאַטאַבייס

# Подцепляем пакеты
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)

אין גאַנץ

ווי אַ רעזולטאַט פון די ימפּלאַמענטיישאַן פון די שריפט, סדר איז געווען געזונט און מיינטיינד אין אַ ביסל חדשים. מאל פאַלש אָנגעפילט VMs דערשייַנען, אָבער די שריפט סערוועס ווי אַ גוט דערמאָנונג און אַ זעלטן VM קומט אין דער רשימה פֿאַר 2 טעג אין אַ רודערן.

גראָונדוואָרק איז אויך געמאכט פֿאַר אַנאַלייזינג היסטארישע דאַטן.

עס איז קלאָר אַז פיל פון דעם קענען זיין ימפּלאַמענאַד נישט אויף די קני, אָבער מיט ספּעשאַלייזד ווייכווארג, אָבער די אַרבעט איז געווען טשיקאַווע און, איינער זאל זאָגן, אַפּשאַנאַל.

ר האט זיך ווידער באוויזן צו זיין אן אויסגעצייכנטע אוניווערסאלע שפראך, וואס איז גאנץ נישט בלויז צו לייזן סטאַטיסטישע פראבלעמען, נאר אויך ווי א אויסגעצייכנטע "שיכטע" צווישן אנדערע דאַטן קוואלן.

טעגלעך ריפּאָרץ וועגן די געזונט פון ווירטואַל מאשינען ניצן R און PowerShell

מקור: www.habr.com

לייגן אַ באַמערקונג