Adroddiadau dyddiol ar statws peiriannau rhithwir gan ddefnyddio R a PowerShell

Adroddiadau dyddiol ar statws peiriannau rhithwir gan ddefnyddio R a PowerShell

Mynediad

Prynhawn Da. Ers hanner blwyddyn bellach rydym wedi bod yn rhedeg sgript (neu yn hytrach set o sgriptiau) sy'n cynhyrchu adroddiadau ar statws peiriannau rhithwir (ac nid yn unig). Penderfynais rannu fy mhrofiad creu a'r cod ei hun. Rwy'n gobeithio am feirniadaeth ac y gallai'r deunydd hwn fod yn ddefnyddiol i rywun.

Ffurfio angen

Mae gennym lawer o beiriannau rhithwir (tua 1500 VMs wedi'u dosbarthu ar draws 3 vCanolfan). Mae rhai newydd yn cael eu creu a hen rai yn cael eu dileu yn eithaf aml. Er mwyn cadw trefn, ychwanegwyd sawl maes arferiad at vCenter i rannu VMs yn Is-systemau, nodi a ydynt yn rhai prawf, a chan bwy a phryd y cawsant eu creu. Arweiniodd y ffactor dynol at y ffaith bod mwy na hanner y peiriannau wedi'u gadael â meysydd gwag, a oedd yn cymhlethu'r gwaith. Unwaith bob chwe mis, roedd rhywun yn gwegian ac yn dechrau gweithio ar ddiweddaru'r data hwn, ond daeth y canlyniad i ben ar ôl wythnos a hanner.
Gadewch imi egluro ar unwaith bod pawb yn deall bod yn rhaid cael ceisiadau ar gyfer creu peiriannau, proses ar gyfer eu creu, ac ati. ac yn y blaen. Ac ar yr un pryd, mae pawb yn dilyn y broses hon yn llym ac mae popeth mewn trefn. Yn anffodus, nid yw hyn yn wir yma, ond nid dyma destun yr erthygl :)

Yn gyffredinol, penderfynwyd awtomeiddio gwirio cywirdeb llenwi'r meysydd.
Fe wnaethom benderfynu y byddai llythyr dyddiol gyda rhestr o beiriannau wedi'u llenwi'n anghywir i bob peiriannydd cyfrifol a'u penaethiaid yn ddechrau da.

Ar y pwynt hwn, roedd un o fy nghydweithwyr eisoes wedi gweithredu sgript yn PowerShell, a oedd bob dydd, yn ôl amserlen, yn casglu gwybodaeth ar holl beiriannau pob vCenters ac yn cynhyrchu 3 dogfen csv (pob un ar gyfer ei vCenter ei hun), a oedd yn cael eu huwchlwytho i disg cyffredin. Penderfynwyd cymryd y sgript hon fel sail a'i hategu â sieciau gan ddefnyddio'r iaith R, y cawsom rywfaint o brofiad ohoni.

Yn y broses o gwblhau, cafodd yr ateb wybodaeth trwy'r post, cronfa ddata gyda phrif dabl a hanesyddol (mwy am hyn yn ddiweddarach), yn ogystal â dadansoddiad o logiau vSphere i ddod o hyd i wir grewyr vm ac amser eu creu.

Defnyddiwyd IDE RStudio Desktop a PowerShell ISE ar gyfer datblygu.

Mae'r sgript yn cael ei lansio o beiriant rhithwir Windows rheolaidd.

Disgrifiad o'r rhesymeg gyffredinol.

Mae rhesymeg gyffredinol y sgriptiau fel a ganlyn.

  • Rydyn ni'n casglu data ar beiriannau rhithwir gan ddefnyddio sgript PowerShell, rydyn ni'n ei galw trwy R, ac yn cyfuno'r canlyniad yn un csv. Mae'r rhyngweithio gwrthdro rhwng ieithoedd yn cael ei wneud yn yr un modd. (roedd yn bosibl gyrru data yn uniongyrchol o R i PowerShell ar ffurf newidynnau, ond mae hyn yn anodd, ac mae cael csvs canolradd yn ei gwneud hi'n haws dadfygio a rhannu canlyniadau canolradd gyda rhywun).
  • Gan ddefnyddio R, rydym yn ffurfio paramedrau dilys ar gyfer y meysydd y mae eu gwerthoedd yr ydym yn eu gwirio. — Rydym yn creu dogfen Word a fydd yn cynnwys gwerthoedd y meysydd hyn i’w mewnosod yn y llythyr gwybodaeth, a fydd yn ateb i gwestiynau cydweithwyr “Na, ond sut ddylwn i lenwi hon?”
  • Rydym yn llwytho data ar gyfer pob VM o csv gan ddefnyddio R, yn creu ffrâm ddata, yn dileu meysydd diangen ac yn creu dogfen xlsx gwybodaeth a fydd yn cynnwys gwybodaeth gryno ar gyfer pob VM, y byddwn yn ei huwchlwytho i adnodd a rennir.
  • Rydym yn cymhwyso pob gwiriad ar gyfer cywirdeb llenwi'r meysydd i'r ffrâm ddata ar gyfer pob VM ac yn creu tabl sy'n cynnwys dim ond VMs gyda meysydd wedi'u llenwi'n anghywir (a dim ond y meysydd hyn).
  • Rydym yn anfon y rhestr canlyniadol o VMs i sgript PowerShell arall, a fydd yn edrych ar y logiau vCenter ar gyfer digwyddiadau creu VM, a fydd yn caniatáu inni nodi amcangyfrif o amser creu'r VM a'r crëwr arfaethedig. Mae hyn yn wir pan nad oes neb yn cyfaddef car pwy ydyw. Nid yw'r sgript hon yn gweithio'n gyflym, yn enwedig os oes llawer o logiau, felly rydym yn edrych ar y 2 wythnos diwethaf yn unig, a hefyd yn defnyddio llif gwaith sy'n eich galluogi i chwilio am wybodaeth ar sawl VM ar yr un pryd. Mae'r sgript enghreifftiol yn cynnwys sylwadau manwl ar y mecanwaith hwn. Rydyn ni'n ychwanegu'r canlyniad i mewn i csv, ac rydyn ni'n ei lwytho eto i R.
  • Rydym yn cynhyrchu dogfen xlsx wedi'i fformatio'n hyfryd lle bydd meysydd sydd wedi'u llenwi'n anghywir yn cael eu hamlygu mewn coch, bydd hidlwyr yn cael eu gosod ar rai colofnau, a bydd colofnau ychwanegol sy'n cynnwys y crewyr arfaethedig ac amser creu'r VM yn cael eu nodi.
  • Rydym yn cynhyrchu e-bost, lle rydym yn atodi dogfen sy'n disgrifio'r gwerthoedd maes dilys, yn ogystal â thabl gyda meysydd sydd wedi'u llenwi'n anghywir. Yn y testun rydym yn nodi cyfanswm nifer y VMs a grëwyd yn anghywir, dolen i adnodd a rennir a darlun ysgogol. Os nad oes unrhyw VMs wedi'u llenwi'n anghywir, byddwn yn anfon llythyr arall gyda llun ysgogol hapusach.
  • Rydym yn cofnodi data ar gyfer yr holl VMs yng nghronfa ddata SQL Server, gan ystyried mecanwaith gweithredu tablau hanesyddol (mecanwaith diddorol iawn - pa un arall yn ddiweddarach)

Mewn gwirionedd sgriptiau

Prif ffeil cod 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")

Sgript ar gyfer cael rhestr o vm yn 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
}

Sgript PowerShell sy'n echdynnu o'r logiau crewyr peiriannau rhithwir a dyddiad eu creu

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

Mae'r llyfrgell yn haeddu sylw arbennig xlsx, a oedd yn ei gwneud hi'n bosibl gwneud yr atodiad i'r llythyr wedi'i fformatio'n glir (fel y mae'r rheolwyr yn ei hoffi), ac nid tabl CSV yn unig.

Cynhyrchu dogfen xlsx hardd gyda rhestr o beiriannau wedi'u llenwi'n anghywir

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

Mae'r allbwn yn edrych rhywbeth fel hyn:

Adroddiadau dyddiol ar statws peiriannau rhithwir gan ddefnyddio R a PowerShell

Roedd naws ddiddorol hefyd ynglŷn â sefydlu'r amserlen Windows. Roedd yn amhosibl dod o hyd i'r hawliau a'r gosodiadau cywir fel y byddai popeth yn dechrau fel y dylai. O ganlyniad, darganfuwyd y llyfrgell R, sydd ei hun yn creu tasg i lansio sgript R ac nid yw hyd yn oed yn anghofio am y ffeil log. Yna gallwch chi gywiro'r dasg â llaw.

Darn o god R gyda dwy enghraifft sy'n creu tasg yn y 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)

Ar wahân am y gronfa ddata

Ar ôl sefydlu'r sgript, dechreuodd materion eraill ymddangos. Er enghraifft, roeddwn i eisiau dod o hyd i'r dyddiad pan gafodd y VM ei ddileu, ond roedd y logiau yn vCenter eisoes wedi treulio. Gan fod y sgript yn rhoi ffeiliau mewn ffolder bob dydd ac nad yw'n ei lanhau (rydym yn ei lanhau â'n dwylo pan gofiwn), gallwch edrych trwy hen ffeiliau a dod o hyd i'r ffeil gyntaf lle nad yw'r VM hwn yn bresennol. Ond nid yw hynny'n cŵl.

Roeddwn i eisiau creu cronfa ddata hanesyddol.

Daeth ymarferoldeb MS SQL SERVER - tabl amser fersiwn system - i'r adwy. Fel arfer caiff ei gyfieithu fel tablau dros dro (nid dros dro).

Gallwch ddarllen yn fanwl yn dogfennaeth swyddogol Microsoft.

Yn fyr, rydym yn creu tabl, yn dweud y byddwn yn ei gael gyda fersiwn, ac mae SQL Server yn creu 2 golofn dyddiad dyddiad ychwanegol yn y tabl hwn (y dyddiad y crëwyd y cofnod a dyddiad dod i ben y cofnod) a thabl ychwanegol y mae newidiadau ynddo bydd yn cael ei ysgrifennu. O ganlyniad, rydym yn derbyn y wybodaeth ddiweddaraf a, thrwy ymholiadau syml, y rhoddir enghreifftiau ohonynt yn y ddogfennaeth, gallwn weld naill ai cylch bywyd peiriant rhithwir penodol, neu gyflwr pob VM ar adeg benodol. mewn amser.

O safbwynt perfformiad, ni fydd y trafodiad ysgrifennu i'r prif dabl yn cael ei gwblhau nes bod y trafodiad ysgrifennu i'r tabl dros dro wedi'i gwblhau. Y rhai. ar fyrddau gyda nifer fawr o weithrediadau ysgrifennu, dylid gweithredu'r swyddogaeth hon yn ofalus, ond yn ein hachos ni mae'n beth cŵl iawn.

Er mwyn i'r mecanwaith weithio'n gywir, roedd yn rhaid i mi ychwanegu darn bach o god yn R a fyddai'n cymharu'r tabl newydd â data ar gyfer pob VM â'r un sydd wedi'i storio yn y gronfa ddata ac ysgrifennu rhesi wedi'u newid iddo yn unig. Nid yw'r cod yn arbennig o glyfar; mae'n defnyddio'r llyfrgell CompareDF, ond byddaf hefyd yn ei gyflwyno isod.

Cod R ar gyfer ysgrifennu data i gronfa ddata

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

Yn gyfan gwbl

O ganlyniad i weithredu'r sgript, cafodd trefn ei hadfer a'i chynnal o fewn ychydig fisoedd. Weithiau mae VMs wedi'u llenwi'n anghywir yn ymddangos, ond mae'r sgript yn atgof da ac mae VM prin yn mynd i mewn i'r rhestr am 2 ddiwrnod yn olynol.

Gwnaed Groundwork hefyd ar gyfer dadansoddi data hanesyddol.

Mae'n amlwg y gellir gweithredu llawer o hyn nid ar y pen-glin, ond gyda meddalwedd arbenigol, ond roedd y dasg yn ddiddorol ac, efallai, yn ddewisol.

Mae R unwaith eto wedi dangos ei bod yn iaith gyffredinol ragorol, sy'n berffaith nid yn unig ar gyfer datrys problemau ystadegol, ond sydd hefyd yn gweithredu fel “haen” ragorol rhwng ffynonellau data eraill.

Adroddiadau dyddiol ar statws peiriannau rhithwir gan ddefnyddio R a PowerShell

Ffynhonnell: hab.com

Ychwanegu sylw