使用R和PowerShell的虚拟机状态每日报告


参赛作品


大家好 半年以来,我们一直在运行一个脚本(更确切地说是一组脚本),该脚本生成有关虚拟机状态的报告(不仅如此)。 我决定分享创建经验和代码本身。 我指望批评以及该材料可能对某人有用的事实。


需要形成


我们有很多虚拟机(在第三个vCenter上分布了大约1500个VM)。 新的被创建,而旧的被经常删除。 为了保留顺序,在vCenter中添加了几个自定义字段,以将VM划分为多个子系统,指示它们是否在测试中,以及由谁以及何时进行测试。 人为因素导致以下事实:一半以上的汽车留有空旷的场地,这使工作变得复杂。 每六个月一次,有人吓坏了,开始着手更新这些数据,但是结果在一个半星期内不再有意义。
我将立即澄清,每个人都知道应该有用于创建计算机的应用程序,用于创建计算机的过程等。 等 虽然严格遵守了所有这些过程,但是这些都是有序的。 不幸的是,事实并非如此,但这不是本文的主题:)


通常,决定自动执行填充字段正确性的验证。
我们决定每天写一封信,列出所有负责任的工程​​师及其上司不正确填充的机器,这是一个好的开始。


此时,一位同事已经实现了一个PowerShell脚本,该脚本每天按计划收集所有vCenter-s的所有计算机上的信息,并形成3个csv文档(每个都在其自己的vCenter中),这些文档放置在共享磁盘上。 决定以该脚本为基础,并使用我曾经有过使用R语言的检查功能对其进行补充。


在完成过程中,该解决方案过于庞大,无法通过邮件通知,包含主表和历史表的数据库(稍后会详细介绍)以及分析vSphere日志以查找vm的实际创建者及其创建时间。


为了进行开发,我们使用了IDE RStudio桌面和PowerShell ISE。


该脚本是从常规Windows虚拟机启动的。


一般逻辑的描述。


脚本的一般逻辑如下。


  • 我们使用PowerShell脚本(通过R调用)在虚拟机上收集数据,结果被合并到一个csv中。 语言之间的反向交互类似地进行。 (可以以变量的形式将数据直接从R驱动到PowerShell,但这很困难,即使使用中间的csv,也可以更轻松地与某人调试和共享中间结果)。
  • 使用R,我们为要检查其值的字段形成有效的参数。 -我们正在形成一个Word文档,其中将包含这些字段的值,以便插入到信息信函中,这将是对同事问题的答案:“不好,但是我应该如何填写?”
  • 我们使用R从csv加载所有VM上的数据,形成一个数据框,删除不必要的字段,并形成一个信息xlsx文档,其中将包含有关我们上载到共享资源的所有VM的摘要信息。
  • 对于所有VM的数据框,我们将应用所有检查以检查字段填充的正确性,并形成一个表,该表仅包含字段填充错误(仅这些字段)的VM。
  • 虚拟机的结果列表将发送到另一个PowerShell脚本,该脚本将查看vCenter日志中有关虚拟机创建事件的信息,该脚本将允许您指定虚拟机的创建时间和预期的创建者。 当没有人认罪谁的车时就是这种情况。 该脚本无法快速运行,尤其是在有大量日志的情况下,因此我们仅查看最近两周的时间,并且还使用工作流,该工作流使您可以同时搜索多个VM上的信息。 在示例脚本中,有关于此机制的详细注释。 将结果添加到csv,然后再次加载到R中。
  • 我们形成了格式精美的xlsx文档,其中错误填写的字段将以红色突出显示,过滤器将应用于某些列,并且还会显示其他包含所谓的创建者和VM创建时间的列。
  • 我们形成一封电子邮件,在其中放置描述有效字段值的文档以及错误填写的表格。 在文本中,我们指示错误创建的VM的总数,指向共享资源的链接和动机映像。 如果没有错误填充的虚拟机,我们将发送一封带有更令人兴奋的动机图片的信。
  • 考虑到历史表的已实现机制(一种非常有趣的机制-有关该机制的更多信息),我们将数据记录在SQL Server数据库中的所有VM上

剧本


具有R的代码的主文件
#     (       ) setwd("C:\\Scripts\\getVm") ####    #### 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.ru\\VM\\', 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.ru\\VM\\', 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:\Scripts\getVm\data\allvm\all-vm-$(get-date -f yyyy-MM-dd).csv" $destinationSMB = "\\server.ru\myfolder$\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:\Scripts\getVm\creators_VM\creators_VM_$(get-date -f yyyy-MM-dd).csv" #   ,      $filePath = "C:\Scripts\getVm\data\creators\creators-$(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) 

输出是这样的:




设置Windows调度程序也有一个有趣的细微差别。 选择正确的权限和设置参数无法正常工作,因此一切都会按预期进行。 结果,找到了R库,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) 

另外,关于数据库


设置脚本后,其他问题开始出现。 例如,我想查找删除虚拟机的日期,并且vCenter中的日志已经磨损。 由于脚本每天都会将文件放入文件夹中并且不会对其进行清理(记住时我们会用手清理它),因此您可以查看旧文件并查找该虚拟机不存在的第一个文件。 但这并不酷。


我想创建一个历史数据库。


MS SQL SERVER功能有助于实现-系统版本的时间表。 它通常被转换为临时(非临时)表。


您可以在Microsoft官方文档中详细阅读。


简而言之,我们创建了一个表,我们说它将具有版本控制功能,并且SQL Server在该表中创建2个其他datetime列(创建记录的日期和记录到期的日期)以及一个将写入更改的表。 结果,我们获得了相关的信息,并且通过简单的查询(在文档中给出了示例),我们可以查看特定虚拟机的生命周期或特定时间点所有VM的状态。


在性能方面,直到完成对临时表的写入事务之前,不会完成对主表的写入事务。 即 在具有大量写操作的表上,应谨慎实现此功能,但对我们而言,这是一件很酷的事情。


为了使该机制正常工作,有必要在R上添加一小段代码,该代码将新表与所有VM的数据与存储在数据库中的VM的数据进行比较,并仅向其中写入更改的行。 该代码不是很棘手,它使用了compareDF库,但我也将在下面给出。


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) 

合计


由于引入了脚本,订单被维持了几个月。 有时会出现填充错误的VM,但是脚本可以很好地提醒您,罕见的VM连续两天进入列表。


还为历史数据分析预留了资金。


显然,其中的大部分不是“一the而就”的,而是可以通过专用软件实现的,但是这项任务很有趣,而且可以说是可选的。


再一次,R被证明是一种出色的通用语言,它不仅是解决统计问题的完美选择,而且还充当了其他数据源之间的完美“铺垫”。


Source: https://habr.com/ru/post/zh-CN454400/


All Articles