esx: Add support for virtual serial device network backing

Since version 4.1 ESX(i) can expose virtual serial devices over TCP.

Add support in the VMX handling code for this, add test cases to cover
it and add links to some documentation.

ESX supports two additional protocols: TELNETS and TLS. Add them to
the list of serial-over-TCP protocols.
This commit is contained in:
Matthias Bolte 2010-09-29 23:04:19 +02:00
parent 62a50a0b80
commit 02e11b8353
15 changed files with 304 additions and 31 deletions

View File

@ -1469,7 +1469,9 @@ qemu-kvm -net nic,model=? /dev/null
...</pre>
<p>
Alternatively you can use telnet instead of raw TCP.
Alternatively you can use <code>telnet</code> instead of <code>raw</code> TCP.
<span class="since">Since 0.8.5</span> you can also use <code>telnets</code>
(secure telnet) and <code>tls</code>.
<p>
<pre>

View File

@ -203,6 +203,12 @@ VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST,
"tcp",
"unix")
VIR_ENUM_IMPL(virDomainChrTcpProtocol, VIR_DOMAIN_CHR_TCP_PROTOCOL_LAST,
"raw",
"telnet",
"telnets",
"tls")
VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST,
"sb16",
"es1370",
@ -2748,12 +2754,10 @@ virDomainChrDefParseXML(virCapsPtr caps,
def->data.tcp.listen = 1;
}
if (protocol == NULL ||
STREQ(protocol, "raw"))
if (protocol == NULL)
def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW;
else if (STREQ(protocol, "telnet"))
def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
else {
else if ((def->data.tcp.protocol =
virDomainChrTcpProtocolTypeFromString(protocol)) < 0) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown protocol '%s'"), protocol);
goto error;
@ -5848,9 +5852,7 @@ virDomainChrDefFormat(virBufferPtr buf,
def->data.tcp.host,
def->data.tcp.service);
virBufferVSprintf(buf, " <protocol type='%s'/>\n",
def->data.tcp.protocol ==
VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET
? "telnet" : "raw");
virDomainChrTcpProtocolTypeToString(def->data.tcp.protocol));
break;
case VIR_DOMAIN_CHR_TYPE_UNIX:

View File

@ -357,6 +357,8 @@ enum virDomainChrType {
enum virDomainChrTcpProtocol {
VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW,
VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET,
VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNETS, /* secure telnet */
VIR_DOMAIN_CHR_TCP_PROTOCOL_TLS,
VIR_DOMAIN_CHR_TCP_PROTOCOL_LAST,
};
@ -1172,6 +1174,7 @@ VIR_ENUM_DECL(virDomainChrDevice)
VIR_ENUM_DECL(virDomainChrChannelTarget)
VIR_ENUM_DECL(virDomainChrConsoleTarget)
VIR_ENUM_DECL(virDomainChr)
VIR_ENUM_DECL(virDomainChrTcpProtocol)
VIR_ENUM_DECL(virDomainSoundModel)
VIR_ENUM_DECL(virDomainMemballoonModel)
VIR_ENUM_DECL(virDomainWatchdogModel)

View File

@ -3,13 +3,24 @@ Some links to relevant documentation
====================================
VI/vSphere API: http://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/
http://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/
VMX config: http://www.sanbarrow.com/vmx.html
CPUID: http://www.sandpile.org/ia32/cpuid.htm
Memory model: http://www.vmware.com/pdf/esx3_memory.pdf
http://www.vmware.com/pdf/usenix_resource_mgmt.pdf
VI/vSphere API:
http://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/
http://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/
http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/
VMX config:
http://www.sanbarrow.com/vmx.html
CPUID:
http://www.sandpile.org/ia32/cpuid.htm
Memory model:
http://www.vmware.com/pdf/esx3_memory.pdf
http://www.vmware.com/pdf/usenix_resource_mgmt.pdf
Virtual serial port (network backed):
http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.device.VirtualSerialPort.URIBackingInfo.html
http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/vsp41_usingproxy_virtual_serial_ports.pdf

View File

@ -379,6 +379,36 @@ def->serials[0]...
??? <=> serial0.yieldOnMsrRead = "true" # defaults to "false", FIXME: not representable
## serials: network, server (since vSphere 4.1) ################################
->type = _CHR_TYPE_TCP <=> serial0.fileType = "network"
->data.tcp.host = <host> <=> serial0.fileName = "<protocol>://<host>:<service>"
->data.tcp.service = <service> # e.g. "telnet://0.0.0.0:42001"
->data.tcp.protocol = <protocol>
->data.tcp.listen = 1 <=> serial0.network.endPoint = "server" # defaults to "server"
??? <=> serial0.vspc = "foobar" # defaults to <not present>, FIXME: not representable
??? <=> serial0.tryNoRxLoss = "false" # defaults to "false", FIXME: not representable
??? <=> serial0.yieldOnMsrRead = "true" # defaults to "false", FIXME: not representable
## serials: network, client (since vSphere 4.1) ################################
->type = _CHR_TYPE_TCP <=> serial0.fileType = "network"
->data.tcp.host = <host> <=> serial0.fileName = "<protocol>://<host>:<service>"
->data.tcp.service = <service> # e.g. "telnet://192.168.0.17:42001"
->data.tcp.protocol = <protocol>
->data.tcp.listen = 0 <=> serial0.network.endPoint = "client" # defaults to "server"
??? <=> serial0.vspc = "foobar" # defaults to <not present>, FIXME: not representable
??? <=> serial0.tryNoRxLoss = "false" # defaults to "false", FIXME: not representable
??? <=> serial0.yieldOnMsrRead = "true" # defaults to "false", FIXME: not representable
################################################################################
## parallels ###################################################################
@ -424,8 +454,11 @@ def->parallels[0]...
#define VIR_FROM_THIS VIR_FROM_ESX
#define ESX_BUILD_VMX_NAME_EXTRA(_suffix, _extra) \
snprintf(_suffix##_name, sizeof(_suffix##_name), "%s."_extra, prefix);
#define ESX_BUILD_VMX_NAME(_suffix) \
snprintf(_suffix##_name, sizeof(_suffix##_name), "%s."#_suffix, prefix);
ESX_BUILD_VMX_NAME_EXTRA(_suffix, #_suffix)
/* directly map the virDomainControllerModel to esxVMX_SCSIControllerModel,
* this is good enough for now because all virDomainControllerModel values
@ -2113,6 +2146,11 @@ esxVMX_ParseSerial(esxVMX_Context *ctx, virConfPtr conf, int port,
char fileName_name[48] = "";
char *fileName = NULL;
char network_endPoint_name[48] = "";
char *network_endPoint = NULL;
xmlURIPtr parsedUri = NULL;
if (def == NULL || *def != NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
return -1;
@ -2137,6 +2175,7 @@ esxVMX_ParseSerial(esxVMX_Context *ctx, virConfPtr conf, int port,
ESX_BUILD_VMX_NAME(startConnected);
ESX_BUILD_VMX_NAME(fileType);
ESX_BUILD_VMX_NAME(fileName);
ESX_BUILD_VMX_NAME_EXTRA(network_endPoint, "network.endPoint");
/* vmx:present */
if (esxUtil_GetConfigBoolean(conf, present_name, &present, false,
@ -2165,6 +2204,12 @@ esxVMX_ParseSerial(esxVMX_Context *ctx, virConfPtr conf, int port,
goto cleanup;
}
/* vmx:network.endPoint -> def:data.tcp.listen */
if (esxUtil_GetConfigString(conf, network_endPoint_name, &network_endPoint,
true) < 0) {
goto cleanup;
}
/* Setup virDomainChrDef */
if (STRCASEEQ(fileType, "device")) {
(*def)->target.port = port;
@ -2190,10 +2235,72 @@ esxVMX_ParseSerial(esxVMX_Context *ctx, virConfPtr conf, int port,
(*def)->data.file.path = fileName;
fileName = NULL;
} else if (STRCASEEQ(fileType, "network")) {
(*def)->target.port = port;
(*def)->type = VIR_DOMAIN_CHR_TYPE_TCP;
parsedUri = xmlParseURI(fileName);
if (parsedUri == NULL) {
virReportOOMError();
goto cleanup;
}
if (parsedUri->port == 0) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("VMX entry '%s' doesn't contain a port part"),
fileName_name);
goto cleanup;
}
(*def)->data.tcp.host = strdup(parsedUri->server);
if ((*def)->data.tcp.host == NULL) {
virReportOOMError();
goto cleanup;
}
if (virAsprintf(&(*def)->data.tcp.service, "%d", parsedUri->port) < 0) {
virReportOOMError();
goto cleanup;
}
/* See vSphere API documentation about VirtualSerialPortURIBackingInfo */
if (parsedUri->scheme == NULL ||
STRCASEEQ(parsedUri->scheme, "tcp") ||
STRCASEEQ(parsedUri->scheme, "tcp4") ||
STRCASEEQ(parsedUri->scheme, "tcp6")) {
(*def)->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW;
} else if (STRCASEEQ(parsedUri->scheme, "telnet")) {
(*def)->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
} else if (STRCASEEQ(parsedUri->scheme, "telnets")) {
(*def)->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNETS;
} else if (STRCASEEQ(parsedUri->scheme, "ssl") ||
STRCASEEQ(parsedUri->scheme, "tcp+ssl") ||
STRCASEEQ(parsedUri->scheme, "tcp4+ssl") ||
STRCASEEQ(parsedUri->scheme, "tcp6+ssl")) {
(*def)->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TLS;
} else {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("VMX entry '%s' contains unsupported scheme '%s'"),
fileName_name, parsedUri->scheme);
goto cleanup;
}
if (network_endPoint == NULL || STRCASEEQ(network_endPoint, "server")) {
(*def)->data.tcp.listen = 1;
} else if (STRCASEEQ(network_endPoint, "client")) {
(*def)->data.tcp.listen = 0;
} else {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Expecting VMX entry '%s' to be 'server' or 'client' "
"but found '%s'"), network_endPoint_name, network_endPoint);
goto cleanup;
}
} else {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Expecting VMX entry '%s' to be 'device', 'file' or 'pipe' "
"but found '%s'"), fileType_name, fileType);
"or 'network' but found '%s'"), fileType_name, fileType);
goto cleanup;
}
@ -2207,6 +2314,8 @@ esxVMX_ParseSerial(esxVMX_Context *ctx, virConfPtr conf, int port,
VIR_FREE(fileType);
VIR_FREE(fileName);
VIR_FREE(network_endPoint);
xmlFreeURI(parsedUri);
return result;
@ -3069,6 +3178,7 @@ esxVMX_FormatSerial(esxVMX_Context *ctx, virDomainChrDefPtr def,
virBufferPtr buffer)
{
char *fileName = NULL;
const char *protocol;
if (def->target.port < 0 || def->target.port > 3) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
@ -3077,13 +3187,6 @@ esxVMX_FormatSerial(esxVMX_Context *ctx, virDomainChrDefPtr def,
return -1;
}
if (def->data.file.path == NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
_("Expecting domain XML attribute 'path' of entry "
"'devices/serial/source' to be present"));
return -1;
}
virBufferVSprintf(buffer, "serial%d.present = \"true\"\n", def->target.port);
/* def:type -> vmx:fileType and def:data.file.path -> vmx:fileName */
@ -3124,6 +3227,41 @@ esxVMX_FormatSerial(esxVMX_Context *ctx, virDomainChrDefPtr def,
def->target.port, def->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_TCP:
switch (def->data.tcp.protocol) {
case VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW:
protocol = "tcp";
break;
case VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET:
protocol = "telnet";
break;
case VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNETS:
protocol = "telnets";
break;
case VIR_DOMAIN_CHR_TCP_PROTOCOL_TLS:
protocol = "ssl";
break;
default:
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Unsupported character device TCP protocol '%s'"),
virDomainChrTcpProtocolTypeToString(def->data.tcp.protocol));
return -1;
}
virBufferVSprintf(buffer, "serial%d.fileType = \"network\"\n",
def->target.port);
virBufferVSprintf(buffer, "serial%d.fileName = \"%s://%s:%s\"\n",
def->target.port, protocol, def->data.tcp.host,
def->data.tcp.service);
virBufferVSprintf(buffer, "serial%d.network.endPoint = \"%s\"\n",
def->target.port,
def->data.tcp.listen ? "server" : "client");
break;
default:
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Unsupported character device type '%s'"),
@ -3154,13 +3292,6 @@ esxVMX_FormatParallel(esxVMX_Context *ctx, virDomainChrDefPtr def,
return -1;
}
if (def->data.file.path == NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
_("Expecting domain XML attribute 'path' of entry "
"'devices/parallel/source' to be present"));
return -1;
}
virBufferVSprintf(buffer, "parallel%d.present = \"true\"\n",
def->target.port);

View File

@ -0,0 +1,6 @@
config.version = "8"
virtualHW.version = "4"
serial0.present = "true"
serial0.fileType = "network"
serial0.fileName = "tcp://192.168.0.17:42001"
serial0.network.endPoint = "client"

View File

@ -0,0 +1,25 @@
<domain type='vmware'>
<uuid>00000000-0000-0000-0000-000000000000</uuid>
<memory>32768</memory>
<currentMemory>32768</currentMemory>
<vcpu>1</vcpu>
<os>
<type arch='i686'>hvm</type>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<serial type='tcp'>
<source mode='connect' host='192.168.0.17' service='42001'/>
<protocol type='raw'/>
<target port='0'/>
</serial>
<console type='tcp'>
<source mode='connect' host='192.168.0.17' service='42001'/>
<protocol type='raw'/>
<target type='serial' port='0'/>
</console>
</devices>
</domain>

View File

@ -0,0 +1,6 @@
config.version = "8"
virtualHW.version = "4"
serial0.present = "true"
serial0.fileType = "network"
serial0.fileName = "telnets://0.0.0.0:42001"
serial0.network.endPoint = "server"

View File

@ -0,0 +1,25 @@
<domain type='vmware'>
<uuid>00000000-0000-0000-0000-000000000000</uuid>
<memory>32768</memory>
<currentMemory>32768</currentMemory>
<vcpu>1</vcpu>
<os>
<type arch='i686'>hvm</type>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<serial type='tcp'>
<source mode='bind' host='0.0.0.0' service='42001'/>
<protocol type='telnets'/>
<target port='0'/>
</serial>
<console type='tcp'>
<source mode='bind' host='0.0.0.0' service='42001'/>
<protocol type='telnets'/>
<target type='serial' port='0'/>
</console>
</devices>
</domain>

View File

@ -266,6 +266,8 @@ mymain(int argc, char **argv)
DO_TEST("serial-pipe-server-vm", "serial-pipe", esxVI_ProductVersion_ESX35);
DO_TEST("serial-pipe-client-app", "serial-pipe", esxVI_ProductVersion_ESX35);
DO_TEST("serial-pipe-server-vm", "serial-pipe", esxVI_ProductVersion_ESX35);
DO_TEST("serial-network-server", "serial-network-server", esxVI_ProductVersion_ESX41);
DO_TEST("serial-network-client", "serial-network-client", esxVI_ProductVersion_ESX41);
DO_TEST("parallel-file", "parallel-file", esxVI_ProductVersion_ESX35);
DO_TEST("parallel-device", "parallel-device", esxVI_ProductVersion_ESX35);

View File

@ -0,0 +1,14 @@
config.version = "8"
virtualHW.version = "7"
guestOS = "other"
uuid.bios = "56 4d 9b ef ac d9 b4 e0-c8 f0 ae a8 b9 10 35 15"
displayName = "serial-network"
memsize = "4"
numvcpus = "1"
floppy0.present = "false"
floppy1.present = "false"
serial0.present = "true"
serial0.fileType = "network"
serial0.fileName = "tcp://192.168.0.17:42001"
serial0.network.endPoint = "client"
serial0.yieldOnMsrRead = "true"

View File

@ -0,0 +1,15 @@
<domain type='vmware'>
<name>serial-network</name>
<uuid>564d9bef-acd9-b4e0-c8f0-aea8b9103515</uuid>
<memory>4096</memory>
<os>
<type>hvm</type>
</os>
<devices>
<serial type='tcp'>
<source mode="connect" host="192.168.0.17" service="42001"/>
<protocol type="raw"/>
<target port='0'/>
</serial>
</devices>
</domain>

View File

@ -0,0 +1,14 @@
config.version = "8"
virtualHW.version = "7"
guestOS = "other"
uuid.bios = "56 4d 9b ef ac d9 b4 e0-c8 f0 ae a8 b9 10 35 15"
displayName = "serial-network"
memsize = "4"
numvcpus = "1"
floppy0.present = "false"
floppy1.present = "false"
serial0.present = "true"
serial0.fileType = "network"
serial0.fileName = "ssl://0.0.0.0:42001"
serial0.network.endPoint = "server"
serial0.yieldOnMsrRead = "true"

View File

@ -0,0 +1,15 @@
<domain type='vmware'>
<name>serial-network</name>
<uuid>564d9bef-acd9-b4e0-c8f0-aea8b9103515</uuid>
<memory>4096</memory>
<os>
<type>hvm</type>
</os>
<devices>
<serial type='tcp'>
<source mode="bind" host="0.0.0.0" service="42001"/>
<protocol type="tls"/>
<target port='0'/>
</serial>
</devices>
</domain>

View File

@ -259,6 +259,8 @@ mymain(int argc, char **argv)
DO_TEST("serial-file", "serial-file", esxVI_ProductVersion_ESX35);
DO_TEST("serial-device", "serial-device", esxVI_ProductVersion_ESX35);
DO_TEST("serial-pipe", "serial-pipe", esxVI_ProductVersion_ESX35);
DO_TEST("serial-network-server", "serial-network-server", esxVI_ProductVersion_ESX41);
DO_TEST("serial-network-client", "serial-network-client", esxVI_ProductVersion_ESX41);
DO_TEST("parallel-file", "parallel-file", esxVI_ProductVersion_ESX35);
DO_TEST("parallel-device", "parallel-device", esxVI_ProductVersion_ESX35);