Báo cáo hàng ngày về tình trạng của máy ảo sử dụng R và PowerShell

Báo cáo hàng ngày về tình trạng của máy ảo sử dụng R và PowerShell

Nhập

Chào buổi chiều. Trong nửa năm nay, chúng tôi đã chạy một tập lệnh (hay đúng hơn là một tập lệnh) tạo báo cáo về trạng thái của máy ảo (và không chỉ). Tôi quyết định chia sẻ kinh nghiệm sáng tạo của mình và chính mã đó. Tôi hy vọng những lời chỉ trích và tài liệu này có thể hữu ích cho ai đó.

Sự hình thành nhu cầu

Chúng tôi có rất nhiều máy ảo (khoảng 1500 máy ảo được phân phối trên 3 vCenter). Những cái mới được tạo ra và những cái cũ bị xóa khá thường xuyên. Để duy trì trật tự, một số trường tùy chỉnh đã được thêm vào vCenter để phân chia máy ảo thành các Hệ thống con, cho biết chúng có phải là máy thử nghiệm hay không, chúng được tạo bởi ai và khi nào. Yếu tố con người đã khiến hơn một nửa số máy móc bị bỏ trống, khiến công việc trở nên phức tạp. Sáu tháng một lần, lại có người lo lắng và bắt đầu cập nhật dữ liệu này, nhưng kết quả không còn phù hợp sau một tuần rưỡi.
Hãy để tôi nói rõ ngay rằng mọi người đều hiểu rằng phải có các ứng dụng để tạo ra máy móc, quy trình tạo ra chúng, v.v. và như thế. Đồng thời, mọi người đều tuân thủ nghiêm ngặt quy trình này và mọi việc đều diễn ra theo đúng trình tự. Thật không may, đây không phải là trường hợp ở đây, nhưng đây không phải là chủ đề của bài viết :)

Nói chung, quyết định được đưa ra là tự động hóa việc kiểm tra tính chính xác của việc điền vào các trường.
Chúng tôi quyết định rằng một lá thư hàng ngày với danh sách các máy được điền sai cho tất cả các kỹ sư có trách nhiệm và ông chủ của họ sẽ là một khởi đầu tốt.

Tại thời điểm này, một trong những đồng nghiệp của tôi đã triển khai một tập lệnh trong PowerShell, theo lịch trình, mỗi ngày sẽ thu thập thông tin trên tất cả các máy của tất cả các vCenter và tạo ra 3 tài liệu csv (mỗi tài liệu cho vCenter riêng), được tải lên một đĩa chung. Chúng tôi đã quyết định lấy tập lệnh này làm cơ sở và bổ sung nó bằng các kiểm tra bằng ngôn ngữ R mà chúng tôi đã có một số kinh nghiệm.

Trong quá trình hoàn thiện, giải pháp đã thu thập thông tin qua thư, cơ sở dữ liệu với bảng chính và lịch sử (sẽ nói thêm về điều này sau), cũng như phân tích nhật ký vSphere để tìm ra người tạo thực sự của vm và thời điểm tạo ra chúng.

IDE RStudio Desktop và PowerShell ISE đã được sử dụng để phát triển.

Tập lệnh được khởi chạy từ máy ảo Windows thông thường.

Mô tả logic tổng quát

Logic chung của các tập lệnh như sau.

  • Chúng tôi thu thập dữ liệu trên các máy ảo bằng tập lệnh PowerShell mà chúng tôi gọi thông qua R và kết hợp kết quả vào một csv. Sự tương tác ngược giữa các ngôn ngữ được thực hiện tương tự. (có thể truyền dữ liệu trực tiếp từ R sang PowerShell dưới dạng biến, nhưng điều này rất khó và việc có csv trung gian giúp gỡ lỗi và chia sẻ kết quả trung gian với ai đó dễ dàng hơn).
  • Sử dụng R, chúng tôi tạo các tham số hợp lệ cho các trường có giá trị mà chúng tôi đang kiểm tra. — Chúng tôi đang tạo một tài liệu word chứa các giá trị của các trường này để chèn vào thư thông tin, đây sẽ là câu trả lời cho câu hỏi của đồng nghiệp “Không, nhưng tôi nên điền thông tin này như thế nào?”
  • Chúng tôi tải dữ liệu cho tất cả VM từ csv bằng R, tạo khung dữ liệu, xóa các trường không cần thiết và tạo tài liệu xlsx thông tin sẽ chứa thông tin tóm tắt cho tất cả VM mà chúng tôi tải lên tài nguyên được chia sẻ.
  • Chúng tôi áp dụng tất cả các kiểm tra về tính chính xác của việc điền các trường vào khung dữ liệu cho tất cả các VM và tạo một bảng chỉ chứa các VM có các trường được điền không chính xác (và chỉ các trường này).
  • Chúng tôi gửi danh sách kết quả các máy ảo đến một tập lệnh PowerShell khác. Tập lệnh này sẽ xem xét nhật ký vCenter cho các sự kiện tạo máy ảo, điều này sẽ cho phép chúng tôi chỉ ra thời gian ước tính tạo máy ảo và người tạo dự kiến. Đây là trường hợp không ai thừa nhận đó là xe của ai. Tập lệnh này không hoạt động nhanh, đặc biệt nếu có nhiều nhật ký, vì vậy chúng tôi chỉ xem xét 2 tuần qua và cũng sử dụng quy trình làm việc cho phép bạn tìm kiếm thông tin trên một số máy ảo cùng một lúc. Tập lệnh mẫu chứa các nhận xét chi tiết về cơ chế này. Chúng tôi thêm kết quả vào một csv, rồi tải lại vào R.
  • Chúng tôi tạo một tài liệu xlsx được định dạng đẹp mắt, trong đó các trường điền không chính xác sẽ được đánh dấu màu đỏ, các bộ lọc sẽ được áp dụng cho một số cột và các cột bổ sung chứa người tạo dự định và thời gian tạo VM sẽ được chỉ định.
  • Chúng tôi tạo một email, trong đó chúng tôi đính kèm một tài liệu mô tả các giá trị trường hợp lệ, cũng như một bảng có các trường được điền không chính xác. Trong văn bản, chúng tôi chỉ ra tổng số máy ảo được tạo không chính xác, liên kết đến tài nguyên được chia sẻ và hình ảnh động lực. Nếu không có máy ảo nào được điền sai, chúng tôi sẽ gửi một lá thư khác với hình ảnh động lực vui vẻ hơn.
  • Chúng tôi ghi lại dữ liệu cho tất cả các máy ảo trong cơ sở dữ liệu SQL Server, có tính đến cơ chế triển khai của các bảng lịch sử (một cơ chế rất thú vị - sẽ nói thêm về điều này sau)

Trên thực tế kịch bản

Tệp mã R chính

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

Tập lệnh lấy danh sách vm trong 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
}

Tập lệnh PowerShell trích xuất từ ​​nhật ký của người tạo máy ảo và ngày tạo máy ảo

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

Thư viện xứng đáng được quan tâm đặc biệt xlsx, điều này giúp có thể tạo tệp đính kèm với chữ cái được định dạng rõ ràng (theo ý muốn của quản lý) chứ không chỉ là bảng CSV.

Tạo tài liệu xlsx đẹp với danh sách các máy được điền không chính xác

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

Đầu ra trông giống như thế này:

Báo cáo hàng ngày về tình trạng của máy ảo sử dụng R và PowerShell

Ngoài ra còn có một sắc thái thú vị về việc thiết lập bộ lập lịch Windows. Không thể tìm được các quyền và cài đặt phù hợp để mọi thứ bắt đầu như bình thường. Kết quả là, thư viện R đã được tìm thấy, thư viện này tự tạo tác vụ khởi chạy tập lệnh R và thậm chí không quên tệp nhật ký. Sau đó, bạn có thể sửa tác vụ theo cách thủ công.

Một đoạn mã R với hai ví dụ tạo tác vụ trong Bộ lập lịch Windows

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)

Riêng về cơ sở dữ liệu

Sau khi thiết lập xong kịch bản, các vấn đề khác bắt đầu xuất hiện. Ví dụ: tôi muốn tìm ngày VM bị xóa, nhưng nhật ký trong vCenter đã cũ. Vì tập lệnh đặt các tệp vào một thư mục hàng ngày và không dọn dẹp nó (chúng tôi sẽ xóa nó bằng tay khi nhớ ra), nên bạn có thể xem qua các tệp cũ và tìm thấy tệp đầu tiên không có VM này. Nhưng điều đó không hay chút nào.

Tôi muốn tạo một cơ sở dữ liệu lịch sử.

Chức năng của MS SQL SERVER - bảng tạm thời được phiên bản hệ thống - đã được giải cứu. Nó thường được dịch là các bảng tạm thời (không phải tạm thời).

Bạn có thể đọc chi tiết tại tài liệu chính thức của Microsoft.

Nói tóm lại, chúng tôi tạo một bảng, giả sử rằng chúng tôi sẽ có nó với phiên bản và SQL Server tạo thêm 2 cột ngày giờ trong bảng này (ngày tạo bản ghi và ngày hết hạn của bản ghi) và một bảng bổ sung có những thay đổi Sẽ được viết. Do đó, chúng tôi nhận được thông tin cập nhật và thông qua các truy vấn đơn giản, ví dụ được đưa ra trong tài liệu, chúng tôi có thể thấy vòng đời của một máy ảo cụ thể hoặc trạng thái của tất cả các máy ảo tại một thời điểm nhất định đúng giờ.

Từ góc độ hiệu suất, giao dịch ghi vào bảng chính sẽ không hoàn thành cho đến khi giao dịch ghi vào bảng tạm thời hoàn tất. Những thứ kia. trên các bảng có số lượng lớn thao tác ghi, chức năng này cần được triển khai một cách thận trọng, nhưng trong trường hợp của chúng tôi, đó thực sự là một điều thú vị.

Để cơ chế hoạt động chính xác, tôi phải thêm một đoạn mã nhỏ vào R để so sánh bảng mới với dữ liệu của tất cả các máy ảo với bảng được lưu trữ trong cơ sở dữ liệu và chỉ ghi các hàng đã thay đổi vào đó. Mã này không đặc biệt thông minh; nó sử dụng thư viện so sánh, nhưng tôi cũng sẽ trình bày nó bên dưới.

Mã R để ghi dữ liệu vào cơ sở dữ liệu

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

trong tổng số

Nhờ việc thực hiện kịch bản, trật tự đã được khôi phục và duy trì trong vòng vài tháng. Đôi khi các máy ảo được điền không chính xác xuất hiện nhưng tập lệnh đóng vai trò như một lời nhắc hữu ích và một máy ảo hiếm hoi xuất hiện trong danh sách trong 2 ngày liên tiếp.

Nền tảng cũng được thực hiện để phân tích dữ liệu lịch sử.

Rõ ràng là phần lớn việc này có thể được thực hiện không phải bằng đầu gối mà bằng phần mềm chuyên dụng, nhưng nhiệm vụ này rất thú vị và có thể nói là tùy chọn.

R một lần nữa chứng tỏ mình là một ngôn ngữ phổ quát xuất sắc, không chỉ hoàn hảo để giải quyết các vấn đề thống kê mà còn hoạt động như một “lớp” xuất sắc giữa các nguồn dữ liệu khác.

Báo cáo hàng ngày về tình trạng của máy ảo sử dụng R và PowerShell

Nguồn: www.habr.com

Thêm một lời nhận xét