Compartilhar via


Data Exchange: Usando pares chave-valor para compartilhar informações entre o host e o convidado no Hyper-V

O Data Exchange é um serviço de integração (também conhecido como troca de pares chave-valor ou KVP) que pode ser usado para compartilhar pequenas informações entre uma máquina virtual (convidado) e seu host Hyper-V. Informações gerais sobre a máquina virtual e o host são automaticamente criadas e armazenadas como pares chave-valor. Você também pode criar seus próprios pares para compartilhar dados personalizados.

Os pares chave-valor consistem em uma "chave" e um "valor". Ambas são cadeias de caracteres; não há suporte para outros tipos de dados. Quando um par chave-valor é criado ou alterado, ele fica visível tanto para o convidado quanto para o host. Os dados do KVP viajam pelo Hyper-V VMbus e não exigem nenhuma conectividade de rede entre o convidado e o host.

Depois de criados, os pares chave-valor permanecem até serem excluídos. Qualquer aplicativo que crie pares chave-valor deve excluí-los quando eles não forem mais necessários. Os pares chave-valor se movem com a máquina virtual durante a Migração Dinâmica.

Convidados do Windows

Em convidados do Windows, os dados do KVP são armazenados no registro em:

HKLM\SOFTWARE\Microsoft\Virtual Machine

Os dados são organizados nestas subchaves:

  • Máquina Virtual\Auto – Dados que descrevem o convidado. Criado por drivers de serviço de integração após serem carregados. Visível para o host como dados intrínsecos.
  • Máquina Virtual\Externa – Dados enviados ao convidado do host por um usuário.
  • Máquina Virtual\Hóspede – Dados criados no hóspede. Visível para o host como dados não intrínsecos.
  • Máquina Virtual\Máquina Convidada\Parâmetro – Dados transmitidos para a máquina convidada a partir do host, que descrevem as características do host.

Adicionar valores de dentro do ambiente convidado é tão simples quanto criar um novo valor de string em HKLM\SOFTWARE\Microsoft\Virtual Machine\Guest. Você deve ser um administrador do sistema convidado para modificar essa localização. Você pode usar o WMI (PowerShell ou outras ferramentas) do host ou de um computador remoto (com permissões) para recuperar o valor.

Para obter informações sobre os limites de tamanho do Registro, consulte o artigo ( herdado) Limites de tamanho do elemento do Registro.

Adicionar um novo par chave-valor ao convidado

Neste exemplo, o valor de Status é definido como Ready:

$regPath = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest"
Set-ItemProperty -Path $regPath -Name "Status" -Value "Ready" -Type String

Você pode usar a mesma sintaxe para alterar o valor.

Consultar pares chave-valor no convidado

Para consultar o valor da subchave External (dados transferidos para o convidado a partir do host):

$regPath = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\External"
Get-ItemProperty -Path $regPath -Name "Name"

Convidados do Linux

O Linux não tem um registro, portanto, os itens de KVP são armazenados no sistema de arquivos. Um processo de daemon deve hv_kvp_daemonestar em execução para lidar com o processamento. Para a maioria das distribuições com LIS (Linux Integration Services) ou drivers no kernel instalados, esse daemon é iniciado automaticamente. Em alguns casos, etapas extras podem ser necessárias para instalar e iniciar o daemon.

Os serviços de integração do Linux implementam a troca de dados com pools de KVP. Um pool de KVP é um arquivo armazenado em um caminho específico. Há quatro arquivos de pool:

/var/lib/hyperv/.kvp_pool_0
/var/lib/hyperv/.kvp_pool_1
/var/lib/hyperv/.kvp_pool_2
/var/lib/hyperv/.kvp_pool_3

Esses arquivos de pool são associados aos seguintes conjuntos de chaves do Registro do Windows:

  • Pool 0: Virtual Machine\External
  • Pool 1: Virtual Machine\Guest
  • Pool 2: Virtual Machine\Auto
  • Pool 3: Virtual Machine\Guest\Parameter

Observação

Para obter mais informações sobre o suporte ao KVP do Linux, consulte Máquinas Virtuais Linux e FreeBSD no Hyper-V.

A infraestrutura de par chave-valor pode não funcionar corretamente sem uma atualização de software do Linux. Entre em contato com o fornecedor de distribuição para obter uma atualização se você encontrar problemas.

Estrutura do pool

Cada arquivo de pool contém registros com esta estrutura:

struct kvp_record
{
    unsigned char key[ HV_KVP_EXCHANGE_MAK_KEY_SIZE ];
    unsigned char value[ HV_KVP_EXCHANGE_MAX_VALUE_SIZE ];
};

Essas constantes de tamanho são definidas em um cabeçalho de kernel (distribuído com os códigos-fonte do kernel do Linux).

Ler e exibir valores do pool 0

Este exemplo lê os valores de KVP do pool 0 e os exibe.

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "../include/linux/hyperv.h"

typedef struct kvp_record
{
    unsigned char key [HV_KVP_EXCHANGE_MAX_KEY_SIZE];
    unsigned char value [HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
} KVP_RECORD;

KVP_RECORD myRecords[200];

void KVPAcquireLock(int fd)
{
    struct flock fl = {F_RDLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLKW, &fl))
    {
        perror("fcntl lock");
        exit (-10);
    }
}

void KVPReleaseLock(int fd)
{
    struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLK, &fl))
    {
        perror("fcntl unlock");
        exit (-20);
    }
}

int main (int argc, char **argv)
{
    char poolName[] = "/var/lib/hyperv/.kvp_pool_0";
    int   i;
    int   fd;
    int   bytesRead;
    int   numRecords;

    fd = open(poolName, O_RDONLY);
    if (-1 == fd)
    {
        printf("Error: Unable to open pool file %s\n", poolName);
        exit (-30);
    }

    KVPAcquireLock(fd);
    bytesRead = read(fd, myRecords, sizeof(myRecords));
    KVPReleaseLock(fd);

    numRecords = bytesRead / sizeof(struct kvp_record);
    printf("Number of records : %d\n", numRecords);

    for (i = 0; i < numRecords; i++)
    {
        printf("  Key  : %s\n  Value: %s\n\n", myRecords[i].key, myRecords[i].value);
    }

    close(fd);

    return 0;
}

Criar um item KVP no pool 1

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "../include/linux/hyperv.h"

typedef struct kvp_record
{
    unsigned char key [HV_KVP_EXCHANGE_MAX_KEY_SIZE];
    unsigned char value [HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
} KVP_RECORD;

void KVPAcquireWriteLock(int fd)
{
    struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLKW, &fl))
    {
        perror("fcntl lock");
        exit (-10);
    }
}

void KVPReleaseLock(int fd)
{
    struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLK, &fl))
    {
        perror("fcntl unlock");
        exit (-20);
    }
}

int main (int argc, char **argv)
{
    char poolName[] = "/var/lib/hyperv/.kvp_pool_1";
    int   fd;
    KVP_RECORD newKvp;

    if (3 != argc)
    {
        printf("Usage: WritePool keyName valueString\n\n");
        exit (-5);
    }

    fd = open(poolName, O_WRONLY);
    if (-1 == fd)
    {
        printf("Error: Unable to open pool file %s\n", poolName);
        exit (-30);
    }

    memset((void *)&newKvp, 0, sizeof(KVP_RECORD));
    memcpy(newKvp.key, argv[1], strlen(argv[1]));
    memcpy(newKvp.value, argv[2], strlen(argv[2]));

    KVPAcquireWriteLock(fd);
    write(fd, (void *)&newKvp, sizeof(KVP_RECORD));
    KVPReleaseLock(fd);

    close(fd);

    return 0;
}

Excluir um item KVP do pool 1

Este exemplo exclui um item.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <uapi/linux/hyperv.h>

typedef struct kvp_record
{
    unsigned char key [HV_KVP_EXCHANGE_MAX_KEY_SIZE];
    unsigned char value [HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
} KVP_RECORD;

void KVPAcquireWriteLock(int fd)
{
    struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLKW, &fl))
    {
        perror("fcntl lock");
        exit (-10);
    }
}

void KVPReleaseLock(int fd)
{
    struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
    fl.l_pid = getpid();

    if (-1 == fcntl(fd, F_SETLK, &fl))
    {
        perror("fcntl unlock");
        exit (-20);
    }
}

int find_record_offset(int fd, char *key)
{
    int bytesRead;
    int offset = 0;
    int retval = -1;

    KVP_RECORD kvpRec;

    while (1)
    {
        lseek(fd, offset, SEEK_SET);
        bytesRead = read(fd, &kvpRec, sizeof(KVP_RECORD));
        if (0 == bytesRead)
        {
            break;
        }

        if (0 == strcmp(key, (const char *) kvpRec.key))
        {
            retval = offset;
            break;
        }

        offset += sizeof(KVP_RECORD);
    }

    return retval;
}

int main (int argc, char **argv)
{
    char  poolName[] = "/var/lib/hyperv/.kvp_pool_1";
    int   fd;
    int   exitVal = -1;
    int   bytesRead;
    int   bytesWritten;
    int   offset_to_delete;
    int   offset_last_record;
    KVP_RECORD kvpRec;

    if (2 != argc)
    {
        printf("Usage: WritePool keyName valueString\n\n");
        exit (-5);
    }

    fd = open(poolName, O_RDWR, 0644);
    if (-1 == fd)
    {
        printf("Error: Unable to open pool file %s\n", poolName);
        exit (-10);
    }

    KVPAcquireWriteLock(fd);
    offset_to_delete = find_record_offset(fd, argv[1]);
    if (offset_to_delete < 0)
    {
        exitVal = -15;
        goto cleanup2;
    }

    offset_last_record = lseek(fd, -sizeof(KVP_RECORD), SEEK_END);
    if (offset_last_record < 0)
    {
        exitVal = -20;
        goto cleanup2;
    }

    if (offset_last_record != offset_to_delete)
    {
        lseek(fd, offset_last_record, SEEK_SET);
        bytesRead = read(fd, &kvpRec, sizeof(KVP_RECORD));
        lseek(fd, offset_to_delete, SEEK_SET);
        bytesWritten = write(fd, &kvpRec, sizeof(KVP_RECORD));
    }

    ftruncate(fd, offset_last_record);

    exitVal = 0;

cleanup2:
    KVPReleaseLock(fd);

cleanup1:
    close(fd);

    return exitVal;
}

Trabalhando com pares chave-valor do host usando WMI

Os exemplos a seguir usam o namespace WMI v2. Para WMI v1 (versões mais antigas), remova o segmento \v2 do caminho do namespace.

Observação

Se você estiver usando o Windows 8 ou o Windows 8.1, instale o Cliente Hyper-V para obter os namespaces.

Ler o valor do sistema host

Este exemplo obtém o valor da chave Status de uma VM chamada Vm1:

$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName = 'Vm1'}
$vm.GetRelated("Msvm_KvpExchangeComponent").GuestExchangeItems | % { \
    $GuestExchangeItemXml = ([XML]$_).SelectSingleNode(\
        "/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text() = 'Status']")
    if ($GuestExchangeItemXml -ne $null)
    {
        $GuestExchangeItemXml.SelectSingleNode(\
            "/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()" ).Value
    }
}

Adicionar ou modificar pares chave-valor do host

Para adicionar um par chave-valor do host, obtenha instâncias tanto do serviço de gerenciamento quanto da VM e crie uma nova instância de Msvm_KvpExchangeDataItem. Ao criar a nova instância, especifique o Name, Datae Source (deve ser 0). Em seguida, chame AddKvpItems.

A consulta para pares chave-valor criados pelo host é semelhante às consultas de convidado, mas requer um salto de associação adicional para Msvm_KvpExchangeComponentSettingData. Modificar e excluir valores funciona da mesma maneira—especifique o mesmo nome de chave e chame o método Modify ou Remove apropriado.

Importante

Os exemplos a seguir usam o namespace v2. Se você estiver usando o Windows Server 2008 ou o Windows Server 2008 R2, remova o \v2 segmento.

Adicionar um novo par chave-valor

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_VirtualSystemManagementService
$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName='VM1'}
$kvpDataItem = ([WMIClass][String]::Format("\\{0}\\{1}:{2}", \
    $VmMgmt.ClassPath.Server, \
    $VmMgmt.ClassPath.NamespacePath, \
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = "Data"
$kvpDataItem.Source = 0

$VmMgmt.AddKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

Consulte pares chave-valor no host

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_VirtualSystemManagementService
$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName='VM1'}
($vm.GetRelated("Msvm_KvpExchangeComponent")[0] ).GetRelated("Msvm_KvpExchangeComponentSettingData").HostExchangeItems | % { \
    $GuestExchangeItemXml = ([XML]$_).SelectSingleNode(\
        "/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text() = 'Name2']")
    if ($GuestExchangeItemXml -ne $null)
    {
        $GuestExchangeItemXml.SelectSingleNode(\
            "/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()" ).Value
    }
}

Modificar um par chave-valor

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_VirtualSystemManagementService
$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName='VM1'}
$kvpDataItem = ([WMIClass][String]::Format("\\{0}\\{1}:{2}", \
    $VmMgmt.ClassPath.Server, \
    $VmMgmt.ClassPath.NamespacePath, \
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = "Data2"
$kvpDataItem.Source = 0

$VmMgmt.ModifyKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

Remover um par chave-valor

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_VirtualSystemManagementService
$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class \
    Msvm_ComputerSystem -Filter {ElementName='VM1'}
$kvpDataItem = ([WMIClass][String]::Format("\\{0}\\{1}:{2}", \
    $VmMgmt.ClassPath.Server, \
    $VmMgmt.ClassPath.NamespacePath, \
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = [String]::Empty
$kvpDataItem.Source = 0

$VmMgmt.RemoveKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

Consulte também