xenxs: support parsing and formatting vif bandwidth

xm config has long supported specifying vif rate limiting, e.g.

vif = [ 'mac=00:16:3E:74:3d:76,bridge=br0,rate=10MB/s' ]

Add support for mapping rate to and from <bandwidth> in the xenxs xm and
sxpr parser/formatter. rate is mapped to the required 'average' attribute
of the <outbound> element, e.g.

  <interface type='bridge'>
    ...
    <bandwidth>
      <outbound average='10240'/>
    </bandwidth>
  </interface>

https://bugzilla.suse.com/show_bug.cgi?id=960305

Based on upstream commit 1dd34bbb

Index: libvirt-1.2.5/src/xenxs/xen_sxpr.c
===================================================================
--- libvirt-1.2.5.orig/src/xenxs/xen_sxpr.c
+++ libvirt-1.2.5/src/xenxs/xen_sxpr.c
@@ -26,6 +26,8 @@
 
 #include <config.h>
 
+#include <regex.h>
+
 #include "internal.h"
 #include "virerror.h"
 #include "virconf.h"
@@ -318,6 +320,66 @@ xenParseSxprChar(const char *value,
 }
 
 
+static const char *vif_bytes_per_sec_re = "^[0-9]+[GMK]?[Bb]/s$";
+
+int
+xenParseSxprVifRate(const char *rate, unsigned long long *kbytes_per_sec)
+{
+    char *trate = NULL;
+    char *p;
+    regex_t rec;
+    int err;
+    char *suffix;
+    unsigned long long tmp;
+    int ret = -1;
+
+    if (VIR_STRDUP(trate, rate) < 0)
+        return -1;
+
+    p = strchr(trate, '@');
+    if (p != NULL)
+        *p = 0;
+
+    err = regcomp(&rec, vif_bytes_per_sec_re, REG_EXTENDED|REG_NOSUB);
+    if (err != 0) {
+        char error[100];
+        regerror(err, &rec, error, sizeof(error));
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to compile regular expression '%s': %s"),
+                       vif_bytes_per_sec_re, error);
+        goto cleanup;
+    }
+
+    if (regexec(&rec, trate, 0, NULL, 0)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Invalid rate '%s' specified"), rate);
+        goto cleanup;
+    }
+
+    if (virStrToLong_ull(rate, &suffix, 10, &tmp)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to parse rate '%s'"), rate);
+        goto cleanup;
+    }
+
+    if (*suffix == 'G')
+       tmp *= 1024 * 1024;
+    else if (*suffix == 'M')
+       tmp *= 1024;
+
+    if (*suffix == 'b' || *(suffix + 1) == 'b')
+       tmp /= 8;
+
+    *kbytes_per_sec = tmp;
+    ret = 0;
+
+ cleanup:
+    regfree(&rec);
+    VIR_FREE(trate);
+    return ret;
+}
+
+
 /**
  * xenParseSxprDisks:
  * @def: the domain config
@@ -595,6 +657,25 @@ xenParseSxprNets(virDomainDefPtr def,
                 }
             }
 
+            tmp = sexpr_node(node, "device/vif/rate");
+            if (tmp) {
+                virNetDevBandwidthPtr bandwidth;
+                unsigned long long kbytes_per_sec;
+
+                if (xenParseSxprVifRate(tmp, &kbytes_per_sec) < 0)
+                    goto cleanup;
+
+                if (VIR_ALLOC(bandwidth) < 0)
+                    goto cleanup;
+                if (VIR_ALLOC(bandwidth->out) < 0) {
+                    VIR_FREE(bandwidth);
+                    goto cleanup;
+                }
+
+                bandwidth->out->average = kbytes_per_sec;
+                net->bandwidth = bandwidth;
+            }
+
             if (VIR_STRDUP(net->model, model) < 0)
                 goto cleanup;
 
@@ -1895,6 +1976,9 @@ xenFormatSxprNet(virConnectPtr conn,
 
     virBufferAsprintf(buf, "(mac '%s')", virMacAddrFormat(&def->mac, macaddr));
 
+    if (def->bandwidth && def->bandwidth->out && def->bandwidth->out->average)
+        virBufferAsprintf(buf, "(rate '%lluKB/s')", def->bandwidth->out->average);
+
     switch (def->type) {
     case VIR_DOMAIN_NET_TYPE_BRIDGE:
         virBufferEscapeSexpr(buf, "(bridge '%s')", def->data.bridge.brname);
Index: libvirt-1.2.5/src/xenxs/xen_sxpr.h
===================================================================
--- libvirt-1.2.5.orig/src/xenxs/xen_sxpr.h
+++ libvirt-1.2.5/src/xenxs/xen_sxpr.h
@@ -53,6 +53,8 @@ int xenParseSxprSound(virDomainDefPtr de
 
 virDomainChrDefPtr xenParseSxprChar(const char *value, const char *tty);
 
+int xenParseSxprVifRate(const char *rate, unsigned long long *kbytes_per_sec);
+
 int xenFormatSxprDisk(virDomainDiskDefPtr def, virBufferPtr buf, int hvm,
                       int xendConfigVersion, int isAttach);
 
Index: libvirt-1.2.5/src/xenxs/xen_xm.c
===================================================================
--- libvirt-1.2.5.orig/src/xenxs/xen_xm.c
+++ libvirt-1.2.5/src/xenxs/xen_xm.c
@@ -677,6 +677,7 @@ xenParseXM(virConfPtr conf, int xendConf
             char mac[18];
             char bridge[50];
             char vifname[50];
+            char rate[50];
             char *key;
 
             bridge[0] = '\0';
@@ -685,6 +686,7 @@ xenParseXM(virConfPtr conf, int xendConf
             model[0] = '\0';
             type[0] = '\0';
             vifname[0] = '\0';
+            rate[0] = '\0';
 
             if ((list->type != VIR_CONF_STRING) || (list->str == NULL))
                 goto skipnic;
@@ -748,6 +750,13 @@ xenParseXM(virConfPtr conf, int xendConf
                                        _("IP %s too big for destination"), data);
                         goto skipnic;
                     }
+                } else if (STRPREFIX(key, "rate=")) {
+                    int len = nextkey ? (nextkey - data) : sizeof(rate) - 1;
+                    if (virStrncpy(rate, data, len, sizeof(rate)) == NULL) {
+                        virReportError(VIR_ERR_INTERNAL_ERROR,
+                                       _("rate %s too big for destination"), data);
+                        goto skipnic;
+                    }
                 }
 
                 while (nextkey && (nextkey[0] == ',' ||
@@ -801,6 +810,24 @@ xenParseXM(virConfPtr conf, int xendConf
                 VIR_STRDUP(net->ifname, vifname) < 0)
                 goto cleanup;
 
+            if (rate[0]) {
+                virNetDevBandwidthPtr bandwidth;
+                unsigned long long kbytes_per_sec;
+
+                if (xenParseSxprVifRate(rate, &kbytes_per_sec) < 0)
+                    goto cleanup;
+
+                if (VIR_ALLOC(bandwidth) < 0)
+                    goto cleanup;
+                if (VIR_ALLOC(bandwidth->out) < 0) {
+                    VIR_FREE(bandwidth);
+                    goto cleanup;
+                }
+
+                bandwidth->out->average = kbytes_per_sec;
+                net->bandwidth = bandwidth;
+            }
+
             if (VIR_APPEND_ELEMENT(def->nets, def->nnets, net) < 0)
                 goto cleanup;
 
@@ -1448,6 +1475,9 @@ static int xenFormatXMNet(virConnectPtr
         virBufferAsprintf(&buf, ",vifname=%s",
                           net->ifname);
 
+    if (net->bandwidth && net->bandwidth->out && net->bandwidth->out->average)
+        virBufferAsprintf(&buf, ",rate=%lluKB/s", net->bandwidth->out->average);
+
     if (virBufferError(&buf)) {
         virReportOOMError();
         goto cleanup;
Index: libvirt-1.2.5/src/libvirt_xenxs.syms
===================================================================
--- libvirt-1.2.5.orig/src/libvirt_xenxs.syms
+++ libvirt-1.2.5/src/libvirt_xenxs.syms
@@ -15,6 +15,7 @@ xenParseSxpr;
 xenParseSxprChar;
 xenParseSxprSound;
 xenParseSxprString;
+xenParseSxprVifRate;
 
 # xenxs/xen_xm.h
 xenFormatXM;
