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

Olga Krishtal okrishtal at parallels.com
Thu May 21 13:52:38 UTC 2015


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 at 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.




More information about the libvir-list mailing list