Data Exchange は、仮想マシン (ゲスト) とその Hyper-V ホストの間で小さな情報を共有するために使用できる統合サービス (キーと値のペア交換または KVP とも呼ばれます) です。 仮想マシンとホストに関する一般的な情報が自動的に作成され、キーと値のペアとして格納されます。 カスタム データを共有する独自のペアを作成することもできます。
キーと値のペアは、"key" と "value" で構成されます。 どちらも文字列です。他のデータ型はサポートされていません。 キーと値のペアが作成または変更されると、ゲストとホストの両方に表示されます。 KVP データは Hyper-V VMbus 経由で移動され、ゲストとホスト間のネットワーク接続は必要ありません。
作成されると、キーと値のペアは削除されるまで保持されます。 キーと値のペアを作成するアプリケーションは、不要になったらそれらを削除する必要があります。 キーと値のペアは、ライブ マイグレーション中に仮想マシンと共に移動します。
Windows ゲスト
Windows ゲストでは、KVP データは次の下のレジストリに格納されます。
HKLM\SOFTWARE\Microsoft\Virtual Machine
データは、次のサブキーに編成されます。
- Virtual Machine\Auto – ゲストを記述するデータ。 読み込み後に統合サービス ドライバーによって作成されます。 組み込みデータとしてホストに表示されます。
- 仮想マシン\外部 – ユーザーによってホストからゲストにプッシュされたデータ。
- Virtual Machine\Guest – ゲスト に作成されたデータ。 非組み込みデータとしてホストに表示されます。
- Virtual Machine\Guest\Parameter – ホストを記述するホストからゲストにプッシュされたデータ。
ゲスト内からの値の追加は、 HKLM\SOFTWARE\Microsoft\Virtual Machine\Guestの下に新しい文字列値を作成するのと同じくらい簡単です。 この場所を変更するには、ゲストの管理者である必要があります。 ホストまたはリモート コンピューター (アクセス許可あり) から WMI (PowerShell またはその他のツール) を使用して値を取得できます。
レジストリ サイズの制限については、(レガシ) の記事 「レジストリ要素のサイズ制限」を参照してください。
ゲストに新しいキーと値のペアを追加する
この例では、 Status の値は Ready に設定されています。
$regPath = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest"
Set-ItemProperty -Path $regPath -Name "Status" -Value "Ready" -Type String
同じ構文を使用して値を変更できます。
ゲストのキーと値のペアに対してクエリを実行する
外部サブキー (ホストからゲストにプッシュされたデータ) の値を照会するには:
$regPath = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\External"
Get-ItemProperty -Path $regPath -Name "Name"
Linux ゲスト
Linux にはレジストリがないため、KVP 項目はファイル システムに格納されます。 処理を処理するには、デーモン プロセス ( hv_kvp_daemon) が実行されている必要があります。 Linux Integration Services (LIS) またはカーネル内ドライバーがインストールされているほとんどのディストリビューションでは、このデーモンが自動的に起動します。 場合によっては、デーモンをインストールして起動するために追加の手順が必要になる場合があります。
Linux 統合サービスは、KVP プールとのデータ交換を実装します。 KVP プールは、特定のパスに格納されているファイルです。 次の 4 つのプール ファイルがあります。
/var/lib/hyperv/.kvp_pool_0
/var/lib/hyperv/.kvp_pool_1
/var/lib/hyperv/.kvp_pool_2
/var/lib/hyperv/.kvp_pool_3
これらのプール ファイルは、次の Windows レジストリ キー セットにマップされます。
- プール 0:
Virtual Machine\External - プール 1:
Virtual Machine\Guest - プール 2:
Virtual Machine\Auto - プール 3:
Virtual Machine\Guest\Parameter
注
Linux KVP のサポートの詳細については、 Hyper-V 上の Linux および FreeBSD Virtual Machines を参照してください。
キーと値のペアのインフラストラクチャは、Linux ソフトウェア更新プログラムなしでは正しく機能しない可能性があります。 問題が発生した場合は、ディストリビューション ベンダーに更新プログラムをお問い合わせください。
プールの構造
各プール ファイルには、次の構造のレコードが含まれています。
struct kvp_record
{
unsigned char key[ HV_KVP_EXCHANGE_MAK_KEY_SIZE ];
unsigned char value[ HV_KVP_EXCHANGE_MAX_VALUE_SIZE ];
};
これらのサイズ定数は、 hyperv.h (Linux カーネル ソースと共に配布されるカーネル ヘッダー) で定義されます。
プール 0 から値を読み取って表示する
このサンプルでは、プール 0 から KVP 値を読み取り、それらを表示します。
```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;
}
プール 1 に KVP 項目を作成する
#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;
}
プール 1 から KVP 項目を削除する
このサンプルでは、項目を削除します。
#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;
}
WMI を使用してホストからキー・バリュー・ペアを操作する
次の例では、WMI v2 名前空間を使用します。 WMI v1 (以前のバージョン) の場合は、名前空間パスから \v2 セグメントを削除します。
注
Windows 8 または Windows 8.1 を使用している場合は、クライアント Hyper-V をインストールして名前空間を取得します。
ホストから値を読み取る
この例では、Status という名前の VM からキー 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
}
}
ホストからキーと値のペアを追加または変更する
ホストからキーと値のペアを追加するには、管理サービスと VM の両方のインスタンスを取得し、 Msvm_KvpExchangeDataItemの新しいインスタンスを作成します。 新しいインスタンスを作成するときは、 Name、 Data、 Source を指定します ( 0する必要があります)。 次に、 AddKvpItemsを呼び出します。
ホストによって作成されたキーと値のペアのクエリはゲスト クエリに似ていますが、Msvm_KvpExchangeComponentSettingDataと関連付けるためには追加のホップが必要です。 値の変更と削除は同じように機能します。同じキー名を指定し、適切な Modify または Remove メソッドを呼び出します。
Important
次の例では、v2 名前空間を使用します。 Windows Server 2008 または Windows Server 2008 R2 を使用している場合は、 \v2 セグメントを削除します。
新しいキーと値のペアを追加する
$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))
ホスト上のキーと値のペアに対してクエリを実行する
$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
}
}
キーと値のペアを変更する
$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))
キーと値のペアを削除する
$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))