diff mbox

[RFC,1/3] qemu-agent: add guest-network-set-interface command

Message ID 97201fc4eb0dacc2480382ff75e3170fdc24ce7a.1429257498.git.chen.fan.fnst@cn.fujitsu.com
State New
Headers show

Commit Message

chenfan April 17, 2015, 8:53 a.m. UTC
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(+)

Comments

Olga Krishtal May 21, 2015, 1:52 p.m. UTC | #1
On 17/04/15 11:53, Chen Fan wrote:
> 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' }
I thought that usage of built-in types as the returning value is 
deprecated.
Lets return dictionary in guest-network-set (get)-interface
> +
> +##
>   # @GuestLogicalProcessor:
>   #
>   # @logical-id: Arbitrary guest-specific unique identifier of the VCPU.
Eric Blake May 21, 2015, 2:43 p.m. UTC | #2
On 05/21/2015 07:52 AM, Olga Krishtal wrote:
> On 17/04/15 11:53, Chen Fan wrote:
>> 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>
>> ---

>> @@ -588,6 +626,22 @@
>>     'returns': ['GuestNetworkInterface'] }
>>     ##
>> +# @guest-network-set-interface:
>> +#
>> +# Set guest network interface
>> +#
>> +# return: 0:      call successful.
>> +#
>> +#         -1:     call failed.
>> +#
>> +#
>> +# Since: 2.3

You've missed 2.3; if we still want this, it will need to be updated to 2.4.

>> +##
>> +{ 'command': 'guest-network-set-interface',
>> +  'data'   : {'interface': 'GuestNetworkInterface2' },
>> +  'returns': 'int' }
> I thought that usage of built-in types as the returning value is
> deprecated.
> Lets return dictionary in guest-network-set (get)-interface

Correct. Returning a non-dictionary now causes the generator to barf if
you don't update a whitelist.  But you don't even need a return value -
QGA is already set up to return {} on success and an error message on
failure, if you have nothing further to add.  Just omit 'returns' from
your 'command' definition.
diff mbox

Patch

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.