qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC 0/2] qemu-ga: add guest-network-set-interface command
@ 2015-02-25  3:26 Chen Fan
  2015-02-25  3:26 ` [Qemu-devel] [RFC 1/2] qemu-agent: " Chen Fan
  2015-02-25  3:26 ` [Qemu-devel] [RFC 2/2] qemu-agent: add guest-network-delete-interface command Chen Fan
  0 siblings, 2 replies; 3+ messages in thread
From: Chen Fan @ 2015-02-25  3:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: izumi.taku

Nowadays, qemu has supported physical NIC hotplug for high network
throughput. but it's in conflict with live migration feature, to keep
network connectivity, we could to create bond device interface which
provides a mechanism for enslaving multiple network interfaces into a
single "bond" interface. the active-backup mode can be used for an
automatic switch. so this patch is adding a guest-network-set-interface
command for creating bond device. so the management can easy to create
a bond device dynamically when guest running.

we can specify:

add interface:
{"execute":"guest-network-set-interface", "arguments":
{"interface": {"type":"bond", "name":"bond0", "onboot":"onboot",
 "options":"mode=active-backup primary=eth1 miimon=100 updelay=10 use_carrier=ioctl",
 "ip-address": {"ip-address":"192.168.122.89", "ip-address-type":"ipv4", "prefix":24,
 "gateway":"192.168.122.1"}, "subInterfaces":[{"name":"eth0"}, {"name":"eth1"}]}}}

delete interface:
{"execute":"guest-network-delete-interface", "arguments":{"name":"bond0"}}

Note:
this patch has introduces netcf lib for create interfaces. because
the netcf provides a convenient facility for programs that want to
shield themselves from the intricacies of networking setup.
on different environment, creating bonding device is distinct.
On Fedora/Redhat, that requires modifying the files ifcfg-eth0,
ifcfg-eth1 and ifcfg-bond0 in /etc/sysconfig/network-scripts;
on Debian, it requires changing several entries in /etc/network/interfaces.

Chen Fan (2):
  qemu-agent: add guest-network-set-interface command
  qemu-agent: add guest-network-delete-interface command

 configure            |  16 +++
 qga/commands-posix.c | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qga/commands-win32.c |  13 +++
 qga/qapi-schema.json |  65 +++++++++++
 4 files changed, 406 insertions(+)

-- 
1.9.3

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [Qemu-devel] [RFC 1/2] qemu-agent: add guest-network-set-interface command
  2015-02-25  3:26 [Qemu-devel] [RFC 0/2] qemu-ga: add guest-network-set-interface command Chen Fan
@ 2015-02-25  3:26 ` Chen Fan
  2015-02-25  3:26 ` [Qemu-devel] [RFC 2/2] qemu-agent: add guest-network-delete-interface command Chen Fan
  1 sibling, 0 replies; 3+ messages in thread
From: Chen Fan @ 2015-02-25  3:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: izumi.taku

Nowadays, qemu has supported physical NIC hotplug for high network
throughput. but it's in conflict with live migration feature, to keep
network connectivity, we could to create bond device interface which
provides a mechanism for enslaving multiple network interfaces into a
single "bond" interface. the active-backup mode can be used for an
automatic switch. so this patch is adding a guest-network-set-interface
command for creating bond device. so the management can easy to create
a bond device dynamically when guest running.

Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>

---
 configure            |  16 ++++
 qga/commands-posix.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qga/commands-win32.c |   7 ++
 qga/qapi-schema.json |  54 +++++++++++
 4 files changed, 338 insertions(+)

diff --git a/configure b/configure
index f185dd0..ebfcc6a 100755
--- a/configure
+++ b/configure
@@ -3618,6 +3618,18 @@ if test "$darwin" != "yes" -a "$mingw32" != "yes" -a "$solaris" != yes -a \
 fi
 
 ##########################################
+# Do we need netcf
+netcf=no
+cat > $TMPC << EOF
+#include <netcf.h>
+int main(void) { return 0; }
+EOF
+if compile_prog "" "-lnetcf" ; then
+    netcf=yes
+    libs_qga="$libs_qga -lnetcf"
+fi
+
+##########################################
 # spice probe
 if test "$spice" != "no" ; then
   cat > $TMPC << EOF
@@ -4697,6 +4709,10 @@ if test "$spice" = "yes" ; then
   echo "CONFIG_SPICE=y" >> $config_host_mak
 fi
 
+if test "$netcf" = "yes" ; then
+  echo "CONFIG_NETCF=y" >> $config_host_mak
+fi
+
 if test "$smartcard_nss" = "yes" ; then
   echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak
   echo "NSS_LIBS=$nss_libs" >> $config_host_mak
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index f6f3e3c..5ee7949 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -46,6 +46,10 @@ extern char **environ;
 #include <sys/socket.h>
 #include <net/if.h>
 
+#ifdef CONFIG_NETCF
+#include <netcf.h>
+#endif
+
 #ifdef FIFREEZE
 #define CONFIG_FSFREEZE
 #endif
@@ -1719,6 +1723,263 @@ error:
     return NULL;
 }
 
+#ifdef CONFIG_NETCF
+static const char *interface_type_string[] = {
+    "bond",
+};
+
+static const char *ip_address_type_string[] = {
+    "ipv4",
+    "ipv6",
+};
+
+static char *parse_options(const char *str, const char *needle)
+{
+    char *start, *end, *buffer = NULL;
+    char *ret = NULL;
+
+    buffer = g_strdup(str);
+    start = buffer;
+    if ((start = strstr(start, needle))) {
+        start += strlen(needle);
+        end = strchr(start, ' ');
+        if (end) {
+            *end = '\0';
+        }
+        if (strlen(start) == 0) {
+            goto cleanup;
+        }
+        ret = g_strdup(start);
+    }
+
+cleanup:
+    g_free(buffer);
+    return ret;
+}
+
+/**
+ * @buffer: xml string data to be formatted
+ * @indent: indent number relative to first line
+ *
+ */
+static void adjust_indent(char **buffer, int indent)
+{
+    char spaces[1024];
+    int i;
+
+    if (!*buffer) {
+        return;
+    }
+
+    if (indent < 0 || indent >= 1024) {
+        return;
+    }
+    memset(spaces, 0, sizeof(spaces));
+    for (i = 0; i < indent; i++) {
+        spaces[i] = ' ';
+    }
+
+    sprintf(*buffer + strlen(*buffer), "%s", spaces);
+}
+
+static char *create_bond_interface(GuestNetworkInterface2 *interface)
+{
+    char *target_xml;
+
+    target_xml = g_malloc0(1024);
+    if (!target_xml) {
+        return NULL;
+    }
+
+    sprintf(target_xml, "<interface type='%s' name='%s'>\n",
+            interface_type_string[interface->type], interface->name);
+    adjust_indent(&target_xml, 2);
+    sprintf(target_xml + strlen(target_xml), "<start mode='%s'/>\n",
+            interface->has_onboot ? interface->onboot : "none");
+    if (interface->has_ip_address) {
+        GuestIpAddress *address_item = interface->ip_address;
+
+        adjust_indent(&target_xml, 2);
+        sprintf(target_xml + strlen(target_xml), "<protocol family='%s'>\n",
+                ip_address_type_string[address_item->ip_address_type]);
+        adjust_indent(&target_xml, 4);
+        sprintf(target_xml + strlen(target_xml), "<ip address='%s' prefix='%" PRId64 "'/>\n",
+                address_item->ip_address, address_item->prefix);
+        if (address_item->has_gateway) {
+            adjust_indent(&target_xml, 4);
+            sprintf(target_xml + strlen(target_xml), "<route gateway='%s'/>\n",
+                    address_item->gateway);
+        }
+        adjust_indent(&target_xml, 2);
+        sprintf(target_xml + strlen(target_xml), "%s\n", "</protocol>");
+    }
+
+    adjust_indent(&target_xml, 2);
+    if (interface->has_options) {
+        char *value;
+
+        value = parse_options(interface->options, "mode=");
+        if (value) {
+            sprintf(target_xml + strlen(target_xml), "<bond mode='%s'>\n",
+                    value);
+            g_free(value);
+        } else {
+            sprintf(target_xml + strlen(target_xml), "%s\n", "<bond>");
+        }
+
+        value = parse_options(interface->options, "miimon=");
+        if (value) {
+            adjust_indent(&target_xml, 4);
+            sprintf(target_xml + strlen(target_xml), "<miimon freq='%s'",
+                   value);
+            g_free(value);
+
+            value = parse_options(interface->options, "updelay=");
+            if (value) {
+                sprintf(target_xml + strlen(target_xml), " updelay='%s'",
+                        value);
+                g_free(value);
+            }
+            value = parse_options(interface->options, "downdelay=");
+            if (value) {
+                sprintf(target_xml + strlen(target_xml), " downdelay='%s'",
+                        value);
+                g_free(value);
+            }
+            value = parse_options(interface->options, "use_carrier=");
+            if (value) {
+                sprintf(target_xml + strlen(target_xml), " carrier='%s'",
+                        value);
+                g_free(value);
+            }
+
+            sprintf(target_xml + strlen(target_xml), "%s\n", "/>");
+        }
+
+        value = parse_options(interface->options, "arp_interval=");
+        if (value) {
+            adjust_indent(&target_xml, 4);
+            sprintf(target_xml + strlen(target_xml), "<arpmon interval='%s'",
+                    value);
+            g_free(value);
+
+            value = parse_options(interface->options, "arp_ip_target=");
+            if (value) {
+                sprintf(target_xml + strlen(target_xml), " target='%s'",
+                        value);
+                g_free(value);
+            }
+
+            value = parse_options(interface->options, "arp_validate=");
+            if (value) {
+                sprintf(target_xml + strlen(target_xml), " validate='%s'",
+                        value);
+                g_free(value);
+            }
+
+            sprintf(target_xml + strlen(target_xml), "%s\n", "/>");
+        }
+    } else {
+        sprintf(target_xml + strlen(target_xml), "%s\n", "<bond>");
+    }
+
+    if (interface->has_subInterfaces) {
+        GuestNetworkInterfaceList *head = interface->subInterfaces;
+
+        for (; head; head = head->next) {
+            adjust_indent(&target_xml, 4);
+            sprintf(target_xml + strlen(target_xml),
+                    "<interface type='ethernet' name='%s'/>\n",
+                    head->value->name);
+        }
+    }
+
+    adjust_indent(&target_xml, 2);
+    sprintf(target_xml + strlen(target_xml), "%s\n", "</bond>");
+    sprintf(target_xml + strlen(target_xml), "%s\n", "</interface>");
+
+    return target_xml;
+}
+
+static struct netcf *netcf;
+
+static void create_interface(GuestNetworkInterface2 *interface, Error **errp)
+{
+    int ret = -1;
+    struct netcf_if *iface;
+    unsigned int flags = 0;
+    char *target_xml;
+
+    /* open netcf */
+    if (netcf == NULL) {
+        if (ncf_init(&netcf, NULL) != 0) {
+            error_setg(errp, "netcf init failed");
+            return;
+        }
+    }
+
+    if (interface->type != GUEST_INTERFACE_TYPE_BOND) {
+        error_setg(errp, "interface type is not supported, only support 'bond' type");
+        return;
+    }
+
+   target_xml = create_bond_interface(interface);
+   if (!target_xml) {
+        error_setg(errp, "no enough memory spaces");
+        return;
+    }
+
+    iface = ncf_define(netcf, target_xml);
+    if (!iface) {
+        error_setg(errp, "netcf interface define failed");
+        g_free(target_xml);
+        goto cleanup;
+    }
+
+    g_free(target_xml);
+
+    if (ncf_if_status(iface, &flags) < 0) {
+        error_setg(errp, "netcf interface get status failed");
+        goto cleanup;
+    }
+
+    if (flags & NETCF_IFACE_ACTIVE) {
+        error_setg(errp, "interface is already running");
+        goto cleanup;
+    }
+
+    ret = ncf_if_up(iface);
+    if (ret < 0) {
+        error_setg(errp, "netcf interface up failed");
+        goto cleanup;
+    }
+
+ cleanup:
+    ncf_if_free(iface);
+}
+
+int64_t qmp_guest_network_set_interface(GuestNetworkInterface2 *interface,
+                                        Error **errp)
+{
+    Error *local_err = NULL;
+
+    create_interface(interface, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return -1;
+    }
+
+    return 0;
+}
+#else
+int64_t qmp_guest_network_set_interface(GuestNetworkInterface2 *interface,
+                                        Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return -1;
+}
+#endif
+
 #define SYSCONF_EXACT(name, errp) sysconf_exact((name), #name, (errp))
 
 static long sysconf_exact(int name, const char *name_str, Error **errp)
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 3bcbeae..4c14514 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -446,6 +446,13 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
     return -1;
 }
 
+int64_t qmp_guest_network_set_interface(GuestNetworkInterface2 *interface,
+                                        Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return -1;
+}
+
 /* add unsupported commands to the blacklist */
 GList *ga_command_blacklist_init(GList *blacklist)
 {
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 376e79f..77f499b 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -556,6 +556,7 @@
 { 'type': 'GuestIpAddress',
   'data': {'ip-address': 'str',
            'ip-address-type': 'GuestIpAddressType',
+           '*gateway': 'str',
            'prefix': 'int'} }
 
 ##
@@ -575,6 +576,43 @@
            '*ip-addresses': ['GuestIpAddress'] } }
 
 ##
+# @GuestInterfaceType:
+#
+# An enumeration of supported interface types
+#
+# @bond: bond device
+#
+# Since: 2.3
+##
+{ 'enum': 'GuestInterfaceType',
+  'data': [ 'bond' ] }
+
+##
+# @GuestNetworkInterface2:
+#
+# @type: the interface type which supported in enum GuestInterfaceType.
+#
+# @name: the interface name.
+#
+# @onboot: the interface start model.
+#
+# @ip-address: IP address.
+#
+# @options: the options argument.
+#
+# @subInterfaces: the slave interfaces.
+#
+# Since: 2.3
+##
+{ 'type': 'GuestNetworkInterface2',
+  'data': {'type': 'GuestInterfaceType',
+           'name': 'str',
+           '*onboot': 'str',
+           '*ip-address': 'GuestIpAddress',
+           '*options': 'str',
+           '*subInterfaces': ['GuestNetworkInterface'] } }
+
+##
 # @guest-network-get-interfaces:
 #
 # Get list of guest IP addresses, MAC addresses
@@ -588,6 +626,22 @@
   'returns': ['GuestNetworkInterface'] }
 
 ##
+# @guest-network-set-interface:
+#
+# Set guest network interface
+#
+# return: 0:      call successful.
+#
+#         -1:     call failed.
+#
+#
+# Since: 2.3
+##
+{ 'command': 'guest-network-set-interface',
+  'data'   : {'interface': 'GuestNetworkInterface2' },
+  'returns': 'int' }
+
+##
 # @GuestLogicalProcessor:
 #
 # @logical-id: Arbitrary guest-specific unique identifier of the VCPU.
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [Qemu-devel] [RFC 2/2] qemu-agent: add guest-network-delete-interface command
  2015-02-25  3:26 [Qemu-devel] [RFC 0/2] qemu-ga: add guest-network-set-interface command Chen Fan
  2015-02-25  3:26 ` [Qemu-devel] [RFC 1/2] qemu-agent: " Chen Fan
@ 2015-02-25  3:26 ` Chen Fan
  1 sibling, 0 replies; 3+ messages in thread
From: Chen Fan @ 2015-02-25  3:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: izumi.taku

Add a corresponding command to guest-network-set-interface.

Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
 qga/commands-posix.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qga/commands-win32.c |  6 ++++++
 qga/qapi-schema.json | 11 +++++++++++
 3 files changed, 68 insertions(+)

diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 5ee7949..058085f 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1971,6 +1971,51 @@ int64_t qmp_guest_network_set_interface(GuestNetworkInterface2 *interface,
 
     return 0;
 }
+
+int64_t qmp_guest_network_delete_interface(const char *name, Error **errp)
+{
+    struct netcf_if *iface;
+    int ret = -1;
+    unsigned int flags = 0;
+
+    /* open netcf */
+    if (netcf == NULL) {
+        if (ncf_init(&netcf, NULL) != 0) {
+            error_setg(errp, "netcf init failed");
+            return ret;
+        }
+    }
+
+    iface = ncf_lookup_by_name(netcf, name);
+    if (!iface) {
+       error_setg(errp, "couldn't find interface named '%s'", name);
+       return ret;
+    }
+
+    if (ncf_if_status(iface, &flags) < 0) {
+        error_setg(errp, "netcf interface get status failed");
+        goto cleanup;
+    }
+
+    if (flags & NETCF_IFACE_ACTIVE) {
+        ret = ncf_if_down(iface);
+        if (ret < 0) {
+            error_setg(errp, "netcf interface stop failed");
+            goto cleanup;
+        }
+    }
+
+    ret = ncf_if_undefine(iface);
+    if (ret < 0) {
+        error_setg(errp, "netcf interface delete failed");
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    ncf_if_free(iface);
+    return ret;
+}
 #else
 int64_t qmp_guest_network_set_interface(GuestNetworkInterface2 *interface,
                                         Error **errp)
@@ -1978,6 +2023,12 @@ int64_t qmp_guest_network_set_interface(GuestNetworkInterface2 *interface,
     error_set(errp, QERR_UNSUPPORTED);
     return -1;
 }
+
+int64_t qmp_guest_network_delete_interface(const char *name, Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return -1;
+}
 #endif
 
 #define SYSCONF_EXACT(name, errp) sysconf_exact((name), #name, (errp))
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 4c14514..52f6e47 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -453,6 +453,12 @@ int64_t qmp_guest_network_set_interface(GuestNetworkInterface2 *interface,
     return -1;
 }
 
+int64_t qmp_guest_network_delete_interface(const char *name, Error **errp)
+{
+    error_set(errp, QERR_UNSUPPORTED);
+    return -1;
+}
+
 /* add unsupported commands to the blacklist */
 GList *ga_command_blacklist_init(GList *blacklist)
 {
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 77f499b..b886f97 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -642,6 +642,17 @@
   'returns': 'int' }
 
 ##
+# @guest-network-delete-interface:
+#
+# @name: interface name.
+#
+# Since: 2.3
+##
+{ 'command': 'guest-network-delete-interface',
+  'data'   : {'name': 'str' },
+  'returns': 'int' }
+
+##
 # @GuestLogicalProcessor:
 #
 # @logical-id: Arbitrary guest-specific unique identifier of the VCPU.
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2015-02-25  3:33 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-02-25  3:26 [Qemu-devel] [RFC 0/2] qemu-ga: add guest-network-set-interface command Chen Fan
2015-02-25  3:26 ` [Qemu-devel] [RFC 1/2] qemu-agent: " Chen Fan
2015-02-25  3:26 ` [Qemu-devel] [RFC 2/2] qemu-agent: add guest-network-delete-interface command Chen Fan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).