Rapporti ta’ kuljum dwar is-saħħa tal-magni virtwali li jużaw R u PowerShell

Rapporti ta’ kuljum dwar is-saħħa tal-magni virtwali li jużaw R u PowerShell

Dħul

Il-waranofsinhar it-tajjeb. Ilna nofs sena issa qed inħaddmu script (jew aħjar sett ta’ skripts) li jiġġenera rapporti dwar l-istatus tal-magni virtwali (u mhux biss). Iddeċidejt li naqsam l-esperjenza tal-ħolqien tiegħi u l-kodiċi innifsu. Nittama għall-kritika u li dan il-materjal jista’ jkun utli għal xi ħadd.

Formazzjoni tal-bżonn

Għandna ħafna magni virtwali (madwar 1500 VM mqassma fuq 3 vCenters). Jinħolqu oħrajn ġodda u dawk qodma jitħassru spiss. Biex tinżamm l-ordni, ġew miżjuda diversi oqsma tad-dwana ma' vCenter biex jaqsmu l-VMs f'Subsistemi, jindikaw jekk humiex dawk tat-test, u minn min u meta nħolqu. Il-fattur uman wassal għall-fatt li aktar minn nofs il-magni tħallew b'oqsma vojta, li kkomplika x-xogħol. Darba kull sitt xhur, xi ħadd imbeżża’ u beda jaħdem biex jaġġorna din id-dejta, iżda r-riżultat ma baqax rilevanti wara ġimgħa u nofs.
Ħa niċċara mill-ewwel li kulħadd jifhem li jrid ikun hemm applikazzjonijiet għall-ħolqien ta’ magni, proċess għall-ħolqien tagħhom, eċċ. u l-bqija. U fl-istess ħin, kulħadd isegwi b'mod strett dan il-proċess u kollox huwa fl-ordni. Sfortunatament, dan mhux il-każ hawnhekk, iżda dan mhuwiex is-suġġett tal-artiklu :)

B'mod ġenerali, ittieħdet id-deċiżjoni li jiġi awtomatizzat il-verifika tal-korrettezza tal-mili tal-oqsma.
Iddeċidejna li ittra ta 'kuljum b'lista ta' magni mimlija ħażin lill-inġiniera responsabbli kollha u lill-kapijiet tagħhom tkun bidu tajjeb.

F'dan il-punt, wieħed mill-kollegi tiegħi kien diġà implimenta skript fil-PowerShell, li kuljum, skont skeda, ġabar informazzjoni fuq il-magni kollha tal-vCenters kollha u ġġenera 3 dokumenti csv (kull wieħed għall-vCenter tiegħu), li kienu mtellgħin fuq disk komuni. Ġie deċiż li nieħdu din l-iskrittura bħala bażi u nissupplimentawh b'kontrolli bl-użu tal-lingwa R, li magħha kellna xi esperjenza.

Fil-proċess ta 'finalizzazzjoni, is-soluzzjoni akkwistat informazzjoni bil-posta, database b'tabella prinċipali u storika (aktar dwar dan aktar tard), kif ukoll analiżi ta' zkuk ta 'vSphere biex jinstabu l-ħallieqa attwali ta' vm u l-ħin tal-ħolqien tagħhom.

IDE RStudio Desktop u PowerShell ISE intużaw għall-iżvilupp.

L-iskrittura hija mnedija minn magna virtwali Windows regolari.

Deskrizzjoni tal-loġika ġenerali.

Il-loġika ġenerali tal-iskripts hija kif ġej.

  • Aħna niġbru data fuq magni virtwali billi tuża script PowerShell, li nsejħu permezz ta' R, u ngħaqqdu r-riżultat f'csv wieħed. L-interazzjoni inversa bejn il-lingwi ssir bl-istess mod. (kien possibbli li d-data tissuq direttament minn R għal PowerShell fil-forma ta 'varjabbli, iżda dan huwa diffiċli, u li jkollok csvs intermedji tagħmilha aktar faċli biex tiddibaggja u taqsam riżultati intermedji ma' xi ħadd).
  • Bl-użu ta 'R, aħna niffurmaw parametri validi għall-oqsma li l-valuri tagħhom qed niċċekkjaw. — Qed noħolqu dokument bil-kelma li se jkun fih il-valuri ta’ dawn l-oqsma biex jiddaħħlu fl-ittra ta’ informazzjoni, li se tkun it-tweġiba għall-mistoqsijiet tal-kollegi "Le, imma kif għandi nimla dan?"
  • Aħna tagħbija data għall-VMs kollha minn csv bl-użu ta 'R, noħolqu dataframe, neħħi oqsma mhux meħtieġa u noħolqu dokument ta' informazzjoni xlsx li jkun fih informazzjoni fil-qosor għall-VMs kollha, li aħna ntellgħu għal riżors kondiviż.
  • Aħna napplikaw il-kontrolli kollha għall-korrettezza tal-mili tal-oqsma għad-dataframe għall-VMs kollha u noħolqu tabella li jkun fiha biss VMs b'oqsma mimlija b'mod żbaljat (u dawn l-oqsma biss).
  • Nibagħtu l-lista li tirriżulta ta’ VMs lil script PowerShell ieħor, li se jħares lejn ir-reġistri ta’ vCenter għal avvenimenti ta’ ħolqien ta’ VM, li se jippermettilna nindikaw il-ħin stmat tal-ħolqien tal-VM u l-kreatur maħsub. Dan għall-każ meta ħadd ma jammetti ta’ min hi l-karozza. Dan l-iskript ma jaħdimx malajr, speċjalment jekk ikun hemm ħafna zkuk, għalhekk inħarsu biss lejn l-aħħar ġimgħat 2, u nużaw ukoll fluss tax-xogħol li jippermettilek tfittex informazzjoni fuq diversi VMs fl-istess ħin. L-iskrittura eżempju fiha kummenti dettaljati dwar dan il-mekkaniżmu. Aħna nżidu r-riżultat f'csv, li nerġgħu tagħbija f'R.
  • Aħna niġġeneraw dokument xlsx ifformattjat b'mod sabiħ li fih oqsma mimlija b'mod żbaljat se jiġu enfasizzati bl-aħmar, se jiġu applikati filtri għal xi kolonni, u se jiġu indikati kolonni addizzjonali li jkun fihom il-ħallieqa maħsuba u l-ħin tal-ħolqien tal-VM.
  • Niġġeneraw email, fejn nehmeż dokument li jiddeskrivi l-valuri tal-qasam validi, kif ukoll tabella b'oqsma mimlija b'mod żbaljat. Fit-test aħna nindikaw in-numru totali ta 'VMs maħluqa b'mod żbaljat, link għal riżors kondiviż u stampa motivazzjonali. Jekk ma jkunx hemm VMs mimlija ħażin, aħna nibagħtu ittra oħra bi stampa motivazzjonali aktar ferħana.
  • Aħna nirreġistraw dejta għall-VMs kollha fid-database SQL Server, b'kont meħud tal-mekkaniżmu implimentat ta 'tabelli storiċi (mekkaniżmu interessanti ħafna - li dwaru aktar tard)

Attwalment skripts

Fajl prinċipali tal-kodiċi R

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

Script biex tinkiseb lista ta 'vm fil-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
}

Skript PowerShell li jiġbed mir-zkuk il-ħallieqa tal-magni virtwali u d-data tal-ħolqien tagħhom

# Путь к файлу, из которого будем доставать список 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"

Il-librerija jistħoqqilha attenzjoni speċjali xlsx, li għamilha possibbli li t-twaħħil mal-ittra jkun ifformattjat b'mod ċar (kif jħobb il-maniġment), u mhux biss tabella CSV.

Ġenerazzjoni ta 'dokument xlsx sabiħ b'lista ta' magni mimlija ħażin

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

L-output jidher xi ħaġa bħal din:

Rapporti ta’ kuljum dwar is-saħħa tal-magni virtwali li jużaw R u PowerShell

Kien hemm ukoll sfumatura interessanti dwar it-twaqqif tal-Windows Scheduler. Kien impossibbli li ssib id-drittijiet u s-settings it-tajba biex kollox jibda kif suppost. Bħala riżultat, instabet il-librerija R, li hi stess toħloq kompitu biex tniedi skript R u lanqas biss tinsa dwar il-fajl tal-ġurnal. Imbagħad tista 'tikkoreġi l-kompitu manwalment.

Biċċa kodiċi R b'żewġ eżempji li toħloq kompitu fil-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)

Separatament dwar id-database

Wara li waqqaf l-iskrittura, bdew jidhru kwistjonijiet oħra. Pereżempju, ridt insib id-data meta tħassar il-VM, iżda r-zkuk fil-vCenter kienu diġà mikula. Peress li l-iskript ipoġġi fajls f'folder kuljum u ma naddafx (aħna naddafha b'idejna meta niftakru), tista 'tfittex minn fajls qodma u ssib l-ewwel fajl li fih din il-VM ma tkunx preżenti. Imma dak mhux jibred.

Ridt noħloq database storika.

Il-funzjonalità ta 'MS SQL SERVER - tabella temporali b'verżjoni tas-sistema - waslet għall-salvataġġ. Normalment jiġi tradott bħala tabelli temporanji (mhux temporanji).

Tista' taqra fid-dettall fuq dokumentazzjoni uffiċjali tal-Microsoft.

Fil-qosor, noħolqu tabella, ngħidu li ser ikollnaha b'verżjoni, u SQL Server joħloq 2 kolonni addizzjonali ta 'datetime f'din it-tabella (id-data li fiha nħoloq ir-rekord u d-data ta' skadenza tar-rekord) u tabella addizzjonali li fiha tinbidel se jinkiteb. Bħala riżultat, nirċievu informazzjoni aġġornata u, permezz ta’ mistoqsijiet sempliċi, li eżempji tagħhom jingħataw fid-dokumentazzjoni, nistgħu naraw jew iċ-ċiklu tal-ħajja ta’ magna virtwali speċifika, jew l-istat tal-VMs kollha f’ċertu punt. fil-ħin.

Mill-perspettiva tal-prestazzjoni, it-tranżazzjoni tal-kitba fit-tabella prinċipali mhix se titlesta sakemm titlesta t-tranżazzjoni tal-kitba fit-tabella temporanja. Dawk. fuq tabelli b'numru kbir ta 'operazzjonijiet ta' kitba, din il-funzjonalità għandha tiġi implimentata b'kawtela, iżda fil-każ tagħna hija ħaġa tassew friska.

Sabiex il-mekkaniżmu jaħdem b'mod korrett, kelli nżid biċċa żgħira ta 'kodiċi f'R li tqabbel it-tabella l-ġdida mad-dejta għall-VMs kollha ma' dik maħżuna fid-database u nikteb biss ringieli mibdula magħha. Il-kodiċi mhuwiex partikolarment għaqlija; juża l-librerija compareDF, iżda jien se nippreżentah ukoll hawn taħt.

Kodiċi R għall-kitba ta' data f'database

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

B'kollox

Bħala riżultat tal-implimentazzjoni tal-iskrittura, l-ordni ġiet restawrata u miżmuma fi żmien ftit xhur. Kultant jidhru VMs mimlija ħażin, iżda l-iskrittura sservi bħala tfakkira tajba u VM rari tidħol fil-lista għal jumejn konsekuttivi.

Saret ukoll il-bażi għall-analiżi tad-dejta storika.

Huwa ċar li ħafna minn dan jista 'jiġi implimentat mhux fuq l-irkoppa, iżda b'softwer speċjalizzat, iżda l-kompitu kien interessanti u, wieħed jista' jgħid, fakultattiv.

R għal darb'oħra wera lilu nnifsu bħala lingwa universali eċċellenti, li hija perfetta mhux biss biex issolvi problemi statistiċi, iżda taġixxi wkoll bħala "saff" eċċellenti bejn sorsi ta 'dejta oħra.

Rapporti ta’ kuljum dwar is-saħħa tal-magni virtwali li jużaw R u PowerShell

Sors: www.habr.com

Żid kumment