From 02e11b8353548933b2bff4f6169bd2c3aceb61db Mon Sep 17 00:00:00 2001 From: Matthias Bolte Date: Wed, 29 Sep 2010 23:04:19 +0200 Subject: [PATCH] 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. --- docs/formatdomain.html.in | 4 +- src/conf/domain_conf.c | 18 +- src/conf/domain_conf.h | 3 + src/esx/README | 23 ++- src/esx/esx_vmx.c | 163 ++++++++++++++++-- .../vmx2xml-serial-network-client.vmx | 6 + .../vmx2xml-serial-network-client.xml | 25 +++ .../vmx2xml-serial-network-server.vmx | 6 + .../vmx2xml-serial-network-server.xml | 25 +++ tests/vmx2xmltest.c | 2 + .../xml2vmx-serial-network-client.vmx | 14 ++ .../xml2vmx-serial-network-client.xml | 15 ++ .../xml2vmx-serial-network-server.vmx | 14 ++ .../xml2vmx-serial-network-server.xml | 15 ++ tests/xml2vmxtest.c | 2 + 15 files changed, 304 insertions(+), 31 deletions(-) create mode 100644 tests/vmx2xmldata/vmx2xml-serial-network-client.vmx create mode 100644 tests/vmx2xmldata/vmx2xml-serial-network-client.xml create mode 100644 tests/vmx2xmldata/vmx2xml-serial-network-server.vmx create mode 100644 tests/vmx2xmldata/vmx2xml-serial-network-server.xml create mode 100644 tests/xml2vmxdata/xml2vmx-serial-network-client.vmx create mode 100644 tests/xml2vmxdata/xml2vmx-serial-network-client.xml create mode 100644 tests/xml2vmxdata/xml2vmx-serial-network-server.vmx create mode 100644 tests/xml2vmxdata/xml2vmx-serial-network-server.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 9f077eb2b7..7baa62b6d0 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1469,7 +1469,9 @@ qemu-kvm -net nic,model=? /dev/null ...

- Alternatively you can use telnet instead of raw TCP. + Alternatively you can use telnet instead of raw TCP. + Since 0.8.5 you can also use telnets + (secure telnet) and tls.

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index afb06e839a..539d44350d 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -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, "      \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:
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 7195c04ac3..9bf13d8026 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -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)
diff --git a/src/esx/README b/src/esx/README
index 72543320d3..9ae93b12c0 100644
--- a/src/esx/README
+++ b/src/esx/README
@@ -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
 
 
 
diff --git a/src/esx/esx_vmx.c b/src/esx/esx_vmx.c
index 59eb3b2d7a..bfebc4a0e0 100644
--- a/src/esx/esx_vmx.c
+++ b/src/esx/esx_vmx.c
@@ -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 =           <=>   serial0.fileName = "://:"
+->data.tcp.service =                                                   # e.g. "telnet://0.0.0.0:42001"
+->data.tcp.protocol = 
+
+->data.tcp.listen = 1             <=>   serial0.network.endPoint = "server"     # defaults to "server"
+
+???                               <=>   serial0.vspc = "foobar"                 # defaults to , 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 =           <=>   serial0.fileName = "://:"
+->data.tcp.service =                                                   # e.g. "telnet://192.168.0.17:42001"
+->data.tcp.protocol = 
+
+->data.tcp.listen = 0             <=>   serial0.network.endPoint = "client"     # defaults to "server"
+
+???                               <=>   serial0.vspc = "foobar"                 # defaults to , 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);
 
diff --git a/tests/vmx2xmldata/vmx2xml-serial-network-client.vmx b/tests/vmx2xmldata/vmx2xml-serial-network-client.vmx
new file mode 100644
index 0000000000..18cfc57c61
--- /dev/null
+++ b/tests/vmx2xmldata/vmx2xml-serial-network-client.vmx
@@ -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"
diff --git a/tests/vmx2xmldata/vmx2xml-serial-network-client.xml b/tests/vmx2xmldata/vmx2xml-serial-network-client.xml
new file mode 100644
index 0000000000..b0c071549c
--- /dev/null
+++ b/tests/vmx2xmldata/vmx2xml-serial-network-client.xml
@@ -0,0 +1,25 @@
+
+  00000000-0000-0000-0000-000000000000
+  32768
+  32768
+  1
+  
+    hvm
+  
+  
+  destroy
+  restart
+  destroy
+  
+    
+      
+      
+      
+    
+    
+      
+      
+      
+    
+  
+
diff --git a/tests/vmx2xmldata/vmx2xml-serial-network-server.vmx b/tests/vmx2xmldata/vmx2xml-serial-network-server.vmx
new file mode 100644
index 0000000000..d4a9c41310
--- /dev/null
+++ b/tests/vmx2xmldata/vmx2xml-serial-network-server.vmx
@@ -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"
diff --git a/tests/vmx2xmldata/vmx2xml-serial-network-server.xml b/tests/vmx2xmldata/vmx2xml-serial-network-server.xml
new file mode 100644
index 0000000000..e15101714a
--- /dev/null
+++ b/tests/vmx2xmldata/vmx2xml-serial-network-server.xml
@@ -0,0 +1,25 @@
+
+  00000000-0000-0000-0000-000000000000
+  32768
+  32768
+  1
+  
+    hvm
+  
+  
+  destroy
+  restart
+  destroy
+  
+    
+      
+      
+      
+    
+    
+      
+      
+      
+    
+  
+
diff --git a/tests/vmx2xmltest.c b/tests/vmx2xmltest.c
index c71f536dc1..2d59297845 100644
--- a/tests/vmx2xmltest.c
+++ b/tests/vmx2xmltest.c
@@ -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);
diff --git a/tests/xml2vmxdata/xml2vmx-serial-network-client.vmx b/tests/xml2vmxdata/xml2vmx-serial-network-client.vmx
new file mode 100644
index 0000000000..d10f403398
--- /dev/null
+++ b/tests/xml2vmxdata/xml2vmx-serial-network-client.vmx
@@ -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"
diff --git a/tests/xml2vmxdata/xml2vmx-serial-network-client.xml b/tests/xml2vmxdata/xml2vmx-serial-network-client.xml
new file mode 100644
index 0000000000..e2a5afe45e
--- /dev/null
+++ b/tests/xml2vmxdata/xml2vmx-serial-network-client.xml
@@ -0,0 +1,15 @@
+
+  serial-network
+  564d9bef-acd9-b4e0-c8f0-aea8b9103515
+  4096
+  
+    hvm
+  
+  
+    
+      
+      
+      
+    
+  
+
diff --git a/tests/xml2vmxdata/xml2vmx-serial-network-server.vmx b/tests/xml2vmxdata/xml2vmx-serial-network-server.vmx
new file mode 100644
index 0000000000..b5d77f476f
--- /dev/null
+++ b/tests/xml2vmxdata/xml2vmx-serial-network-server.vmx
@@ -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"
diff --git a/tests/xml2vmxdata/xml2vmx-serial-network-server.xml b/tests/xml2vmxdata/xml2vmx-serial-network-server.xml
new file mode 100644
index 0000000000..a17e2fef24
--- /dev/null
+++ b/tests/xml2vmxdata/xml2vmx-serial-network-server.xml
@@ -0,0 +1,15 @@
+
+  serial-network
+  564d9bef-acd9-b4e0-c8f0-aea8b9103515
+  4096
+  
+    hvm
+  
+  
+    
+      
+      
+      
+    
+  
+
diff --git a/tests/xml2vmxtest.c b/tests/xml2vmxtest.c
index 2a457b4b11..0881ae9904 100644
--- a/tests/xml2vmxtest.c
+++ b/tests/xml2vmxtest.c
@@ -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);
-- 
GitLab