Ҳисоботи ҳамарӯза дар бораи саломатии мошинҳои виртуалӣ бо истифода аз R ва PowerShell

Ҳисоботи ҳамарӯза дар бораи саломатии мошинҳои виртуалӣ бо истифода аз R ва PowerShell

даромад

Нимирӯзи ба хайр. Дар тӯли ним сол мо як скриптро (ё дурусттараш маҷмӯи скриптҳоро) иҷро карда истодаем, ки гузоришҳоро дар бораи ҳолати мошинҳои виртуалӣ тавлид мекунад (ва на танҳо). Ман тасмим гирифтам, ки таҷрибаи эҷоди худ ва худи кодро мубодила кунам. Умедворам интиқод ва ин мавод шояд барои касе муфид бошад.

Ташаккули эҳтиёҷот

Мо бисёр мошинҳои виртуалӣ дорем (тақрибан 1500 VM дар 3 vCenters тақсим карда шудааст). Навъҳои нав сохта мешаванд ва кӯҳнаҳо зуд-зуд нест карда мешаванд. Барои нигоҳ доштани тартибот, ба vCenter якчанд майдонҳои фармоишӣ илова карда шуданд, то VM-ҳоро ба зерсистемаҳо тақсим кунанд, нишон диҳед, ки оё онҳо озмоишӣ ҳастанд ва аз ҷониби кӣ ва кай офарида шудаанд. Омили инсонй ба он оварда расонд, ки бештар аз нисфи дастгоххо дар сахро холй монда, корро душвор мегардонд. Як маротиба дар шаш моҳ, касе хашмгин шуд ва ба навсозии ин маълумот шурӯъ кард, аммо натиҷа пас аз якуним ҳафта аҳамияти худро гум кард.
Иҷозат диҳед фавран равшан кунам, ки ҳама мефаҳманд, ки барои эҷоди мошинҳо, раванди эҷоди онҳо ва ғайра бояд барномаҳо мавҷуд бошанд. ва ғайра. Ва дар баробари ин хама ба ин процесс катъиян риоя мекунанд ва хама чиз ба тартиб медарояд. Мутаассифона, дар ин ҷо ин тавр нест, аммо ин мавзӯи мақола нест :)

Умуман дар бораи автоматй кунондани тафтиши дурустии пур кардани майдонхо карор кабул карда шуд.
Мо карор додем, ки мактуби харруза бо руйхати дастгоххои нодуруст пуршуда ба хамаи инженерони масъул ва сардорони онхо ибтидои хуб хохад буд.

Дар айни замон, яке аз ҳамкорони ман аллакай скриптро дар PowerShell амалӣ карда буд, ки ҳар рӯз мувофиқи ҷадвал дар бораи ҳамаи мошинҳои ҳамаи vCenters маълумот ҷамъоварӣ мекард ва 3 ҳуҷҷати csv (ҳар кадоме барои vCenter худ) тавлид мекард, ки ба диски умумӣ. Қарор дода шуд, ки ин скриптро ҳамчун асос гирифта, онро бо чекҳо бо истифода аз забони R, ки мо каме таҷриба доштем, пурра кунем.

Дар ҷараёни анҷомёбӣ, ҳалли маълумот тавассути почта, пойгоҳи додаҳо бо ҷадвали асосӣ ва таърихӣ (дар ин бора баъдтар), инчунин таҳлили гузоришҳои vSphere барои дарёфти офаринандагони воқеии vm ва вақти офариниши онҳо ба даст омад.

Барои таҳия IDE RStudio Desktop ва PowerShell ISE истифода мешуданд.

Скрипт аз як мошини муқаррарии виртуалии Windows оғоз карда мешавад.

Тавсифи мантиқи умумӣ.

Мантиқи умумии скриптҳо чунин аст.

  • Мо маълумотро дар мошинаҳои виртуалӣ бо истифода аз скрипти PowerShell ҷамъ меорем, ки мо онро тавассути R даъват мекунем ва натиҷаро ба як csv муттаҳид мекунем. Муносибати баръакси байни забонҳо низ ҳамин тавр сурат мегирад. (ин имкон буд, ки маълумотро мустақиман аз R ба PowerShell дар шакли тағирёбанда интиқол диҳед, аммо ин мушкил аст ва доштани csv-ҳои мобайнӣ ислоҳ ва мубодилаи натиҷаҳои мобайниро бо касе осонтар мекунад).
  • Бо истифода аз R, мо барои майдонҳое, ки арзишҳои онҳоро тафтиш мекунем, параметрҳои дурустро ташкил мекунем. — Мо як ҳуҷҷати калимаро эҷод карда истодаем, ки арзишҳои ин майдонҳоро барои ворид кардан ба номаи иттилоотӣ дар бар гирад, ки он ба саволҳои ҳамкасбон ҷавоб хоҳад дод: "Не, аммо ман инро чӣ гуна бояд пур кунам?"
  • Мо маълумотро барои ҳамаи VM-ҳо аз csv бо истифода аз R бор мекунем, чаҳорчӯбаи додаҳо эҷод мекунем, майдонҳои нолозимро нест мекунем ва ҳуҷҷати иттилоотии xlsx эҷод мекунем, ки дорои маълумоти ҷамъбастӣ барои ҳамаи VMҳо мебошад, ки мо ба манбаи муштарак бор мекунем.
  • Мо ҳама санҷишҳоро барои дурустии пур кардани майдонҳо ба чаҳорчӯбаи додаҳо барои ҳамаи VMҳо татбиқ мекунем ва ҷадвалеро эҷод мекунем, ки танҳо VM-ҳои дорои майдонҳои нодуруст пур карда шудаанд (ва танҳо ин майдонҳо).
  • Мо рӯйхати натиҷавии VM-ро ба скрипти дигари PowerShell мефиристем, ки он ба гузоришҳои vCenter барои рӯйдодҳои эҷоди VM назар мекунад, ки ба мо имкон медиҳад, ки вақти тахминии эҷоди VM ва созандаи пешбинишударо нишон диҳем. Ин барои он ҳолат аст, ки ҳеҷ кас эътироф намекунад, ки мошини кист. Ин скрипт зуд кор намекунад, хусусан агар гузоришҳо зиёд бошанд, аз ин рӯ, мо танҳо дар 2 ҳафтаи охир назар мекунем ва инчунин ҷараёни корро истифода мебарем, ки ба шумо имкон медиҳад, ки дар як вақт маълумотро дар якчанд VM ҷустуҷӯ кунед. Скрипти намунавӣ шарҳҳои муфассалро дар бораи ин механизм дорад. Мо натиҷаро ба csv илова мекунем, ки онро дубора ба R бор мекунем.
  • Мо як ҳуҷҷати зебо форматшудаи xlsx тавлид мекунем, ки дар он майдонҳои нодуруст пуршуда бо ранги сурх таъкид карда мешаванд, филтрҳо ба баъзе сутунҳо татбиқ карда мешаванд ва сутунҳои иловагие, ки эҷодкунандагони пешбинишуда ва вақти сохтани VM нишон дода мешаванд.
  • Мо почтаи электронӣ тавлид мекунем, ки дар он мо ҳуҷҷатеро замима мекунем, ки арзишҳои саҳроии дурустро тавсиф мекунанд ва инчунин ҷадвалеро, ки дар майдонҳо нодуруст пур карда шудаанд, замима мекунем. Дар матн мо шумораи умумии VM-ҳои нодуруст сохташуда, истинод ба манбаи муштарак ва тасвири ҳавасмандиро нишон медиҳем. Агар VM-ҳои нодуруст пур карда нашуда бошанд, мо мактуби дигарро бо тасвири ҳавасмандии хушбахттар мефиристем.
  • Мо маълумотро барои ҳама VM-ҳо дар базаи SQL Server бо назардошти механизми татбиқшудаи ҷадвалҳои таърихӣ сабт мекунем (механизми хеле ҷолиб - дар бораи он дертар)

Дар асл скриптҳо

Файли асосии 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")

Скрипт барои гирифтани рӯйхати 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"

Китобхона сазовори диккати махсус аст 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

Инчунин як нозуки ҷолиб дар бораи насб кардани нақшаи 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 - ҷадвали муваққатии системавӣ - ба кӯмак расид. Он одатан ҳамчун ҷадвалҳои муваққатӣ (на муваққатӣ) тарҷума карда мешавад.

Муфассал дар зер хонед ҳуҷҷатҳои расмии Microsoft.

Хулоса, мо ҷадвал месозем, мегӯем, ки мо онро бо версиягузорӣ хоҳем дошт ва SQL Server дар ин ҷадвал 2 сутуни иловагии санаи вақт (санаи эҷоди сабт ва санаи анҷоми сабт) ва ҷадвали иловагиеро, ки дар он тағирот ворид мешавад, эҷод мекунад. навишта мешавад. Дар натиҷа, мо маълумоти муосир мегирем ва тавассути дархостҳои оддӣ, ки намунаҳои онҳо дар ҳуҷҷатҳо оварда шудаанд, мо метавонем ё давраи ҳаёти як мошини мушаххаси виртуалӣ ё ҳолати ҳама VM-ро дар як нуқтаи муайян бубинем. дар вакташ.

Аз нуқтаи назари иҷроиш, амалиёти навиштан ба ҷадвали асосӣ то анҷоми амалиёти навиштан ба ҷадвали муваққатӣ анҷом намеёбад. Онхое. дар ҷадвалҳое, ки шумораи зиёди амалиёти навиштан доранд, ин функсия бояд бо эҳтиёт иҷро карда шавад, аммо дар ҳолати мо ин як чизи воқеан аҷиб аст.

Барои дуруст кор кардани механизм, ба ман лозим омад, ки як пораи хурди кодро дар R илова кунам, ки ҷадвали навро бо маълумоти ҳама VM-ҳо бо ҷадвали дар пойгоҳи додаҳо нигоҳ дошташуда муқоиса кунад ва ба он танҳо сатрҳои тағирёфтаро нависад. Рамз махсусан доно нест; он китобхонаи 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

Манбаъ: will.com

Илова Эзоҳ