vSphere PowerCLI

Get-View Parte II: Caso práctico

En este articulo voy a realizar un ejemplo de caso práctico de uso de PowerCLI Get-View, con una solución a uno de sus principales inconvenientes: solo obtenemos las claves y no los nombres.

Es parte de la serie de artículos sobre Get-VM y Get-View:

  1. GET-VM PARTE I: LO BASICO
  2. GET-VM PARTE II: RENDIMIENTO
  3. GET-VIEW PARTE I: LO NO TAN BASICO
  4. GET-VIEW PARTE II: CASO PRÁCTICO
  5. GET-VIEW PARTE III: RENDIMIENTO
  6. GET-VM VS GET-VIEW: CONCLUSIONES

¿Y cual es mejor manera de resolver los nombres de los objetos?

Caso Practico

Vamos con un ejemplo. Quiero obtener un listado con todas las máquinas de vCenter con los siguientes campos:

CampoPropiedad Get-ViewDescripción
ClaveMorefClave de la maquina en vCenter. Siempre retorna, y en este caso ademas da error si lo especificamos explicitamente
NombreNameNombre de la maquina
EstadoRuntime.PowerStateSi la maquina esta encendida o apagada.
ToolsGuest.ToolsStatusEl estado de las tools
CPUConfig.Hardware.NumCPUNumero de cores asignados.
RAMConfig.Hardware.MemoryMBRAM asignada, en MB.
EspacioSummary.Storage.CommittedEspacio consumido en bytes.
NotasConfig.AnnotationLas notas de la maquinas
FolderParentEl parent de una maquina es su folder
HostRuntime.HostHost ESXI donde está corriendo la máquina.
Cluster???Este dato no lo tenemos. Es el atributo Parent del Host. Es decir deberemos obtener el host, y una vez que lo tengamos pedir el parent del host.

Solución 1: SubConsultas

Para poder resolver los nombres podriamos lanzar una consulta (cmdlet) por cada clave.

Primero genero un array con las propiedades que quiero obtener:

$propiedades = @(
    "Name",
    "Runtime.PowerState", 
    "Guest.ToolsStatus", 
    "Config.Hardware.NumCPU", 
    "Config.Hardware.MemoryMB", 
    "Summary.Storage.Committed", 
    "Config.Annotation", 
    "Parent", 
    "Runtime.Host"
)

Y ahora ejecuto Get-View seleccionando solo los campos para obtener un Custom Object con las propiedades definidas.

Recordar que Get-View retorna todos los atributos, aunque estén vacíos. Por eso tenemos que seleccionarlos.

$vms = get-view -ViewType VirtualMachine -Property $propiedades|
    Select-Object @{n="Nombre"; e={$_.Name}},
        @{n="Clave";e={$_.Moref}},
        @{n="Estado"; e={$_.Runtime.PowerState}},
        @{n="Tools"; e={$_.guest.ToolsStatus}}, 
        @{n="CPU"; e={$_.Config.Hardware.NumCPU}},
        @{n="RAM"; e={$_.Config.Hardware.MemoryMB}},
        @{n="Espacio"; e={$_.Summary.Storage.Committed}},
        @{n="Notas"; e={$_.Config.Annotation}},
        @{n='Folder';e={(get-view -Id $_.Parent -property name).Name}},
        @{n='Host';e={(get-view -Id $_.Runtime.Host -property parent).Name}},
        @{n='Cluster';e={get-view -Id (get-view -Id $_.Runtime.Host -property parent).Parent -Property Name}}

$vms

Para resolver Folder, Host he usado subconsultas Get-View con el Id, consultas que son muy rápidas: Entre 20 y 50ms.

(get-view -Id $_.Parent -property name).Name
(get-view -Id $_.Runtime.Host -property parent).Name

En el caso del cluster he tenido que usar una subconsulta (parent del host) de la subconsulta (host de la maquina). Un poco lio si. También podría haberlo separado en dos sentencias por claridad, tal que así:

$hostParentId = (get-view -Id $_.Runtime.Host -property parent).Parent;
get-view -Id $hostParentId -Property Name

Podría haber usado también get-Folder, get-VMHost y get-Cluster respectivamente, pero me ha parecido mas apropiado seguir usando Get-View como ejemplo adicional.

Get-View con subconsultas
Get-View con subconsultas

Resultado = 270 ms por maquina

El problema de esta solución, es que por cada máquina va a ejecutar 5 consultas Get-View. La principal más cuatro subconsultas.

Si este ejemplo ha tardado 270ms y tengo 6000 máquinas = 1620 segundos -> 27 minutos.

Esta solución no es nada eficiente!

info En general, para todo tipo de scripts en los que procesamos muchos objetos, tenemos que minimizar todo lo posible el uso de subconsultas, porque pasamos de unos pocos milisegundos a muchos minutos.

¿Y cómo resuelvo entonces el nombre del cluster o del host?


Solución 2: UpdateViewData

Para esta solución voy a usar el evento UpdateViewData() de las vistas, con el que podemos pedir otros datos de cualquier otro id que contenga nuestra vista y lo guardara en una propiedad llamada LinkedView.

UpdateViewData admite varios propiedades y de vistas diferentes en la misma sentencia. El nombre de el/los campo/s a pedir será:

Ruta de la propiedad en la vista actual + Nombre de la propiedad del objeto que queremos traer.

Y la ruta del LinkedView:

Ruta de la propiedad + LinkedView + Propiedad del objeto traido.

Mejor vemos unos ejemplos que es mucho mas facil de entender:

Quiero pedir los nombres de host de un cluster y donde lo guarda el LinkedView:

$clusters = get-view -ViewType ClusterComputeResource

$clusters.UpdateViewData("Host.name")
$clusters.LinkedView.Host.Name

En este otro ejemplo quiero el nombre de las maquinas que alberga el host y donde ha guardado el LinkedView

$clusters.UpdateViewData("Host.VM.Name")
$clusters.LinkedView.Host.LinkedView.VM.Name

Ahora que he explicado como utilizar UpdateViewData(), vamos con la solución al caso practico:

Primero obtengo la vista con las propiedades seleccionados:

$vms = get-view -ViewType VirtualMachine -Property $propiedades
Get-View de maquinas con propiedades
Get-View de maquinas con propiedades

Resultado = 6,3 segundos para traer la vista con nuestras propiedades.

Ahora ejecuto el UpdateViewData():

$vms.UpdateViewData("Parent.Name", "Runtime.Host.Name", "Runtime.Host.Parent.Name") 
UpdateViewData
UpdateViewData

Resultado = 114 segundos

Y para terminar, saco el listado con los LinkedView de las nombres de Folder, Host y Cluster:

$vms | Select-Object @{n="Nombre"; e={$_.Name}},
    @{n="Clave"; e={$_.Moref}},
    @{n="Estado"; e={$_.Runtime.PowerState}},
    @{n="Tools"; e={$_.guest.ToolsStatus}}, 
    @{n="CPU"; e={$_.Config.Hardware.NumCPU}},
    @{n="RAM"; e={$_.Config.Hardware.MemoryMB}},
    @{n="Espacio"; e={$_.Summary.Storage.Committed}},
    @{n="Notas"; e={$_.Config.Annotation}},
    @{n="Folder"; e={$_.LinkedView.Parent.Name}},
    @{n="Host"; e={$_.Runtime.LinkedView.Host.Name}},
    @{n="Cluster"; e={$_.Runtime.LinkedView.Host.LinkedView.Parent.Name}} 
Get-View Select
Get-View Select

En 2 minutos exactos hemos obtenido todas las propiedades que necesitamos para las 6000 maquinas.

Es una solución muchísimo mas rápida que las subconsultas y muy versátil para cualquier dato. Lógicamente, cuantos mas datos de otras vistas se pidan con el evento UpdateViewData, mas tardara.


Solución 3: HashTables

Para esta solución voy a usar uno de los tipos de objetos más útiles de PowerShell: hashtables.

info En PowerShell un hashtable es un array asociativo, también llamado diccionario, en la que tenemos un conjunto de pares Key = Value. Permite direccionar un elemento directamente con su Key, sin tener que recorrer todo su contenido, por lo que es prácticamente inmediato.

info Para saber más sobre este tipo de array os recomiendo la lectura de este articulo imprescindible: Powershell: Everything you wanted to know about hashtables de Kevin Marquette

Veamos cómo hacerlo: Voy a obtener con Get-View TODOS los nombres de TODOS los Folders, Host y Cluster y cargar su contenido en diferentes HashTables.

Empiezo con los folders: Genero un hashtable con el nombre como Valor y su MoRef como Key. El resultado será algo así:

$hashFolder = @{}
get-view -ViewType Folder -Property Name | %{ $hash.Add($_.moref.value, @{"Name"=$_.name}) }
Get-View HashTable Folder
Get-View HashTable Folder

Resultado: 200 ms para 758 objetos.

Hago lo mismo con TODOS los host ESXI de vCenter. En este caso además obtengo también su Parent, que contiene el MoRef del cluster:

$hashHost = @{}
get-view -ViewType HostSystem -Property Name, Parent | %{ $hashHost.Add($_.moref.value, @{"Name"=$_.name; "Parent"=$_.parent.value}) }
Get-View HashTable Host
Get-View HashTable Host

Resultado: 130 ms para 111 host.

Y ahora lo mismo con los clusters:

$hashCluster = @{}
get-view -ViewType ClusterComputeResource -Property Name | %{ $hashCluster.Add($_.moref.value, @{"Name"=$_.name}) }
Get-View HashTable Cluster
Get-View HashTable Cluster

Resultado: 90 ms para 12 clusters.

Ahora que tengo toda la información que necesitamos en los hashtables, solo tenemos que obtener el valor usando la clave (key). Por ejemplo:

$hashFolder["group-v536545"]
$hashCluster["domain-c105337"]
$hashHost["host-331167"]

Y como parece que me va a ser muy útil en el futuro, creo una función genérica y «universal» que como parámetro le pasamos el tipo de vista:

function Get-HashView {
    param(
         [Parameter(Mandatory)][ValidateNotNullOrEmpty()][String]$Tipo
    )
    $hash = @{}
    get-view -ViewType $Tipo -Property Name, Parent | %{ $hash.Add($_.moref.value, @{"Name"=$_.name; "Parent"=$_.parent.value} )}
    return $hash
}

Y que luego podemos usar así:

$hashFolders     = Get-HashView -Tipo Folder
$hashHosts       = Get-HashView -Tipo HostSystem
$hashClusters    = Get-HashView -Tipo ClusterComputeResource
Get-View Get-HashView
Get-View Get-HashView

Resultado: 442 ms para cargar los 3 hashtables.

Y ahora vuelvo a ejecutar el Get-View de máquinas, pero esta vez usaremos los hashtables cargados:

$vms = get-view -ViewType VirtualMachine -Property $propiedades |
    Select-Object @{n="Nombre"; e={$_.Name}},
        @{n="Clave";e={$_.Moref}},
        @{n="Estado"; e={$_.Runtime.PowerState}},
        @{n="Tools"; e={$_.guest.ToolsStatus}}, 
        @{n="CPU"; e={$_.Config.Hardware.NumCPU}},
        @{n="RAM"; e={[math]::round(($_.Config.Hardware.MemoryMB) / 1KB)}},
        @{n="Espacio"; e={[math]::round(($_.Summary.Storage.Committed) / 1GB)}},
        @{n="Notas"; e={$_.Config.Annotation}},
        @{n='Folder';e={$hashFolders[$_.Parent.Value].name}},
        @{n='Host';e={$hashHosts[$_.Runtime.Host.Value].name}},
        @{n='Cluster';e={$hashClusters[$hashHosts[$_.Runtime.Host.Value].parent].name}}
Get-View resolviendo claves con HashTables
Get-View resolviendo claves con HashTables

Resultado: 7 segundos para obtener 5892 máquinas.

Eureka!!! Todas las propiedades que necesito de TODAS las maquinas en apenas unos segundos!

Pongo todo el código junto para que quede mas claro:

function Get-HashView {
    param(
         [Parameter(Mandatory)]
		[ValidateNotNullOrEmpty()]
		[String]$Tipo
    )
    $hash = @{}
    get-view -ViewType $Tipo -Property Name, Parent | %{ $hash.Add($_.moref.value, @{"Name"=$_.name; "Parent"=$_.parent.value} )}
    return $hash
}

$hashFolders     = Get-HashView -Tipo Folder
$hashHosts       = Get-HashView -Tipo HostSystem
$hashClusters    = Get-HashView -Tipo ClusterComputeResource
$propiedades     = @(
    "Name",
    "Runtime.PowerState", 
    "Guest.ToolsStatus", 
    "Config.Hardware.NumCPU", 
    "Config.Hardware.MemoryMB", 
    "Summary.Storage.Committed", 
    "Config.Annotation", 
    "Parent", 
    "Runtime.Host"
)
$vms = get-view -ViewType VirtualMachine -Property $propiedades |
    Select-Object @{n="Nombre"; e={$_.Name}},
        @{n="Clave";e={$_.Moref}},
        @{n="Estado"; e={$_.Runtime.PowerState}},
        @{n="Tools"; e={$_.guest.ToolsStatus}}, 
        @{n="CPU"; e={$_.Config.Hardware.NumCPU}},
        @{n="RAM"; e={[math]::round(($_.Config.Hardware.MemoryMB) / 1KB)}},
        @{n="Espacio"; e={[math]::round(($_.Summary.Storage.Committed) / 1GB)}},
        @{n="Notas"; e={$_.Config.Annotation}},
        @{n='Folder';e={$hashFolders[$_.Parent.Value].name}},
        @{n='Host';e={$hashHosts[$_.Runtime.Host.Value].name}},
        @{n='Cluster';e={$hashClusters[$hashHosts[$_.Runtime.Host.Value].parent].name}}
$vms.count
$vms |Out-GridView

Como hemos visto, con esta ultima solución podremos resolver el nombre de cualquier MoRef con apenas unos milisegundos de penalización, y superar así una traba habitual de Get-View, pero con las ventajas de su mejor virtud: En unos segundos tengo el informe completo!

Habrá casos en la que no sea practica esta solución y es mejor usar el evento UpdateViewData. Y en otros muchos, quizás lo mejor sea una combinación de ambas soluciones. Lo importante es conocer las opciones de las que disponemos y saber utilizarlas en cada momento.

En el siguiente articulo continuare con el ultimo articulo de Get-View con el Rendimiento en diferentes escenarios y pruebas.

Cualquier duda o pregunta, responderé encantando en los comentarios.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *