R va PowerShell-dan foydalangan holda virtual mashinalarning sog'lig'i haqida kundalik hisobotlar

R va PowerShell-dan foydalangan holda virtual mashinalarning sog'lig'i haqida kundalik hisobotlar

kirish

Xayrli kun. Yarim yil davomida biz virtual mashinalar holati (va nafaqat) haqida hisobotlarni yaratadigan skriptni (aniqrog'i skriptlar to'plamini) ishga tushirmoqdamiz. Men yaratish tajribam va kodning o'zi bilan bo'lishishga qaror qildim. Men tanqidga umid qilaman va bu material kimdir uchun foydali bo'lishi mumkin.

Ehtiyojning shakllanishi

Bizda juda ko'p virtual mashinalar mavjud (taxminan 1500 VM 3 vCenter bo'ylab taqsimlangan). Yangilari yaratiladi va eskilari tez-tez o'chiriladi. Tartibni saqlash uchun vCenter-ga VM-larni quyi tizimlarga bo'lish, ularning sinovdan o'tganligini, kim tomonidan va qachon yaratilganligini ko'rsatish uchun bir nechta maxsus maydonlar qo'shildi. Inson omili mashinalarning yarmidan ko'pi bo'sh maydonlar bilan qolib ketishiga olib keldi, bu esa ishni murakkablashtirdi. Har olti oyda bir marta kimdir g'azablanib, ushbu ma'lumotlarni yangilash ustida ishlay boshladi, ammo bir yarim haftadan so'ng natija ahamiyatsiz bo'lib qoldi.
Darhol aniqlab beramanki, hamma mashinalarni yaratish uchun ilovalar, ularni yaratish jarayoni va boshqalar bo'lishi kerakligini tushunadi. va h.k. Va shu bilan birga, har bir kishi bu jarayonga qat'iy rioya qiladi va hamma narsa tartibda. Afsuski, bu erda bunday emas, lekin bu maqolaning mavzusi emas :)

Umuman olganda, maydonlarni to'ldirishning to'g'riligini tekshirishni avtomatlashtirish qarori qabul qilindi.
Biz barcha mas'ul muhandislar va ularning boshliqlariga noto'g'ri to'ldirilgan mashinalar ro'yxati bilan kundalik xat yaxshi boshlanish bo'ladi, deb qaror qildik.

Shu nuqtada, mening hamkasblarimdan biri PowerShell-da allaqachon skriptni amalga oshirgan edi, u har kuni, jadvalga ko'ra, barcha vCenter-larning barcha mashinalarida ma'lumot to'playdi va 3 ta csv hujjatini (har biri o'z vCenter uchun) yaratdi. umumiy disk. Ushbu skriptni asos qilib olishga va uni bizda biroz tajribaga ega bo'lgan R tilidan foydalangan holda cheklar bilan to'ldirishga qaror qilindi.

Yakuniylashtirish jarayonida yechim pochta orqali ma'lumotni, asosiy va tarixiy jadvalga ega ma'lumotlar bazasini (bu haqda keyinroq), shuningdek vSphere jurnallarining tahlilini vm-ning haqiqiy yaratuvchilari va ularni yaratish vaqtini topdi.

Ishlab chiqish uchun IDE RStudio Desktop va PowerShell ISE ishlatilgan.

Skript oddiy Windows virtual mashinasidan ishga tushiriladi.

Umumiy mantiqning tavsifi.

Skriptlarning umumiy mantig'i quyidagicha.

  • Biz virtual mashinalarda ma'lumotlarni PowerShell skripti yordamida yig'amiz, biz uni R orqali chaqiramiz va natijani bitta csvga birlashtiramiz. Tillar orasidagi teskari o'zaro ta'sir xuddi shunday amalga oshiriladi. (ma'lumotlarni to'g'ridan-to'g'ri R-dan PowerShell-ga o'zgaruvchilar ko'rinishida o'tkazish mumkin edi, lekin bu qiyin va oraliq csv-larga ega bo'lish disk raskadrovka va oraliq natijalarni kimdir bilan baham ko'rishni osonlashtiradi).
  • R dan foydalanib, biz qiymatlari tekshirilayotgan maydonlar uchun tegishli parametrlarni hosil qilamiz. — Biz maʼlumot xatiga kiritish uchun ushbu maydonlarning qiymatlarini oʻz ichiga olgan Word hujjatini yaratmoqdamiz, bu hamkasblarning “Yoʻq, lekin buni qanday toʻldirishim kerak?” degan savollariga javob boʻladi.
  • Biz R yordamida csv-dan barcha VM-lar uchun maʼlumotlarni yuklaymiz, maʼlumotlar ramkasini yaratamiz, keraksiz maydonlarni oʻchirib tashlaymiz va umumiy resursga yuklagan barcha VMlar uchun umumiy maʼlumotlarni oʻz ichiga olgan xlsx maʼlumot hujjatini yaratamiz.
  • Biz barcha VMlar uchun ma'lumotlar ramkasiga maydonlarni to'ldirishning to'g'riligi uchun barcha tekshiruvlarni qo'llaymiz va faqat noto'g'ri to'ldirilgan maydonlar (va faqat shu maydonlar) bo'lgan VMlarni o'z ichiga olgan jadval yaratamiz.
  • Olingan VMlar ro'yxatini boshqa PowerShell skriptiga yuboramiz, u VM yaratish hodisalari uchun vCenter jurnallarini ko'rib chiqadi, bu bizga VMni yaratishning taxminiy vaqtini va mo'ljallangan yaratuvchini ko'rsatishga imkon beradi. Bu kimning mashinasi ekanligini hech kim tan olmagan holatlar uchun. Ushbu skript tezda ishlamaydi, ayniqsa jurnallar ko'p bo'lsa, shuning uchun biz faqat oxirgi 2 haftani ko'rib chiqamiz, shuningdek, bir vaqtning o'zida bir nechta VMlar haqida ma'lumot qidirish imkonini beruvchi ish jarayonidan foydalanamiz. Misol skriptida ushbu mexanizm bo'yicha batafsil sharhlar mavjud. Natijani csv ga qo'shamiz, uni yana R ga yuklaymiz.
  • Biz chiroyli formatlangan xlsx hujjatini yaratamiz, unda noto'g'ri to'ldirilgan maydonlar qizil rang bilan ajratib ko'rsatiladi, ba'zi ustunlarga filtrlar qo'llaniladi va mo'ljallangan yaratuvchilarni o'z ichiga olgan qo'shimcha ustunlar va VMni yaratish vaqti ko'rsatiladi.
  • Biz elektron pochta xabarini yaratamiz, u erda biz tegishli maydon qiymatlarini tavsiflovchi hujjatni, shuningdek, noto'g'ri to'ldirilgan maydonlar bilan jadvalni biriktiramiz. Matnda biz noto'g'ri yaratilgan VMlarning umumiy sonini, umumiy manbaga havolani va motivatsion rasmni ko'rsatamiz. Agar noto'g'ri to'ldirilgan VMlar bo'lmasa, biz baxtliroq motivatsion rasm bilan boshqa xat yuboramiz.
  • Biz SQL Server ma'lumotlar bazasidagi barcha VMlar uchun ma'lumotlarni tarixiy jadvallarning amalga oshirilgan mexanizmini hisobga olgan holda yozamiz (juda qiziqarli mexanizm - bu haqda keyinroq)

Aslida skriptlar

Asosiy R kod fayli

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

PowerShell-da vm ro'yxatini olish uchun skript

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

Virtual mashinalar yaratuvchilari va ularning yaratilgan sanasini jurnallardan chiqaradigan PowerShell skripti.

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

Kutubxona alohida e'tiborga loyiqdir xlsx, bu xatga ilovani faqat CSV jadvalini emas, balki aniq formatlangan (boshqaruv xohlaganicha) qilish imkonini berdi.

Noto'g'ri to'ldirilgan mashinalar ro'yxati bilan chiroyli xlsx hujjatini yaratish

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

Chiqish quyidagicha ko'rinadi:

R va PowerShell-dan foydalangan holda virtual mashinalarning sog'lig'i haqida kundalik hisobotlar

Windows rejalashtiruvchisini o'rnatish haqida qiziqarli nuance ham bor edi. Hamma narsa kerakli tarzda boshlanishi uchun to'g'ri huquqlar va sozlamalarni topish mumkin emas edi. Natijada, R kutubxonasi topildi, u o'zi R skriptini ishga tushirish vazifasini yaratadi va hatto jurnal faylini ham unutmaydi. Keyin vazifani qo'lda tuzatishingiz mumkin.

Windows Scheduler-da vazifa yaratadigan ikkita misol bilan R kodining bir qismi

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)

Ma'lumotlar bazasi haqida alohida

Skriptni o'rnatgandan so'ng, boshqa muammolar paydo bo'la boshladi. Misol uchun, men VM o'chirilgan sanani topmoqchi edim, lekin vCenter-dagi jurnallar allaqachon eskirgan. Skript har kuni fayllarni papkaga qo'yadi va uni tozalamagani uchun (eslab qolganimizda uni qo'llarimiz bilan tozalaymiz), siz eski fayllarni ko'rib chiqishingiz va ushbu VM mavjud bo'lmagan birinchi faylni topishingiz mumkin. Lekin bu zo'r emas.

Men tarixiy ma'lumotlar bazasini yaratmoqchi edim.

MS SQL SERVER funksionalligi - tizim versiyasidagi vaqtinchalik jadval yordamga keldi. Odatda vaqtinchalik (vaqtinchalik emas) jadvallar sifatida tarjima qilinadi.

Batafsil o'qishingiz mumkin rasmiy Microsoft hujjatlari.

Xulosa qilib aytganda, biz jadval yaratamiz, aytaylik, biz uni versiyalash bilan olamiz va SQL Server ushbu jadvalda 2 ta qo'shimcha sana va vaqt ustunini (yozuv yaratilgan sana va yozuvning amal qilish muddati) va qo'shimcha jadval yaratadi. yoziladi. Natijada, biz dolzarb ma'lumotlarni olamiz va misollari hujjatlarda keltirilgan oddiy so'rovlar orqali biz ma'lum bir virtual mashinaning hayot aylanishini yoki ma'lum bir nuqtadagi barcha VMlarning holatini ko'rishimiz mumkin. o'z vaqtida.

Ishlash nuqtai nazaridan, asosiy jadvalga yozish operatsiyasi vaqtinchalik jadvalga yozish tranzaktsiyasi tugamaguncha tugallanmaydi. Bular. ko'p sonli yozish operatsiyalari bo'lgan jadvallarda bu funksiya ehtiyotkorlik bilan amalga oshirilishi kerak, ammo bizning holatlarimizda bu juda ajoyib narsa.

Mexanizm to'g'ri ishlashi uchun men R-ga kichik kod qismini qo'shishim kerak edi, u yangi jadvalni ma'lumotlar bazasida saqlangan barcha VMlar uchun ma'lumotlar bilan taqqoslaydi va unga faqat o'zgartirilgan qatorlarni yozadi. Kod juda aqlli emas; u compareDF kutubxonasidan foydalanadi, lekin men uni quyida ham taqdim etaman.

Ma'lumotlar bazasiga ma'lumotlarni yozish uchun R kodi

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

jami

Ssenariyni amalga oshirish natijasida tartib bir necha oy ichida tiklandi va saqlanib qoldi. Ba'zida noto'g'ri to'ldirilgan VMlar paydo bo'ladi, lekin skript yaxshi eslatma bo'lib xizmat qiladi va kamdan-kam uchraydigan VM ketma-ket 2 kun ro'yxatga kiradi.

Tarixiy ma'lumotlarni tahlil qilish uchun ham asos yaratildi.

Buning ko'p qismini tizzada emas, balki maxsus dasturiy ta'minot yordamida amalga oshirish mumkinligi aniq, ammo vazifa qiziqarli va aytish mumkinki, ixtiyoriy edi.

R yana bir bor o'zini ajoyib universal til ekanligini ko'rsatdi, u nafaqat statistik muammolarni hal qilish uchun juda mos keladi, balki boshqa ma'lumotlar manbalari orasida ajoyib "qatlam" vazifasini ham bajaradi.

R va PowerShell-dan foydalangan holda virtual mashinalarning sog'lig'i haqida kundalik hisobotlar

Manba: www.habr.com

a Izoh qo'shish