របាយការណ៍ប្រចាំថ្ងៃអំពីសុខភាពរបស់ម៉ាស៊ីននិម្មិតដោយប្រើ R និង PowerShell

របាយការណ៍ប្រចាំថ្ងៃអំពីសុខភាពរបស់ម៉ាស៊ីននិម្មិតដោយប្រើ R និង PowerShell

ធាតុ

អរុណសួស្តី។ អស់រយៈពេលកន្លះឆ្នាំមកនេះ យើងបានដំណើរការស្គ្រីប (ឬជាសំណុំនៃស្គ្រីប) ដែលបង្កើតរបាយការណ៍អំពីស្ថានភាពនៃម៉ាស៊ីននិម្មិត (មិនត្រឹមតែប៉ុណ្ណោះ)។ ខ្ញុំបានសម្រេចចិត្តចែករំលែកបទពិសោធន៍នៃការបង្កើតរបស់ខ្ញុំ និងកូដខ្លួនឯង។ ខ្ញុំសង្ឃឹមសម្រាប់ការរិះគន់ ហើយសម្ភារៈនេះអាចមានប្រយោជន៍សម្រាប់នរណាម្នាក់។

ការបង្កើតតម្រូវការ

យើងមានម៉ាស៊ីននិម្មិតជាច្រើន (ប្រហែល 1500 VMs ចែកចាយនៅទូទាំង 3 vCenters)។ ថ្មី​ត្រូវ​បាន​បង្កើត ហើយ​របស់​ចាស់​ត្រូវ​បាន​លុប​ជា​ញឹក​ញាប់។ ដើម្បីរក្សាសណ្តាប់ធ្នាប់ វាលផ្ទាល់ខ្លួនជាច្រើនត្រូវបានបន្ថែមទៅ vCenter ដើម្បីបែងចែក VMs ទៅក្នុងប្រព័ន្ធរង បង្ហាញថាតើពួកគេជាអ្នកសាកល្បង ឬដោយនរណា និងពេលណាដែលពួកគេត្រូវបានបង្កើត។ កត្តាមនុស្សនាំឱ្យការពិតដែលថាជាងពាក់កណ្តាលនៃម៉ាស៊ីនត្រូវបានទុកចោលដោយវាលទទេដែលធ្វើឱ្យស្មុគស្មាញដល់ការងារ។ រៀងរាល់ប្រាំមួយខែម្តង នរណាម្នាក់បានភ្ញាក់ផ្អើល ហើយចាប់ផ្តើមធ្វើការអាប់ដេតទិន្នន័យនេះ ប៉ុន្តែលទ្ធផលបានឈប់ពាក់ព័ន្ធបន្ទាប់ពីមួយសប្តាហ៍កន្លះ។
ខ្ញុំ​សូម​បញ្ជាក់​ភ្លាម​ថា​អ្នក​រាល់​គ្នា​យល់​ថា​ត្រូវ​តែ​មាន​កម្មវិធី​សម្រាប់​បង្កើត​ម៉ាស៊ីន ដំណើរការ​សម្រាប់​ការ​បង្កើត​របស់​វា​ជាដើម។ ល​ល។ ហើយក្នុងពេលជាមួយគ្នានេះមនុស្សគ្រប់គ្នាធ្វើតាមយ៉ាងតឹងរ៉ឹងនូវដំណើរការនេះហើយអ្វីគ្រប់យ៉ាងគឺស្ថិតនៅក្នុងលំដាប់។ ជាអកុសល នេះមិនមែនជាករណីនៅទីនេះទេ ប៉ុន្តែនេះមិនមែនជាប្រធានបទនៃអត្ថបទ :)

ជាទូទៅការសម្រេចចិត្តត្រូវបានធ្វើឡើងដើម្បីធ្វើស្វ័យប្រវត្តិកម្មពិនិត្យភាពត្រឹមត្រូវនៃការបំពេញនៅក្នុងវាល។
យើងបានសម្រេចចិត្តថាសំបុត្រប្រចាំថ្ងៃដែលមានបញ្ជីម៉ាស៊ីនដែលបំពេញមិនត្រឹមត្រូវទៅកាន់វិស្វករដែលមានទំនួលខុសត្រូវទាំងអស់ និងចៅហ្វាយរបស់ពួកគេនឹងក្លាយជាការចាប់ផ្តើមដ៏ល្អ។

នៅពេលនេះ សហសេវិករបស់ខ្ញុំម្នាក់បានអនុវត្តស្គ្រីបនៅក្នុង PowerShell រួចហើយ ដែលជារៀងរាល់ថ្ងៃ យោងតាមកាលវិភាគ ប្រមូលព័ត៌មាននៅលើម៉ាស៊ីនទាំងអស់នៃ vCenters ទាំងអស់ និងបានបង្កើតឯកសារ csv ចំនួន 3 (នីមួយៗសម្រាប់ vCenter របស់វាផ្ទាល់) ដែលត្រូវបានបង្ហោះទៅ ថាសធម្មតា។ វាត្រូវបានសម្រេចចិត្តយកស្គ្រីបនេះជាមូលដ្ឋាន ហើយបន្ថែមវាជាមួយនឹងការត្រួតពិនិត្យដោយប្រើភាសា R ដែលយើងមានបទពិសោធន៍ខ្លះ។

នៅក្នុងដំណើរការនៃការបញ្ចប់ ដំណោះស្រាយទទួលបានព័ត៌មានតាមសំបុត្រ មូលដ្ឋានទិន្នន័យដែលមានតារាងសំខាន់ និងប្រវត្តិសាស្រ្ត (បន្ថែមលើនេះនៅពេលក្រោយ) ក៏ដូចជាការវិភាគនៃកំណត់ហេតុ vSphere ដើម្បីស្វែងរកអ្នកបង្កើត vm ពិតប្រាកដ និងពេលវេលានៃការបង្កើតរបស់ពួកគេ។

IDE RStudio Desktop និង PowerShell ISE ត្រូវបានប្រើប្រាស់សម្រាប់ការអភិវឌ្ឍន៍។

ស្គ្រីបត្រូវបានចាប់ផ្តើមពីម៉ាស៊ីននិម្មិតវីនដូធម្មតា។

ការពិពណ៌នាអំពីតក្កវិជ្ជាទូទៅ។

តក្កវិជ្ជាទូទៅនៃស្គ្រីបមានដូចខាងក្រោម។

  • យើងប្រមូលទិន្នន័យនៅលើម៉ាស៊ីននិម្មិតដោយប្រើស្គ្រីប PowerShell ដែលយើងហៅតាមរយៈ R ហើយបញ្ចូលលទ្ធផលទៅជា csv មួយ។ អន្តរកម្មបញ្ច្រាសរវាងភាសាត្រូវបានធ្វើស្រដៀងគ្នា។ (វាអាចទៅរួចក្នុងការជំរុញទិន្នន័យដោយផ្ទាល់ពី R ទៅ PowerShell ក្នុងទម្រង់ជាអថេរ ប៉ុន្តែវាជាការលំបាក ហើយការមាន csvs កម្រិតមធ្យមធ្វើឱ្យវាកាន់តែងាយស្រួលក្នុងការបំបាត់កំហុស និងចែករំលែកលទ្ធផលកម្រិតមធ្យមជាមួយនរណាម្នាក់)។
  • ដោយប្រើ R យើងបង្កើតប៉ារ៉ាម៉ែត្រត្រឹមត្រូវសម្រាប់វាលដែលតម្លៃដែលយើងកំពុងពិនិត្យ។ - យើងកំពុងបង្កើតឯកសារពាក្យដែលនឹងមានតម្លៃនៃវាលទាំងនេះសម្រាប់បញ្ចូលទៅក្នុងលិខិតព័ត៌មាន ដែលនឹងក្លាយជាចម្លើយចំពោះសំណួររបស់សហសេវិក "ទេ ប៉ុន្តែតើខ្ញុំគួរបំពេញវាដោយរបៀបណា?"
  • យើងផ្ទុកទិន្នន័យសម្រាប់ VMs ទាំងអស់ពី csv ដោយប្រើ R បង្កើត dataframe លុបវាលដែលមិនចាំបាច់ និងបង្កើតឯកសារ xlsx ពត៌មានដែលនឹងមានព័ត៌មានសង្ខេបសម្រាប់ VMs ទាំងអស់ដែលយើងផ្ទុកឡើងទៅកាន់ធនធានដែលបានចែករំលែក។
  • យើងអនុវត្តការត្រួតពិនិត្យទាំងអស់សម្រាប់ភាពត្រឹមត្រូវនៃការបំពេញក្នុងវាលទៅស៊ុមទិន្នន័យសម្រាប់ VMs ទាំងអស់ ហើយបង្កើតតារាងដែលមានតែ VMs ដែលមានវាលដែលបំពេញមិនត្រឹមត្រូវ (ហើយមានតែវាលទាំងនេះប៉ុណ្ណោះ)។
  • យើងផ្ញើបញ្ជីលទ្ធផលនៃ VM ទៅកាន់ស្គ្រីប PowerShell មួយផ្សេងទៀត ដែលនឹងពិនិត្យមើលកំណត់ហេតុ vCenter សម្រាប់ព្រឹត្តិការណ៍បង្កើត VM ដែលនឹងអនុញ្ញាតឱ្យយើងបង្ហាញពីពេលវេលាប៉ាន់ស្មាននៃការបង្កើត VM និងអ្នកបង្កើតដែលមានបំណង។ នេះ​ជា​ករណី​ដែល​គ្មាន​អ្នក​ណា​ទទួល​ស្គាល់​ថា​ជា​រថយន្ត​របស់​អ្នក​ណា​ឡើយ។ ស្គ្រីបនេះមិនដំណើរការលឿនទេ ជាពិសេសប្រសិនបើមានកំណត់ហេតុច្រើន ដូច្នេះយើងមើលទៅត្រឹមតែ 2 សប្តាហ៍ចុងក្រោយប៉ុណ្ណោះ ហើយក៏ប្រើលំហូរការងារដែលអនុញ្ញាតឱ្យអ្នកស្វែងរកព័ត៌មាននៅលើ VMs ជាច្រើនក្នុងពេលដំណាលគ្នា។ ស្គ្រីបឧទាហរណ៍មានមតិយោបល់លម្អិតលើយន្តការនេះ។ យើងបន្ថែមលទ្ធផលទៅក្នុង csv ដែលយើងបញ្ចូលម្តងទៀតទៅក្នុង R ។
  • យើងបង្កើតឯកសារ xlsx ដែលមានទ្រង់ទ្រាយយ៉ាងស្រស់ស្អាត ដែលវាលដែលបំពេញមិនត្រឹមត្រូវនឹងត្រូវបានបន្លិចជាពណ៌ក្រហម តម្រងនឹងត្រូវបានអនុវត្តចំពោះជួរឈរមួយចំនួន ហើយជួរឈរបន្ថែមដែលមានអ្នកបង្កើតដែលមានបំណង និងពេលវេលានៃការបង្កើត VM នឹងត្រូវបានចង្អុលបង្ហាញ។
  • យើងបង្កើតអ៊ីមែល ដែលយើងភ្ជាប់ឯកសារដែលពិពណ៌នាអំពីតម្លៃវាលត្រឹមត្រូវ ក៏ដូចជាតារាងដែលបំពេញដោយមិនត្រឹមត្រូវ។ នៅក្នុងអត្ថបទ យើងបង្ហាញពីចំនួនសរុបនៃ VMs ដែលត្រូវបានបង្កើតមិនត្រឹមត្រូវ តំណភ្ជាប់ទៅកាន់ធនធានដែលបានចែករំលែក និងរូបភាពលើកទឹកចិត្ត។ ប្រសិនបើមិនមាន VMs ដែលបំពេញមិនត្រឹមត្រូវទេ យើងផ្ញើលិខិតមួយទៀតជាមួយនឹងរូបភាពលើកទឹកចិត្តដ៏រីករាយ។
  • យើងកត់ត្រាទិន្នន័យសម្រាប់ VMs ទាំងអស់នៅក្នុងមូលដ្ឋានទិន្នន័យ SQL Server ដោយគិតគូរពីយន្តការដែលបានអនុវត្តនៃតារាងប្រវត្តិសាស្រ្ត (យន្តការគួរឱ្យចាប់អារម្មណ៍ខ្លាំងណាស់ - អំពីអ្វីដែលនៅពេលក្រោយ)

តាមពិតស្គ្រីប

ឯកសារកូដ R ចម្បង

# Путь к рабочей директории (нужно для корректной работы через виндовый планировщик заданий)
setwd("C:ScriptsgetVm")
#### Подгружаем необходимые пакеты ####
library(tidyverse)
library(xlsx)
library(mailR)
library(rmarkdown)
##### Определяем пути к исходным файлам и другие переменные #####
source(file = "const.R", local = T, encoding = "utf-8")
# Проверяем существование файла со всеми ВМ и удаляем, если есть.
if (file.exists(filenameVmCreationRules)) {file.remove(filenameVmCreationRules)}
#### Создаём вордовский документ с допустимыми полями
render("VM_name_rules.Rmd",
output_format = word_document(),
output_file = filenameVmCreationRules)
# Проверяем существование файла со всеми ВМ и удаляем, если есть
if (file.exists(allVmXlsxPath)) {file.remove(allVmXlsxPath)}
#### Забираем данные по всем машинам через PowerShell скрипт. На выходе получим csv.
system(paste0("powershell -File ", getVmPsPath))
# Полный df
fullXslx_df <- allVmXlsxPath %>% 
read.csv2(stringsAsFactors = FALSE)
# Проверяем корректность заполненных полей
full_df <- fullXslx_df %>%
mutate(
# Сначала убираем все лишние пробелы и табуляции, потом учитываем разделитель запятую, потом проверяем вхождение в допустимые значения,
isSubsystemCorrect = Subsystem %>% 
gsub("[[:space:]]", "", .) %>% 
str_split(., ",") %>% 
map(function(x) (all(x %in% AllowedValues$Subsystem))) %>%
as.logical(),
isOwnerCorrect = Owner %in% AllowedValues$Owner,
isCategoryCorrect = Category %in% AllowedValues$Category,
isCreatorCorrect = (!is.na(Creator) & Creator != ''),
isCreation.DateCorrect = map(Creation.Date, IsDate)
)
# Проверяем существование файла со всеми ВМ и удаляем, если есть.
if (file.exists(filenameAll)) {file.remove(filenameAll)}
#### Формируем xslx файл с отчётом ####
# Общие данные на отдельный лист
full_df %>% write.xlsx(file=filenameAll,
sheetName=names[1],
col.names=TRUE,
row.names=FALSE,
append=FALSE)
#### Формируем xslx файл с неправильно заполненными полями ####
# Формируем df
incorrect_df <- full_df %>%
select(VM.Name, 
IP.s, 
Owner,
Subsystem,
Creator,
Category,
Creation.Date,
isOwnerCorrect, 
isSubsystemCorrect, 
isCategoryCorrect,
isCreatorCorrect,
vCenter.Name) %>%
filter(isSubsystemCorrect == F | 
isOwnerCorrect == F |
isCategoryCorrect == F |
isCreatorCorrect == F)
# Проверяем существование файла со всеми ВМ и удаляем, если есть.
if (file.exists(filenameIncVM)) {file.remove(filenameIncVM)}
# Сохраняем список VM с незаполненными полями в csv
incorrect_df %>%
select(VM.Name) %>%
write_csv2(path = filenameIncVM, append = FALSE)
# Фильтруем для вставки в почту
incorrect_df_filtered <- incorrect_df %>% 
select(VM.Name, 
IP.s, 
Owner, 
Subsystem, 
Category,
Creator,
vCenter.Name,
Creation.Date
)
# Считаем количество строк
numberOfRows <- nrow(incorrect_df)
#### Начало условия ####
# Дальше либо у нас есть неправильно заполненные поля, либо нет.
# Если есть - запускаем ещё один скрипт
if (numberOfRows > 0) {
# Проверяем существование файла с создателями и удаляем, если есть.
if (file.exists(creatorsFilePath)) {file.remove(creatorsFilePath)}
# Запускаем PowerShell скрипт, который найдёт создателей найденных VM. На выходе получим csv.
system(paste0("powershell -File ", getCreatorsPath))
# Читаем файл с создателями
creators_df <- creatorsFilePath %>%
read.csv2(stringsAsFactors = FALSE)
# Фильтруем для вставки в почту, добавляем данные из таблицы с создателями
incorrect_df_filtered <- incorrect_df_filtered %>% 
select(VM.Name, 
IP.s, 
Owner, 
Subsystem, 
Category,
Creator,
vCenter.Name,
Creation.Date
) %>% 
left_join(creators_df, by = "VM.Name") %>% 
rename(`Предполагаемый создатель` = CreatedBy, 
`Предполагаемая дата создания` = CreatedOn)  
# Формируем тело письма
emailBody <- paste0(
'<html>
<h3>Добрый день, уважаемые коллеги.</h3>
<p>Полную актуальную информацию по виртуальным машинам вы можете посмотреть на диске H: вот тут:<p>
<p>server.ruVM', sourceFileFormat, '</p>
<p>Также во вложении список ВМ с <strong>некорректно заполненными</strong> полями. Всего их <strong>', numberOfRows,'</strong>.</p>
<p>В таблице появилось 2 дополнительные колонки. <strong>Предполагаемый создатель</strong> и <strong>Предполагаемая дата создания</strong>, которые достаются из логов vCenter за последние 2 недели</p>
<p>Просьба создателей машин уточнить данные и заполнить поля корректно. Правила заполнения полей также во вложении</p>
<p><img src="data/meme.jpg"></p>
</html>'
)
# Проверяем существование файла
if (file.exists(filenameIncorrect)) {file.remove(filenameIncorrect)}
# Формируем красивую таблицу с форматами и т.д.
source(file = "email.R", local = T, encoding = "utf-8")
#### Формируем письмо с плохо подписанными машинами ####
send.mail(from = emailParams$from,
to = emailParams$to,
subject = "ВМ с некорректно заполненными полями",
body = emailBody,
encoding = "utf-8",
html = TRUE,
inline = TRUE,
smtp = emailParams$smtpParams,
authenticate = TRUE,
send = TRUE,
attach.files = c(filenameIncorrect, filenameVmCreationRules),
debug = FALSE)
#### Дальше пойдёт блок, если нет проблем с ВМ ####
} else {
# Формируем тело письма
emailBody <- paste0(
'<html>
<h3>Добрый день, уважаемые коллеги</h3>
<p>Полную актуальную информацию по виртуальным машинам вы можете посмотреть на диске H: вот тут:<p>
<p>server.ruVM', sourceFileFormat, '</p>
<p>Также, на текущий момент, все поля ВМ корректно заполнены</p>
<p><img src="data/meme_correct.jpg"></p>
</html>'
)
#### Формируем письмо без плохо заполненных VM ####
send.mail(from = emailParams$from,
to = emailParams$to,
subject = "Сводная информация",
body = emailBody,
encoding = "utf-8",
html = TRUE,
inline = TRUE,
smtp = emailParams$smtpParams,
authenticate = TRUE,
send = TRUE,
debug = FALSE)
}
####### Записываем данные в БД #####
source(file = "DB.R", local = T, encoding = "utf-8")

ស្គ្រីបសម្រាប់ការទទួលបានបញ្ជី vm នៅក្នុង PowerShell

# Данные для подключения и другие переменные
$vCenterNames = @(
"vcenter01", 
"vcenter02", 
"vcenter03"
)
$vCenterUsername = "myusername"
$vCenterPassword = "mypassword"
$filename = "C:ScriptsgetVmdataallvmall-vm-$(get-date -f yyyy-MM-dd).csv"
$destinationSMB = "server.rumyfolder$vm"
$IP0=""
$IP1=""
$IP2=""
$IP3=""
$IP4=""
$IP5=""
# Подключение ко всем vCenter, что содержатся в переменной. Будет работать, если логин и пароль одинаковые (например, доменные)
Connect-VIServer -Server $vCenterNames -User $vCenterUsername -Password $vCenterPassword
write-host ""
# Создаём функцию с циклом по всем vCenter-ам
function Get-VMinventory {
# В этой переменной будет списко всех ВМ, как объектов
$AllVM = Get-VM | Sort Name
$cnt = $AllVM.Count
$count = 1
# Начинаем цикл по всем ВМ и собираем необходимые параметры каждого объекта
foreach ($vm in $AllVM) {
$StartTime = $(get-date)
$IP0 = $vm.Guest.IPAddress[0]
$IP1 = $vm.Guest.IPAddress[1]
$IP2 = $vm.Guest.IPAddress[2]
$IP3 = $vm.Guest.IPAddress[3]
$IP4 = $vm.Guest.IPAddress[4]
$IP5 = $vm.Guest.IPAddress[5]
If ($IP0 -ne $null) {If ($IP0.Contains(":") -ne 0) {$IP0=""}}
If ($IP1 -ne $null) {If ($IP1.Contains(":") -ne 0) {$IP1=""}}
If ($IP2 -ne $null) {If ($IP2.Contains(":") -ne 0) {$IP2=""}}
If ($IP3 -ne $null) {If ($IP3.Contains(":") -ne 0) {$IP3=""}}
If ($IP4 -ne $null) {If ($IP4.Contains(":") -ne 0) {$IP4=""}}
If ($IP5 -ne $null) {If ($IP5.Contains(":") -ne 0) {$IP5=""}}
$cluster = $vm | Get-Cluster | Select-Object -ExpandProperty name  
$Bootime = $vm.ExtensionData.Runtime.BootTime
$TotalHDDs = $vm.ProvisionedSpaceGB -as [int]
$CreationDate = $vm.CustomFields.Item("CreationDate") -as [string]
$Creator = $vm.CustomFields.Item("Creator") -as [string]
$Category = $vm.CustomFields.Item("Category") -as [string]
$Owner = $vm.CustomFields.Item("Owner") -as [string]
$Subsystem = $vm.CustomFields.Item("Subsystem") -as [string]
$IPS = $vm.CustomFields.Item("IP") -as [string]
$vCPU = $vm.NumCpu
$CorePerSocket = $vm.ExtensionData.config.hardware.NumCoresPerSocket
$Sockets = $vCPU/$CorePerSocket
$Id = $vm.Id.Split('-')[2] -as [int]
# Собираем все параметры в один объект
$Vmresult = New-Object PSObject
$Vmresult | add-member -MemberType NoteProperty -Name "Id" -Value $Id   
$Vmresult | add-member -MemberType NoteProperty -Name "VM Name" -Value $vm.Name  
$Vmresult | add-member -MemberType NoteProperty -Name "Cluster" -Value $cluster  
$Vmresult | add-member -MemberType NoteProperty -Name "Esxi Host" -Value $VM.VMHost  
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 1" -Value $IP0
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 2" -Value $IP1
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 3" -Value $IP2
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 4" -Value $IP3
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 5" -Value $IP4
$Vmresult | add-member -MemberType NoteProperty -Name "IP Address 6" -Value $IP5
$Vmresult | add-member -MemberType NoteProperty -Name "vCPU" -Value $vCPU
$Vmresult | Add-Member -MemberType NoteProperty -Name "CPU Sockets" -Value $Sockets
$Vmresult | Add-Member -MemberType NoteProperty -Name "Core per Socket" -Value $CorePerSocket
$Vmresult | add-member -MemberType NoteProperty -Name "RAM (GB)" -Value $vm.MemoryGB
$Vmresult | add-member -MemberType NoteProperty -Name "Total-HDD (GB)" -Value $TotalHDDs
$Vmresult | add-member -MemberType NoteProperty -Name "Power State" -Value $vm.PowerState
$Vmresult | add-member -MemberType NoteProperty -Name "OS" -Value $VM.ExtensionData.summary.config.guestfullname  
$Vmresult | Add-Member -MemberType NoteProperty -Name "Boot Time" -Value $Bootime
$Vmresult | add-member -MemberType NoteProperty -Name "VMTools Status" -Value $vm.ExtensionData.Guest.ToolsStatus  
$Vmresult | add-member -MemberType NoteProperty -Name "VMTools Version" -Value $vm.ExtensionData.Guest.ToolsVersion  
$Vmresult | add-member -MemberType NoteProperty -Name "VMTools Version Status" -Value $vm.ExtensionData.Guest.ToolsVersionStatus  
$Vmresult | add-member -MemberType NoteProperty -Name "VMTools Running Status" -Value $vm.ExtensionData.Guest.ToolsRunningStatus  
$Vmresult | add-member -MemberType NoteProperty -Name "Creation Date" -Value $CreationDate
$Vmresult | add-member -MemberType NoteProperty -Name "Creator" -Value $Creator
$Vmresult | add-member -MemberType NoteProperty -Name "Category" -Value $Category
$Vmresult | add-member -MemberType NoteProperty -Name "Owner" -Value $Owner
$Vmresult | add-member -MemberType NoteProperty -Name "Subsystem" -Value $Subsystem
$Vmresult | add-member -MemberType NoteProperty -Name "IP's" -Value $IPS
$Vmresult | add-member -MemberType NoteProperty -Name "vCenter Name" -Value $vm.Uid.Split('@')[1].Split(':')[0]  
# Считаем общее и оставшееся время выполнения и выводим на экран результаты. Использовалось для тестирования, но по факту оказалось очень удобно.
$elapsedTime = $(get-date) - $StartTime
$totalTime = "{0:HH:mm:ss}" -f ([datetime]($elapsedTime.Ticks*($cnt - $count)))
clear-host
Write-Host "Processing" $count "from" $cnt 
Write-host "Progress:" ([math]::Round($count/$cnt*100, 2)) "%" 
Write-host "You have about " $totalTime "for cofee"
Write-host ""
$count++
# Выводим результат, чтобы цикл "знал" что является результатом выполнения одного прохода
$Vmresult
}
}
# Вызываем получившуюся функцию и сразу выгружаем результат в csv
$allVm = Get-VMinventory | Export-CSV -Path $filename -NoTypeInformation -UseCulture -Force
# Пытаемся выложить полученный файл в нужное нам место и, в случае ошибки, пишем лог.
try
{
Copy-Item $filename -Destination $destinationSMB -Force -ErrorAction SilentlyContinue
}
catch
{
$error | Export-CSV -Path $filename".error" -NoTypeInformation -UseCulture -Force
}

ស្គ្រីប PowerShell ដែលស្រង់ចេញពីកំណត់ហេតុអ្នកបង្កើតម៉ាស៊ីននិម្មិត និងកាលបរិច្ឆេទនៃការបង្កើតរបស់ពួកគេ។

# Путь к файлу, из которого будем доставать список VM
$VMfilePath = "C:ScriptsgetVmcreators_VMcreators_VM_$(get-date -f yyyy-MM-dd).csv"
# Путь к файлу, в который будем записывать результат
$filePath = "C:ScriptsgetVmdatacreatorscreators-$(get-date -f yyyy-MM-dd).csv"
# Создаём вокрфлоу
Workflow GetCreators-Wf
{
# Параметры, которые можно будет передать при вызове скрипта
param([string[]]$VMfilePath)
# Параметры, которые доступны только внутри workflow
$vCenterUsername = "myusername"
$vCenterPassword = "mypassword"
$daysToLook = 14
$start = (get-date).AddDays(-$daysToLook)
$finish = get-date
# Значения, которые будут вписаны в csv для машин, по которым не будет ничего найдено
$UnknownUser = "UNKNOWN"
$UnknownCreatedTime = "0000-00-00"
# Определяем параметры подключения и выводной файл, которые будут доступны во всём скрипте.
$vCenterNames = @(
"vcenter01", 
"vcenter02", 
"vcenter03"
)
# Получаем список VM из csv и загружаем соответствующие объекты
$list = Import-Csv $VMfilePath -UseCulture | select -ExpandProperty VM.Name
# Цикл, который будет выполняться параллельно (по 5 машин за раз)
foreach -parallel ($row in $list)
{
# Это скрипт, который видит только свои переменные и те, которые ему переданы через $Using
InlineScript {
# Время начала выполнения отдельного блока
$StartTime = $(get-date)
Write-Host ""
Write-Host "Processing $Using:row started at $StartTime"
Write-Host ""
# Подключение оборачиваем в переменную, чтобы информация о нём не мешалась в консоли
$con = Connect-VIServer -Server $Using:vCenterNames -User $Using:vCenterUsername -Password $Using:vCenterPassword
# Получаем объект vm
$vm = Get-VM -Name $Using:row
# Ниже 2 одинаковые команды. Одна с фильтром по времени, вторая - без. Можно пользоваться тем,
$Event = $vm | Get-VIEvent -Start $Using:start -Finish $Using:finish -Types Info | Where { $_.Gettype().Name -eq "VmBeingDeployedEvent" -or $_.Gettype().Name -eq "VmCreatedEvent" -or $_.Gettype().Name -eq "VmRegisteredEvent" -or $_.Gettype().Name -eq "VmClonedEvent"}
# $Event = $vm | Get-VIEvent -Types Info | Where { $_.Gettype().Name -eq "VmBeingDeployedEvent" -or $_.Gettype().Name -eq "VmCreatedEvent" -or $_.Gettype().Name -eq "VmRegisteredEvent" -or $_.Gettype().Name -eq "VmClonedEvent"}
# Заполняем параметры в зависимости от того, удалось ли в логах найти что-то
If (($Event | Measure-Object).Count -eq 0){
$User = $Using:UnknownUser
$Created = $Using:UnknownCreatedTime
$CreatedFormat = $Using:UnknownCreatedTime
} Else {
If ($Event.Username -eq "" -or $Event.Username -eq $null) {
$User = $Using:UnknownUser
} Else {
$User = $Event.Username
} # Else
$CreatedFormat = $Event.CreatedTime
# Один из коллег отдельно просил, чтобы время было в таком формате, поэтому дублируем его. А в БД пойдёт нормальный формат.
$Created = $Event.CreatedTime.ToString('yyyy-MM-dd')
} # Else
Write-Host "Creator for $vm is $User. Creating object."
# Создаём объект. Добавляем параметры.
$Vmresult = New-Object PSObject
$Vmresult | add-member -MemberType NoteProperty -Name "VM Name" -Value $vm.Name  
$Vmresult | add-member -MemberType NoteProperty -Name "CreatedBy" -Value $User
$Vmresult | add-member -MemberType NoteProperty -Name "CreatedOn" -Value $CreatedFormat
$Vmresult | add-member -MemberType NoteProperty -Name "CreatedOnFormat" -Value $Created           
# Выводим результаты
$Vmresult
} # Inline
} # ForEach
}
$Creators = GetCreators-Wf $VMfilePath
# Записываем результат в файл
$Creators | select 'VM Name', CreatedBy, CreatedOn | Export-Csv -Path $filePath -NoTypeInformation -UseCulture -Force
Write-Host "CSV generetion finisghed at $(get-date). PROFIT"

បណ្ណាល័យសមនឹងទទួលបានការយកចិត្តទុកដាក់ជាពិសេស xlsx ។ដែលធ្វើឱ្យវាអាចធ្វើទៅបានដើម្បីធ្វើឱ្យឯកសារភ្ជាប់ទៅលិខិតនោះមានទម្រង់យ៉ាងច្បាស់ (ជាការចូលចិត្តរបស់អ្នកគ្រប់គ្រង) ហើយមិនត្រឹមតែជាតារាង CSV ប៉ុណ្ណោះទេ។

ការបង្កើតឯកសារ xlsx ដ៏ស្រស់ស្អាតជាមួយនឹងបញ្ជីនៃម៉ាស៊ីនដែលបំពេញមិនត្រឹមត្រូវ

# Создаём новую книгу
# Возможные значения : "xls" и "xlsx"
wb<-createWorkbook(type="xlsx")
# Стили для имён рядов и колонок в таблицах
TABLE_ROWNAMES_STYLE <- CellStyle(wb) + Font(wb, isBold=TRUE)
TABLE_COLNAMES_STYLE <- CellStyle(wb) + Font(wb, isBold=TRUE) +
Alignment(wrapText=TRUE, horizontal="ALIGN_CENTER") +
Border(color="black", position=c("TOP", "BOTTOM"), 
pen=c("BORDER_THIN", "BORDER_THICK"))
# Создаём новый лист
sheet <- createSheet(wb, sheetName = names[2])
# Добавляем таблицу
addDataFrame(incorrect_df_filtered, 
sheet, startRow=1, startColumn=1,  row.names=FALSE, byrow=FALSE,
colnamesStyle = TABLE_COLNAMES_STYLE,
rownamesStyle = TABLE_ROWNAMES_STYLE)
# Меняем ширину, чтобы форматирование было автоматическим
autoSizeColumn(sheet = sheet, colIndex=c(1:ncol(incorrect_df)))
# Добавляем фильтры
addAutoFilter(sheet, cellRange = "C1:G1")
# Определяем стиль
fo2 <- Fill(foregroundColor="red")
cs2 <- CellStyle(wb, 
fill = fo2, 
dataFormat = DataFormat("@"))
# Находим ряды с неверно заполненным полем Владельца и применяем к ним определённый стиль
rowsOwner <- getRows(sheet, rowIndex = (which(!incorrect_df$isOwnerCorrect) + 1))
cellsOwner <- getCells(rowsOwner, colIndex = which( colnames(incorrect_df_filtered) == "Owner" )) 
lapply(names(cellsOwner), function(x) setCellStyle(cellsOwner[[x]], cs2))
# Находим ряды с неверно заполненным полем Подсистемы и применяем к ним определённый стиль
rowsSubsystem <- getRows(sheet, rowIndex = (which(!incorrect_df$isSubsystemCorrect) + 1))
cellsSubsystem <- getCells(rowsSubsystem, colIndex = which( colnames(incorrect_df_filtered) == "Subsystem" )) 
lapply(names(cellsSubsystem), function(x) setCellStyle(cellsSubsystem[[x]], cs2))
# Аналогично по Категории
rowsCategory <- getRows(sheet, rowIndex = (which(!incorrect_df$isCategoryCorrect) + 1))
cellsCategory <- getCells(rowsCategory, colIndex = which( colnames(incorrect_df_filtered) == "Category" )) 
lapply(names(cellsCategory), function(x) setCellStyle(cellsCategory[[x]], cs2))
# Создатель
rowsCreator <- getRows(sheet, rowIndex = (which(!incorrect_df$isCreatorCorrect) + 1))
cellsCreator <- getCells(rowsCreator, colIndex = which( colnames(incorrect_df_filtered) == "Creator" )) 
lapply(names(cellsCreator), function(x) setCellStyle(cellsCreator[[x]], cs2))
# Сохраняем файл
saveWorkbook(wb, filenameIncorrect)

លទ្ធផលមើលទៅដូចនេះ៖

របាយការណ៍ប្រចាំថ្ងៃអំពីសុខភាពរបស់ម៉ាស៊ីននិម្មិតដោយប្រើ R និង PowerShell

វាក៏មានចំណុចគួរឱ្យចាប់អារម្មណ៍មួយផងដែរអំពីការដំឡើងកម្មវិធីកំណត់ពេលវីនដូ។ វាមិនអាចទៅរួចទេក្នុងការស្វែងរកសិទ្ធិ និងការកំណត់ត្រឹមត្រូវ ដូច្នេះអ្វីៗនឹងចាប់ផ្តើមដូចដែលវាគួរតែ។ ជាលទ្ធផល បណ្ណាល័យ R ត្រូវបានរកឃើញ ដែលខ្លួនវាបង្កើតភារកិច្ចដើម្បីបើកដំណើរការអក្សរ R និងមិនភ្លេចអំពីឯកសារកំណត់ហេតុ។ បន្ទាប់មកអ្នកអាចកែតម្រូវភារកិច្ចដោយដៃ។

បំណែកនៃលេខកូដ R ដែលមានឧទាហរណ៍ពីរដែលបង្កើតភារកិច្ចនៅក្នុង Windows Scheduler

library(taskscheduleR)
myscript <- file.path(getwd(), "all_vm.R")
## запускаем скрипт через 62 секунды
taskscheduler_create(taskname = "getAllVm", rscript = myscript, 
schedule = "ONCE", starttime = format(Sys.time() + 62, "%H:%M"))
## запускаем скрипт каждый день в 09:10
taskscheduler_create(taskname = "getAllVmDaily", rscript = myscript, 
schedule = "WEEKLY", 
days = c("MON", "TUE", "WED", "THU", "FRI"),
starttime = "02:00")
## удаляем задачи
taskscheduler_delete(taskname = "getAllVm")
taskscheduler_delete(taskname = "getAllVmDaily")
# Смотрим логи (последние 4 строчки)
tail(readLines("all_vm.log"), sep ="n", n = 4)

ដោយឡែកពីគ្នាអំពីមូលដ្ឋានទិន្នន័យ

បន្ទាប់ពីដំឡើងស្គ្រីបបញ្ហាផ្សេងទៀតបានចាប់ផ្តើមលេចឡើង។ ជាឧទាហរណ៍ ខ្ញុំចង់ស្វែងរកកាលបរិច្ឆេទដែល VM ត្រូវបានលុប ប៉ុន្តែកំណត់ហេតុនៅក្នុង vCenter បានអស់ហើយ។ ដោយសារស្គ្រីបដាក់ឯកសារក្នុងថតជារៀងរាល់ថ្ងៃ ហើយមិនសម្អាតវា (យើងសម្អាតវាដោយដៃរបស់យើងនៅពេលយើងចងចាំ) អ្នកអាចរកមើលឯកសារចាស់ៗ ហើយស្វែងរកឯកសារដំបូងដែល VM នេះមិនមានវត្តមាន។ ប៉ុន្តែវាមិនត្រជាក់ទេ។

ខ្ញុំចង់បង្កើតមូលដ្ឋានទិន្នន័យប្រវត្តិសាស្ត្រ។

មុខងាររបស់ MS SQL SERVER - តារាងបណ្ដោះអាសន្នកំណែប្រព័ន្ធ - បានមកជួយសង្គ្រោះ។ ជាធម្មតាវាត្រូវបានបកប្រែជាតារាងបណ្តោះអាសន្ន (មិនមែនបណ្តោះអាសន្ន)។

អ្នកអាចអានលម្អិតនៅ ឯកសារផ្លូវការរបស់ Microsoft.

សរុបមក យើងបង្កើតតារាងមួយ និយាយថាយើងនឹងមានវាជាមួយនឹងកំណែ ហើយ SQL Server បង្កើត 2 datetime columns បន្ថែមក្នុងតារាងនេះ (កាលបរិច្ឆេទដែលកំណត់ត្រាត្រូវបានបង្កើត និងកាលបរិច្ឆេទផុតកំណត់នៃ record) និងតារាងបន្ថែមដែលផ្លាស់ប្តូរ នឹងត្រូវបានសរសេរ។ ជាលទ្ធផល យើងទទួលបានព័ត៌មានទាន់សម័យ ហើយតាមរយៈសំណួរសាមញ្ញ ឧទាហរណ៍ដែលត្រូវបានផ្តល់ឱ្យនៅក្នុងឯកសារ យើងអាចមើលឃើញទាំងវដ្តជីវិតរបស់ម៉ាស៊ីននិម្មិតជាក់លាក់ ឬស្ថានភាពនៃ VMs ទាំងអស់នៅចំណុចជាក់លាក់មួយ។ នៅក្នុងពេលវេលា។

តាមទស្សនៈនៃការអនុវត្ត ប្រតិបត្តិការសរសេរទៅតារាងមេនឹងមិនបញ្ចប់ទេ រហូតដល់ប្រតិបត្តិការសរសេរទៅតារាងបណ្តោះអាសន្នបានបញ្ចប់។ ទាំងនោះ។ នៅលើតារាងដែលមានប្រតិបត្តិការសរសេរច្រើន មុខងារនេះគួរតែត្រូវបានអនុវត្តដោយប្រុងប្រយ័ត្ន ប៉ុន្តែក្នុងករណីរបស់យើង វាពិតជាល្អណាស់។

ដើម្បីឱ្យយន្តការដំណើរការបានត្រឹមត្រូវ ខ្ញុំត្រូវបន្ថែមកូដតូចមួយនៅក្នុង R ដែលនឹងប្រៀបធៀបតារាងថ្មីជាមួយនឹងទិន្នន័យសម្រាប់ VMs ទាំងអស់ជាមួយនឹងមួយដែលបានរក្សាទុកក្នុងមូលដ្ឋានទិន្នន័យ ហើយសរសេរតែជួរដេកដែលបានផ្លាស់ប្តូរទៅវា។ កូដមិនឆ្លាតជាពិសេសវាប្រើបណ្ណាល័យប្រៀបធៀបDF ប៉ុន្តែខ្ញុំនឹងបង្ហាញវាខាងក្រោម។

កូដ 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)

សរុប

ជាលទ្ធផលនៃការអនុវត្តស្គ្រីបការបញ្ជាទិញត្រូវបានស្ដារឡើងវិញនិងរក្សាក្នុងរយៈពេលពីរបីខែ។ ពេលខ្លះ VMs ដែលត្រូវបានបំពេញមិនត្រឹមត្រូវលេចឡើង ប៉ុន្តែស្គ្រីបដើរតួជាការរំលឹកដ៏ល្អ ហើយ VM ដ៏កម្រនឹងចូលទៅក្នុងបញ្ជីរយៈពេល 2 ថ្ងៃជាប់គ្នា។

ការងារមូលដ្ឋានក៏ត្រូវបានបង្កើតឡើងសម្រាប់ការវិភាគទិន្នន័យប្រវត្តិសាស្ត្រផងដែរ។

វាច្បាស់ណាស់ថាភាគច្រើននៃនេះអាចត្រូវបានអនុវត្តមិនមែននៅលើជង្គង់នោះទេប៉ុន្តែជាមួយនឹងកម្មវិធីឯកទេសប៉ុន្តែភារកិច្ចគឺគួរឱ្យចាប់អារម្មណ៍ហើយមនុស្សម្នាក់អាចនិយាយថាជាជម្រើស។

R បានបង្ហាញខ្លួនឯងម្តងទៀតថាជាភាសាសកលដ៏ល្អឥតខ្ចោះ ដែលមិនត្រឹមតែអាចដោះស្រាយបញ្ហាស្ថិតិប៉ុណ្ណោះទេ ប៉ុន្តែថែមទាំងដើរតួជា "ស្រទាប់" ដ៏ល្អរវាងប្រភពទិន្នន័យផ្សេងទៀត។

របាយការណ៍ប្រចាំថ្ងៃអំពីសុខភាពរបស់ម៉ាស៊ីននិម្មិតដោយប្រើ R និង PowerShell

ប្រភព: www.habr.com

បន្ថែមមតិយោបល់