Data Exchange: Using key-value pairs to share information between the host and guest on Hyper-V

 

Updated: October 22, 2015

Applies To: Hyper-V Server 2012, Windows 8.1, Windows Server 2012 R2, Windows Server 2012, Hyper-V Server 2012 R2, Windows 8

Data Exchange is an integration service, also known as a key-value pair or KVP, that can be used share information between virtual machines and the Hyper-V host. General information about the virtual machine and host is automatically created and stored as key-value pairs. There is also a set of pairs that can be created manually for sharing other information.

Key-value pairs consist of a “key” and a “value”. Both the key and the value are strings, no other data types are supported. When a key-value pair is created or changed, it is visible to the guest and the host. The key-value pair information is transferred across the Hyper-V VMbus and does not require any kind of network connection between the guest and the Hyper-V host.

Once created, key-value pairs remain until they are deleted. Applications that use key-value pairs must ensure that they are deleted when they are no longer needed. Key-value pairs are transferred with the guest when the virtual machine is live migrated to another Hyper-V host.

On Windows guests, the key-value pairs for data exchange are stored in the registry under the path:

HKLM\SOFTWARE\Microsoft\Virtual Machine

The data is stored in various subkeys as follows:

Virtual Machine\Auto contains the data describing the guest. This data is created by the integration service drivers after they are loaded. This data is available to the host as intrinsic data.

Virtual Machine\External contains data pushed to the guest from the host by a user.

Virtual Machine\Guest contains data created on the guest. This data is available to the host as non-intrinsic data.

Virtual Machine\Guest\Parameter contains data pushed to the guest from the host. This data describes the host.

Adding values from within the guest is as simple as creating a new registry value under the HKLM\Software\Microsoft\Virtual Machine\Guest section of the registry. You do have to be an administrator in the VM in order to access this section of the registry. You can use a WMI query to retrieve the value from the host or a remote machine with permissions on the host.

For information about limits for the registry, see Registry Element Size Limits.

Adding a new key-value pair in the guest

In the following example, the value of Status is set to Ready:

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

You can use the same syntax to change the value of the key.

Querying for a key-value pairs in the guest

To query for the value of the External subkey containing data pushed to the guest from the host:

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

Linux does not have registry keys, so the KVP items are stored in the Linux file system. There is a daemon process, hv_kvp_daemon, which must be running in the Linux operating system to handle the processing. For most of the distributions with the LIS installed, this daemon should be running automatically. But in some cases, extra steps are needed to get the daemon installed and running.

The Linux integration services implement data exchange with KVP pools. A KVP pool is just a file stored in a specific place in the file system. There are 4 KVP pool files:

  • /var/lib/hyperv/.kvp_pool_0

  • /var/lib/hyperv/.kvp_pool_1

  • /var/lib/hyperv/.kvp_pool_2

  • /var/lib/hyperv/.kvp_pool_3

These pool files map to the following Windows registry key files:

  • pool 0: Virtual Machine\External

  • pool 1: Virtual Machine\Guest

  • pool 2: Virtual Machine\Auto

  • pool 3: Virtual Machine\Guest\Parameter

System_CAPS_ICON_note.jpg Note

  • For more information about support for KVP on Linux, see Linux and FreeBSD Virtual Machines on Hyper-V.
  • In Windows Server 2012 R2, the key-value pair infrastructure might not function correctly without a Linux software update. Contact your distribution vendor to get the software update if you see problems with this feature.

Pool structure

The data in the pool files is a collection of records with the following structure:

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

Those size constants are defined in hyperv.h. This is a kernel header and is distributed with your Linux kernel.

Read and display the value from pool 0

This sample code reads the KVP values from pool 0 and displays them.

#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;  
  
    //  
    // Open the specific pool file  
    //  
    fd = open(poolName, O_RDONLY);  
    if (-1 == fd)  
    {  
        printf("Error: Unable to open pool file %s\n", poolName);  
        exit (-30);  
    }  
  
    //  
    // Read a bunch of records.  Note, this sample code  
    // may not read all records (it will read a max of 200 records).  
    //  
    KVPAcquireLock(fd);  
    bytesRead = read(fd, myRecords, sizeof(myRecords));  
    KVPReleaseLock(fd);  
  
    //  
    // Compute the number of records read and display the data  
    //  
    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;  
}  

Create an item KVP at pool 1

Pool 1 is the only pool a user mode program should write to.

#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);  
    }  
  
    //  
    // Open the specific pool file  
    // Note: Pool 1 is the only pool a user program should write to!  
    //  
    fd = open(poolName, O_WRONLY);  
    if (-1 == fd)  
    {  
        printf("Error: Unable to open pool file %s\n", poolName);  
        exit (-30);  
    }  
  
    //  
    // Populate the data buffer with the key value items  
    //  
    memset((void *)&newKvp, 0, sizeof(KVP_RECORD));  
    memcpy(newKvp.key, argv[1], strlen(argv[1]));  
    memcpy(newKvp.value, argv[2], strlen(argv[2]));  
  
    //  
    // Write the KVP data to the pool  
    //  
    KVPAcquireWriteLock(fd);  
    write(fd, (void *)&newKvp, sizeof(KVP_RECORD));  
    KVPReleaseLock(fd);  
  
    close(fd);  
  
    return 0;  
}  
  

Delete a KVP item from pool 1

This sample shows how to delete an 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);  
    }  
  
    //  
    // Open the specific pool file  
    // Note: Pool 1 is the only pool a user program should write to!  
    //  
    fd = open(poolName, O_RDWR, 0644);  
    if (-1 == fd)  
    {  
        printf("Error: Unable to open pool file %s\n", poolName);  
        exit (-10);  
    }  
  
    //  
    // Try to find the record to delete  
    //  
    KVPAcquireWriteLock(fd);  
    offset_to_delete = find_record_offset(fd, argv[1]);  
    if (offset_to_delete < 0)  
    {  
        exitVal = -15;  
        goto cleanup2;  
    }  
  
    //  
    // Compute offset of last record in file  
    //  
    offset_last_record = lseek(fd, -sizeof(KVP_RECORD), SEEK_END);  
    if (offset_last_record < 0)  
    {  
        exitVal = -20;  
        goto cleanup2;  
    }  
  
    //  
    // If record to delete is not last record in file,  
    // copy last record in file over record to delete  
    //  
    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));  
    }  
  
    //  
    // Truncate file by size of KVP record  
    //  
    ftruncate(fd, offset_last_record);  
  
    exitVal = 0;  
  
 cleanup2:  
    KVPReleaseLock(fd);  
  
cleanup1:  
    close(fd);  
  
    return exitVal;  
}  
  

The following examples use the WMI 2.0 namespace. If you want to use the WMI 1.0 namespace, just remove the \v2 from the namespace path.

System_CAPS_ICON_note.jpg Note

If you are using Windows® 8 or Windows® 8.1, you need to install Client Hyper-V to get the namespaces.

Reading the value from the host

This example gets the value of the key from 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   
    }   
}  

Adding or modifying the key-value pairs from the host

To add a key-value pair from the host you must get the instance of the management service as well as the VM. You must also create a new instance of the Msvm_KvpExchangeDataItem class. The sample below uses the instance of the management service such that if the script is running remotely it will connect to the right server. Once you have the new instance of the class you just need to specify the Name, Data and Source parameters (source must be 0). Then just call the AddKvpItems Method providing the instance of the VM and the instance of the created class.

Querying for key-value pairs created from the host is very similar to ones from the guest except that you have to go through one more association to get to the Msvm_KvpExchangeComponentSettingsData class. Modifying and deleting values is almost the same as creating them – you just specify the same key name as the value you wish to update or delete and call the Modify or Delete method.

System_CAPS_ICON_important.jpg Important

The example below utilizes the V2 namespace, if you are using Windows Server 2008 or Windows Server 2008 R2, remove the \v2 from the namespace path.

Add a new key-value pair

$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))  

Querying for key-value pairs on the 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   
        }      
    }   

Modifying key-value pairs

$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))  

Removing key-value pairs

$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))  

Hyper-V WMI v2 Porting Guide
Customizing The Key-Value Pair (KVP) Integration Component
Hyper-V WMI: KVP Exchange aka Data Exchange (Retrieving and Modifying Parent/Host KVPs)
Hyper-V WMI: KVP Exchange aka Data Exchange (Adding New Items From Parent/Host)

Show: