R እና PowerShellን በመጠቀም ስለ ምናባዊ ማሽኖች ሁኔታ ዕለታዊ ዘገባዎች

R እና PowerShellን በመጠቀም ስለ ምናባዊ ማሽኖች ሁኔታ ዕለታዊ ዘገባዎች

ግቤት

እንደምን አረፈድክ. ለግማሽ ዓመት ያህል, ስለ ምናባዊ ማሽኖች ሁኔታ (እና ብቻ ሳይሆን) ሪፖርቶችን የሚያመነጭ ስክሪፕት (በይበልጥ በትክክል, የስክሪፕት ስብስብ) እየሰራን ነው. የመፍጠር ልምዴን እና ኮዱን እራሱ ለማካፈል ወሰንኩ። እኔ ትችት እና ይህ ቁሳቁስ ለአንድ ሰው ጠቃሚ ሊሆን ይችላል በሚለው እውነታ ላይ እቆጥራለሁ.

የፍላጎት ምስረታ

ብዙ ምናባዊ ማሽኖች አሉን (በ1500 vCenters ላይ ወደ 3 ቪኤምዎች ተሰራጭተዋል)። አዳዲሶች ይፈጠራሉ እና አሮጌዎቹ ብዙ ጊዜ ይሰረዛሉ። ሥርዓትን ለማስጠበቅ፣ VMን ወደ ንኡስ ሲስተሞች ለመከፋፈል፣ የተፈተኑ መሆናቸውን እና እንዲሁም በማንና መቼ እንደተፈጠሩ ለማመልከት ብዙ ብጁ መስኮች ወደ vCenter ተጨምረዋል። የሰው ልጅ መንስኤው ከግማሽ በላይ የሚሆኑት ማሽኖች ባዶ ቦታዎች እንዲቀሩ ምክንያት ሆኗል, ይህም ስራውን አወሳሰበ. በየግማሽ ዓመቱ አንድ ሰው ተበሳጨ፣ እነዚህን መረጃዎች በማዘመን ላይ መሥራት ጀመረ፣ ነገር ግን ውጤቱ ከአንድ ሳምንት ተኩል በኋላ ጠቃሚ መሆን አቆመ።
ሁሉም ሰው ማሽኖችን ለመፍጠር ማመልከቻዎች, ለፈጠራቸው ሂደት, ወዘተ መሆን እንዳለበት እንደሚረዳ ወዲያውኑ ግልጽ አደርጋለሁ. እናም ይቀጥላል. እና በተመሳሳይ ጊዜ ሁሉም ሰው ይህን ሂደት በጥብቅ ይከተላል እና ሁሉም ነገር በሥርዓት ነው. በሚያሳዝን ሁኔታ, ይህ በእኛ ላይ አይደለም, ነገር ግን ይህ የጽሁፉ ርዕሰ ጉዳይ አይደለም 🙂

በአጠቃላይ, በመስኮቹ ውስጥ የመሙላት ትክክለኛነት ማረጋገጥ በራስ-ሰር እንዲሰራ ተወስኗል.
ለሚመለከታቸው መሐንዲሶች እና አለቆቻቸው በየእለቱ በስህተት የተጠናቀቁ ማሽኖች ዝርዝር የያዘ ደብዳቤ ጥሩ ጅምር እንዲሆን ወስነናል።

በዚህ ጊዜ፣ ከስራ ባልደረባዬ አንዱ በየእለቱ በሁሉም የvCenters ማሽኖች ላይ መረጃ የሚሰበስብ እና 3 csv ሰነዶችን (እያንዳንዱ ለራሱ vCenter) የሚያመነጨውን የPowerShell ስክሪፕት ተተግብሮ በጋራ ዲስክ ላይ ተዘርግቷል። ይህንን ስክሪፕት እንደ መነሻ ወስዶ የተወሰነ ልምድ ያለው R ቋንቋን በመጠቀም በቼኮች እንዲጨምር ተወስኗል።

በማጠናቀቅ ሂደት ውስጥ መፍትሄው በፖስታ በመረጃ ተሞልቶ ነበር, የውሂብ ጎታ ከዋናው እና ታሪካዊ ሰንጠረዥ ጋር (በኋላ ላይ ተጨማሪ), እንዲሁም የ vSphere ምዝግብ ማስታወሻዎች ትክክለኛ የቪኤም ፈጣሪዎችን እና ጊዜን ለማግኘት ተወስኗል. ተፈጠሩ።

IDE Rstudio Desktop እና PowerShell ISE ለልማት ስራ ላይ ውለው ነበር።

ስክሪፕቱ የተጀመረው ከተለመደው የዊንዶውስ ቨርቹዋል ማሽን ነው።

የአጠቃላይ አመክንዮ መግለጫ.

የስክሪፕቶቹ አጠቃላይ አመክንዮ እንደሚከተለው ነው።

  • በአር በኩል የምንጠራውን የPowerShell ስክሪፕት በመጠቀም በምናባዊ ማሽኖች ላይ መረጃ እንሰበስባለን ፣ ውጤቱን ወደ አንድ ሲኤስቪ እናዋህዳለን። በቋንቋዎች መካከል ያለው የተገላቢጦሽ መስተጋብር በተመሳሳይ መልኩ ይከናወናል. (መረጃን በቀጥታ ከ R ወደ PowerShell በተለዋዋጭ መልክ ማሽከርከር ይቻል ነበር ፣ ግን ይህ ከባድ ነው ፣ እና መካከለኛ csv መኖሩ ለማረም እና መካከለኛ ውጤቶችን ለአንድ ሰው ለማጋራት ቀላል ነው)።
  • R ን በመጠቀም እሴቶቻቸውን ለምናረጋግጥባቸው መስኮች ትክክለኛ መለኪያዎች እንፈጥራለን። - በመረጃ ደብዳቤ ውስጥ ለማስገባት የእነዚህን መስኮች እሴቶች የሚይዝ የቃላት ሰነድ እንፈጥራለን ፣ ይህም ለባልደረባዎች ጥያቄዎች መልስ ይሆናል "ደህና ፣ ይህንን እንዴት መሙላት አለብኝ?"
  • R ን በመጠቀም ለሁሉም ቪኤምዎች ከሲኤስቪ መረጃ እንጭናለን ፣ የውሂብ ፍሬም እንፈጥራለን ፣ አላስፈላጊ መስኮችን እናስወግዳለን እና ለሁሉም ቪኤምዎች ማጠቃለያ መረጃ የያዘ xlsx የመረጃ ሰነድ እንፈጥራለን ፣ ይህም በጋራ መገልገያ ላይ የምንለጥፈው።
  • መስኮቹን የመሙላት ትክክለኛነት ሁሉንም ቼኮች በሁሉም ቪኤምዎች የመረጃ ቋት ላይ እንተገብራለን እና ቪኤምዎችን በስህተት የተሞሉ መስኮች (እና እነዚህ መስኮች ብቻ) የያዘ ሠንጠረዥ እንፈጥራለን።
  • የተገኘውን የቪኤም ዝርዝር ወደ ሌላ የPowerShell ስክሪፕት እንልካለን የvCenter ምዝግብ ማስታወሻዎችን ለ VM ፈጠራ ክስተቶች የሚመለከት ሲሆን ይህም የVM ፈጠራውን እና የተገመተውን ፈጣሪ የሚገመተውን ጊዜ እንዲገልጹ ያስችልዎታል። ይህ የሚሆነው ማንም የማን መኪና እንደሆነ የማይናዘዝ ከሆነ ነው። ይህ ስክሪፕት በፍጥነት አይሰራም, በተለይም ብዙ ምዝግብ ማስታወሻዎች ካሉ, ስለዚህ ያለፉትን 2 ሳምንታት ብቻ እንመለከታለን, እና የስራ ፍሰትን እንጠቀማለን, ይህም በአንድ ጊዜ በበርካታ ቪኤምዎች ላይ መረጃን ለመፈለግ ያስችልዎታል. የናሙና ስክሪፕቱ በዚህ ዘዴ ላይ ዝርዝር አስተያየቶችን ይዟል. ውጤቱ በ csv ላይ ተጨምሯል፣ እሱም እንደገና ወደ አር ተጭኗል።
  • በሚያምር ሁኔታ የተቀረጸ xlsx ሰነድ እንፈጥራለን፣ በስህተት የተሞሉ መስኮች በቀይ የሚደምቁበት፣ ማጣሪያዎች በአንዳንድ ዓምዶች ላይ ይተገበራሉ፣ እና ፈጣሪ ናቸው የተባሉ ተጨማሪ አምዶች እና ቪኤም የተፈጠረበት ጊዜ ይጠቁማሉ።
  • ኢሜል እንፈጥራለን፣ የመስኮቹን የሚፈቀዱ እሴቶች የሚገልጽ ሰነድ እና እንዲሁም በቪኤም ውስጥ በስህተት የተሞላ ሠንጠረዥ እናያይዛለን። በጽሁፉ ውስጥ፣ በትክክል ያልተፈጠሩ ቪኤምኤዎች ጠቅላላ ቁጥር፣ ወደ የተጋራ መገልገያ አገናኝ እና አነቃቂ ምስል እንጠቁማለን። በስህተት የተሞሉ ቪኤምዎች ከሌሉ፣ የበለጠ አስደሳች አነቃቂ ምስል ያለው ሌላ ደብዳቤ እንልካለን።
  • የተተገበረውን የታሪክ ሠንጠረዦች አሠራር ከግምት ውስጥ በማስገባት ለሁሉም ቪኤምዎች ወደ SQL አገልጋይ ዳታቤዝ መረጃ እንጽፋለን (በጣም አስደሳች ዘዴ - በኋላ ላይ የበለጠ በዝርዝር ይብራራል)

በእውነቱ ስክሪፕቶች

ዋና ፋይል ከ 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")

በ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, ይህም በደብዳቤው ላይ ያለውን ዓባሪ በምስል መልክ እንዲቀርጽ (መመሪያው እንደሚወደው) እና የሲኤስቪ ሠንጠረዥ ብቻ ሳይሆን.

ትክክል ባልሆኑ የተሞሉ ማሽኖች ዝርዝር ያለው የሚያምር 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)

ውጽኢቱ ድማ፡ ኣብ ውሽጢ XNUMX ዓ.ም.

R እና PowerShellን በመጠቀም ስለ ምናባዊ ማሽኖች ሁኔታ ዕለታዊ ዘገባዎች

የዊንዶውስ መርሐግብርን በማዘጋጀት ረገድም አንድ አስደሳች ነገር ነበር። ሁሉም ነገር በሚፈለገው መልኩ እንዲጀምር የመብቶችን እና ቅንብሮችን ትክክለኛ መለኪያዎች ማግኘት አልተቻለም። በውጤቱም, የ R ቤተ-መጽሐፍት ተገኝቷል, እሱ ራሱ የ R ስክሪፕትን ለማስኬድ ስራን ይፈጥራል እና ስለ ሎግ ፋይሉ እንኳን አይረሳም. ከዚያ ስራውን እራስዎ ማስተካከል ይችላሉ.

በዊንዶውስ መርሐግብር ውስጥ ሥራን የሚፈጥር ሁለት ምሳሌዎች ያሉት የ R ኮድ ቁራጭ

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 ውስጥ ያሉ ምዝግብ ማስታወሻዎች ቀድሞውኑ ተሽረዋል። ስክሪፕቱ በየቀኑ ፋይሎቹን በፎልደር ውስጥ ስለሚያስቀምጥ እና አያጸዳውም (በምናስታውስበት ጊዜ በእጃችን እናጸዳዋለን) የድሮ ፋይሎችን በማየት ይህን ቪኤም ያልያዘውን የመጀመሪያውን ፋይል ማግኘት እንችላለን። ግን ያ ጥሩ አይደለም።

ታሪካዊ ዳታቤዝ መፍጠር ፈልጌ ነበር።

የ MS SQL SERVER ተግባር ለማዳን መጣ - በስርዓተ-ተኮር ጊዜያዊ ሰንጠረዥ። ብዙውን ጊዜ እንደ ጊዜያዊ (ጊዜያዊ ያልሆነ) ጠረጴዛዎች ይተረጎማል.

ውስጥ በዝርዝር ማንበብ ትችላለህ የማይክሮሶፍት ኦፊሴላዊ ሰነዶች.

ባጭሩ ጠረጴዛን እንፈጥራለን፣ ከስሪት ጋር ይኖረናል እንላለን፣ እና SQL አገልጋይ በዚህ ሠንጠረዥ ውስጥ 2 ተጨማሪ የቀን ጊዜ አምዶችን (የመዝገብ ፍጥረት ቀን እና የመዝገብ ህይወት ማብቂያ ቀን) እና ለውጦች የሚደረጉበት ተጨማሪ ሰንጠረዥ ይፈጥራል። ተፃፈ። በውጤቱም ፣ ወቅታዊ መረጃዎችን እናገኛለን እና ፣ በቀላል መጠይቆች ፣ በሰነዶቹ ውስጥ የተሰጡ ምሳሌዎች ፣ የአንድ የተወሰነ ቨርችዋል ማሽን የሕይወት ዑደት ፣ ወይም የሁሉም ቪኤምኤስ ሁኔታ በ በጊዜ ውስጥ የተወሰነ ነጥብ.

በአፈጻጸም ረገድ, ወደ ዋናው ሠንጠረዥ የመጻፍ ግብይት ወደ ጊዜያዊ ሠንጠረዥ የጽሑፍ ግብይት እስኪጠናቀቅ ድረስ አይጠናቀቅም. እነዚያ። ብዙ ቁጥር ያላቸው የጽሕፈት ሥራዎች ባሉበት ጠረጴዛዎች ላይ ይህ ተግባር በጥንቃቄ መተግበር አለበት ፣ ግን በእኛ ሁኔታ በጣም ጥሩ ነገር ነው።

አሰራሩ በትክክል እንዲሰራ፣ አዲሱን ሰንጠረዥ ከሁሉም ቪኤምዎች መረጃ ጋር በማነፃፀር በመረጃ ቋቱ ውስጥ ከተከማቸው እና የተቀየሩ ረድፎችን ብቻ የሚጽፍ ትንሽ ኮድ ወደ R ማከል አስፈላጊ ነበር። ኮዱ በተለይ አስቸጋሪ አይደለም፣ የንፅፅር ዲኤፍ ቤተ-መጽሐፍትን ይጠቀማል፣ ግን ከዚህ በታችም እሰጣለሁ።

ወደ ዳታቤዝ ውሂብ ለመፃፍ 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)

ԸՆԴՀԱՆՈՒՐ ԳԻՆ

በስክሪፕቱ አተገባበር ምክንያት, በጥቂት ወራት ውስጥ ቅደም ተከተል ተመስርቷል እና ተጠብቆ ቆይቷል. አንዳንድ ጊዜ በስህተት የተሞሉ ቪኤምዎች ይታያሉ፣ ግን ስክሪፕቱ እንደ ጥሩ አስታዋሽ ሆኖ የሚያገለግል ሲሆን ብርቅዬ ቪኤም በተከታታይ 2 ቀናት ውስጥ ወደ ዝርዝሩ ውስጥ ይገባል።

ታሪካዊ መረጃዎችን ለመተንተንም መሰረት ተጥሏል።

ይህ አብዛኛው ሊተገበር የሚችለው "በጉልበቱ ላይ" ሳይሆን በልዩ ሶፍትዌር ነው, ነገር ግን ስራው አስደሳች እና አንድ ሰው እንደ አማራጭ ሊሆን ይችላል.

R በድጋሚ እጅግ በጣም ጥሩ የሆነ ሁለንተናዊ ቋንቋ መሆኑን አረጋግጧል, እሱም እስታቲስቲካዊ ችግሮችን ለመፍታት ብቻ ሳይሆን በሌሎች የውሂብ ምንጮች መካከል እንደ ጥሩ "ጋዝ" ሆኖ ያገለግላል.

R እና PowerShellን በመጠቀም ስለ ምናባዊ ማሽኖች ሁኔታ ዕለታዊ ዘገባዎች

ምንጭ: hab.com

አስተያየት ያክሉ