Informes diarios del estado de la máquina virtual con R y PowerShell


Entrada


Buen dia Durante medio año, hemos estado ejecutando un script (más precisamente, un conjunto de scripts) que genera informes sobre el estado de las máquinas virtuales (y no solo). Decidí compartir la experiencia de creación y el código en sí. Cuento con las críticas y el hecho de que este material puede ser útil para alguien.


Necesita formación


Tenemos muchas máquinas virtuales (alrededor de 1500 máquinas virtuales distribuidas en el tercer vCenter). Se crean nuevos y los antiguos se eliminan con bastante frecuencia. Para preservar el orden, se agregaron varios campos personalizados a vCenter para dividir las máquinas virtuales en subsistemas, indicando si son prueba, así como quién y cuándo. El factor humano ha llevado al hecho de que más de la mitad de los autos se quedaron con campos vacíos, lo que complicó el trabajo. Una vez cada seis meses, alguien se asustó y comenzó a trabajar en la actualización de estos datos, pero el resultado dejó de ser relevante durante una semana y media.
Aclararé de inmediato que todos entienden que debería haber aplicaciones para crear máquinas, un proceso para crearlas, etc. etc. Y mientras todo este proceso se sigue estrictamente y en orden. Desafortunadamente, este no es el caso con nosotros, pero este no es el tema del artículo :)


En general, se decidió automatizar la verificación de la corrección del llenado de los campos.
Decidimos que un buen comienzo sería una carta diaria con una lista de máquinas llenadas incorrectamente para todos los ingenieros responsables y sus superiores.


En este punto, uno de los colegas ya había implementado un script de PowerShell, que todos los días recopilaba información en todas las máquinas de todos los vCenter-s y formaba 3 documentos csv (cada uno en su propio vCenter), que se distribuían en un disco compartido. Se decidió tomar este script como base y complementarlo con controles utilizando el lenguaje R, con el que tenía algo de experiencia.


En el proceso de finalización, la solución se cubrió de información por correo, una base de datos con las tablas principales e históricas (más sobre esto más adelante), así como el análisis de los registros de vSphere para encontrar los creadores reales de vm y el momento en que se crearon.


Para el desarrollo, se utilizaron IDE RStudio Desktop y PowerShell ISE.


El script se inicia desde una máquina virtual de Windows normal.


Descripción de la lógica general.


La lógica general de los scripts es la siguiente.


  • Recopilamos datos en máquinas virtuales usando el script PowerShell, que se llama a través de R, el resultado se combina en un csv. La interacción inversa entre idiomas se realiza de manera similar. (era posible conducir datos directamente de R a PowerShell en forma de variables, pero es difícil, e incluso con csv intermedio es más fácil depurar y compartir resultados intermedios con alguien).
  • Usando R, formamos parámetros válidos para los campos cuyos valores estamos verificando. - Estamos formando un documento de Word que contendrá los valores de estos campos para insertarlos en una carta de información que será una respuesta a las preguntas de los colegas "No está bien, pero ¿cómo debo completar esto?"
  • Cargamos datos en todas las máquinas virtuales desde csv usando R, formamos un marco de datos, eliminamos campos innecesarios y formamos un documento xlsx de información que contendrá información resumida sobre todas las máquinas virtuales que cargamos en un recurso compartido.
  • En el marco de datos para todas las máquinas virtuales, aplicamos todas las comprobaciones de la corrección del llenado de los campos y formamos una tabla que contiene solo máquinas virtuales con campos rellenados incorrectamente (y solo estos campos).
  • La lista resultante de máquinas virtuales se envía a otro script de PowerShell, que examinará los registros de vCenter para eventos de creación de máquinas virtuales, lo que le permitirá especificar el tiempo estimado de creación de la máquina virtual y el creador previsto. Este es el caso cuando nadie confiesa el auto de quién. Este script no funciona rápido, especialmente si hay muchos registros, por lo que solo observamos las últimas 2 semanas, y también utilizamos el flujo de trabajo, que le permite buscar información en varias máquinas virtuales al mismo tiempo. En el script de ejemplo, hay comentarios detallados sobre este mecanismo. El resultado se agrega a csv, que nuevamente se carga en R.
  • Formamos un documento xlsx bellamente formateado en el que los campos rellenados incorrectamente se resaltarán en rojo, los filtros se aplicarán a algunas columnas y se indicarán las columnas adicionales que contienen los presuntos creadores y el momento de la creación de la VM.
  • Formamos un correo electrónico, donde ponemos un documento que describe los valores de campo válidos, así como una tabla con un relleno incorrecto. En el texto indicamos el número total de máquinas virtuales creadas incorrectamente, un enlace a un recurso compartido y una imagen motivadora. Si no hay máquinas virtuales incorrectamente pobladas, enviamos otra carta con una imagen motivadora más alegre.
  • Registramos datos en todas las máquinas virtuales en la base de datos de SQL Server, teniendo en cuenta el mecanismo implementado de las tablas históricas (un mecanismo muy interesante, sobre el cual se detalla más)

Guiones


El archivo principal con el código para 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") 

Script para obtener la lista de vm en 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 } 

Script de PowerShell que extrae los registros de los creadores de máquinas virtuales y sus fechas de creación
 #   ,      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" 

La biblioteca xlsx merece una atención especial, lo que hizo posible que el archivo adjunto a la carta tuviera un formato claro (como le gusta al manual), y no solo una tabla csv.


Crear un hermoso documento xlsx con una lista de máquinas llenas incorrectamente
 #    #   : "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) 

El resultado es algo como esto:




También hubo un matiz interesante en la configuración del programador de Windows. No funcionó recoger los derechos correctos y los parámetros de configuración para que todo comenzara como debería. Como resultado, se encontró la biblioteca R, que a su vez crea una tarea para ejecutar el script R y ni siquiera se olvida del archivo de registro. Luego puede corregir la tarea con bolígrafos.


Una pieza de código R con dos ejemplos que crea una tarea en el Programador de Windows
 library(taskscheduleR) myscript <- file.path(getwd(), "all_vm.R") ##    62  taskscheduler_create(taskname = "getAllVm", rscript = myscript, schedule = "ONCE", starttime = format(Sys.time() + 62, "%H:%M")) ##      09:10 taskscheduler_create(taskname = "getAllVmDaily", rscript = myscript, schedule = "WEEKLY", days = c("MON", "TUE", "WED", "THU", "FRI"), starttime = "02:00") ##   taskscheduler_delete(taskname = "getAllVm") taskscheduler_delete(taskname = "getAllVmDaily") #   ( 4 ) tail(readLines("all_vm.log"), sep ="\n", n = 4) 

Por separado, sobre la base de datos


Después de configurar el guión, comenzaron a aparecer otras preguntas. Por ejemplo, quería encontrar la fecha en que se eliminó la VM y los registros en vCenter ya estaban desgastados. Dado que el script coloca los archivos en la carpeta todos los días y no lo limpia (lo limpiamos con nuestras manos cuando lo recordamos), puede mirar los archivos antiguos y encontrar el primer archivo en el que esta VM no existe. Pero eso no es genial.


Quería crear una base de datos histórica.


La funcionalidad de MS SQL SERVER fue de gran ayuda: una tabla temporal con versión del sistema. Generalmente se traduce como tablas temporales (no temporales).


Puede leer en detalle en la documentación oficial de Microsoft .


En resumen, creamos una tabla, decimos que la tendremos con versiones y SQL Server crea 2 columnas de fecha y hora adicionales en esta tabla (la fecha en que se creó el registro y la fecha en que expiró el registro) y una tabla adicional en la que se escribirán los cambios. Como resultado, obtenemos información relevante y, a través de consultas simples, cuyos ejemplos se dan en la documentación, podemos ver el ciclo de vida de una máquina virtual en particular o el estado de todas las máquinas virtuales en un determinado momento.


En términos de rendimiento, la transacción de escritura en la tabla principal no se completará hasta que se complete la transacción de escritura en la tabla temporal. Es decir en tablas con una gran cantidad de operaciones de escritura, esta funcionalidad debe implementarse con precaución, pero en nuestro caso es algo muy bueno.


Para que el mecanismo funcionara correctamente, era necesario agregar un pequeño fragmento de código en R que comparara la nueva tabla con los datos de todas las máquinas virtuales con la que está almacenada en la base de datos y solo escribiría las filas cambiadas. El código no es muy complicado, usa la biblioteca compareDF, pero también lo daré a continuación.


Código R para escribir datos en la base de datos
 #   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) 

Total


Como resultado de la introducción del guión, el orden se mantuvo y mantuvo durante varios meses. A veces, aparecen máquinas virtuales pobladas incorrectamente, pero el script sirve como un buen recordatorio y una máquina virtual rara entra en la lista durante 2 días seguidos.


También se hizo una reserva para el análisis de datos históricos.


Está claro que gran parte de esto puede realizarse no "sobre la rodilla", sino con un software especializado, pero la tarea fue interesante y, podría decirse, opcional.


Una vez más, R demostró ser un maravilloso lenguaje universal, que es perfecto no solo para resolver problemas estadísticos, sino que también actúa como un excelente "tendido" entre otras fuentes de datos.


Source: https://habr.com/ru/post/454400/


All Articles