@@ -1009,8 +1009,7 @@ ETEXI
.args_type = "netdev:O",
.params = "[user|tap|socket],id=str[,prop=value][,...]",
.help = "add host network device",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_netdev_add,
+ .mhandler.cmd = hmp_netdev_add,
},
STEXI
@@ -943,3 +943,36 @@ void hmp_device_del(Monitor *mon, const QDict *qdict)
qmp_device_del(id, &err);
hmp_handle_error(mon, &err);
}
+
+void hmp_netdev_add(Monitor *mon, const QDict *qdict)
+{
+ const char *type = qdict_get_try_str(qdict, "type");
+ const char *id = qdict_get_try_str(qdict, "id");
+ QString *arglist = qstring_new();
+ bool has_arglist = false;
+ const QDictEntry *entry;
+ Error *local_err = NULL;
+
+ for (entry = qdict_first(qdict); entry;
+ entry = qdict_next(qdict, entry)) {
+ const char *key = qdict_entry_key(entry);
+ const char *value = qstring_get_str(qobject_to_qstring(qdict_entry_value(entry)));
+
+ if (strcmp(key, "type") == 0 || strcmp(key, "id") == 0) {
+ continue;
+ }
+
+ if (qstring_len(arglist) > 0) {
+ qstring_append(arglist, ",");
+ }
+
+ qstring_append(arglist, key);
+ qstring_append(arglist, "=");
+ qstring_append(arglist, value);
+ has_arglist = true;
+ }
+
+ qmp_netdev_add(type, id, has_arglist, qstring_get_str(arglist), &local_err);
+ hmp_handle_error(mon, &local_err);
+ QDECREF(arglist);
+}
@@ -61,5 +61,6 @@ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
void hmp_migrate(Monitor *mon, const QDict *qdict);
void hmp_device_del(Monitor *mon, const QDict *qdict);
+void hmp_netdev_add(Monitor *mon, const QDict *qdict);
#endif
@@ -39,6 +39,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
const char *devaddr,
const char *opts_str)
{
+ Error *local_err = NULL;
QemuOpts *opts;
PCIBus *bus;
int ret, devfn;
@@ -60,9 +61,12 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
qemu_opt_set(opts, "type", "nic");
- ret = net_client_init(opts, 0);
- if (ret < 0)
+ ret = net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
return NULL;
+ }
if (nd_table[ret].devaddr) {
monitor_printf(mon, "Parameter addr not supported\n");
return NULL;
@@ -1355,6 +1355,7 @@ static int usb_net_initfn(USBDevice *dev)
static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
{
+ Error *local_err = NULL;
USBDevice *dev;
QemuOpts *opts;
int idx;
@@ -1366,8 +1367,10 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
qemu_opt_set(opts, "type", "nic");
qemu_opt_set(opts, "model", "usb");
- idx = net_client_init(opts, 0);
- if (idx == -1) {
+ idx = net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
return NULL;
}
@@ -1081,7 +1081,7 @@ static const struct {
#endif /* CONFIG_NET_BRIDGE */
};
-int net_client_init(QemuOpts *opts, int is_netdev)
+int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
{
const char *name;
const char *type;
@@ -1089,7 +1089,7 @@ int net_client_init(QemuOpts *opts, int is_netdev)
type = qemu_opt_get(opts, "type");
if (!type) {
- qerror_report(QERR_MISSING_PARAMETER, "type");
+ error_set(errp, QERR_MISSING_PARAMETER, "type");
return -1;
}
@@ -1105,21 +1105,21 @@ int net_client_init(QemuOpts *opts, int is_netdev)
strcmp(type, "vde") != 0 &&
#endif
strcmp(type, "socket") != 0) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
- "a netdev backend type");
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
+ "a netdev backend type");
return -1;
}
if (qemu_opt_get(opts, "vlan")) {
- qerror_report(QERR_INVALID_PARAMETER, "vlan");
+ error_set(errp, QERR_INVALID_PARAMETER, "vlan");
return -1;
}
if (qemu_opt_get(opts, "name")) {
- qerror_report(QERR_INVALID_PARAMETER, "name");
+ error_set(errp, QERR_INVALID_PARAMETER, "name");
return -1;
}
if (!qemu_opts_id(opts)) {
- qerror_report(QERR_MISSING_PARAMETER, "id");
+ error_set(errp, QERR_MISSING_PARAMETER, "id");
return -1;
}
}
@@ -1138,8 +1138,7 @@ int net_client_init(QemuOpts *opts, int is_netdev)
qemu_opts_validate(opts, &net_client_types[i].desc[0], &local_err);
if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
return -1;
}
@@ -1155,7 +1154,7 @@ int net_client_init(QemuOpts *opts, int is_netdev)
ret = net_client_types[i].init(opts, name, vlan);
if (ret < 0) {
/* TODO push error reporting into init() methods */
- qerror_report(QERR_DEVICE_INIT_FAILED, type);
+ error_set(errp, QERR_DEVICE_INIT_FAILED, type);
return -1;
}
}
@@ -1163,8 +1162,8 @@ int net_client_init(QemuOpts *opts, int is_netdev)
}
}
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
- "a network client type");
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
+ "a network client type");
return -1;
}
@@ -1195,6 +1194,7 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *opts_str = qdict_get_try_str(qdict, "opts");
+ Error *local_err = NULL;
QemuOpts *opts;
if (!net_host_check_device(device)) {
@@ -1209,8 +1209,10 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
qemu_opt_set(opts, "type", device);
- if (net_client_init(opts, 0) < 0) {
+ net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
monitor_printf(mon, "adding host network device %s failed\n", device);
+ error_free(local_err);
}
}
@@ -1231,22 +1233,45 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict)
qemu_del_vlan_client(vc);
}
-int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_netdev_add(const char *type, const char *id,
+ bool has_props, const char *props, Error **errp)
{
+ QemuOptsList *opts_list;
+ Error *local_err = NULL;
QemuOpts *opts;
- int res;
- opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict);
- if (!opts) {
- return -1;
+ opts_list = qemu_find_opts_err("netdev", &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ opts = qemu_opts_create(opts_list, id, 1, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ qemu_opt_set_err(opts, "type", type, &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
+ }
+
+ qemu_opts_do_parse(opts, props, NULL, &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
}
- res = net_client_init(opts, 1);
- if (res < 0) {
- qemu_opts_del(opts);
+ net_client_init(opts, 1, &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
}
- return res;
+ return;
+
+exit_err:
+ error_propagate(errp, local_err);
+ qemu_opts_del(opts);
}
int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
@@ -1424,14 +1449,30 @@ void net_check_clients(void)
static int net_init_client(QemuOpts *opts, void *dummy)
{
- if (net_client_init(opts, 0) < 0)
+ Error *local_err = NULL;
+
+ net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ error_free(local_err);
return -1;
+ }
+
return 0;
}
static int net_init_netdev(QemuOpts *opts, void *dummy)
{
- return net_client_init(opts, 1);
+ Error *local_err = NULL;
+ int ret;
+
+ ret = net_client_init(opts, 1, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
+ }
+
+ return ret;
}
int net_init_clients(void)
@@ -163,14 +163,13 @@ struct HCIInfo *qemu_next_hci(void);
extern const char *legacy_tftp_prefix;
extern const char *legacy_bootp_filename;
-int net_client_init(QemuOpts *opts, int is_netdev);
+int net_client_init(QemuOpts *opts, int is_netdev, Error **errp);
int net_client_parse(QemuOptsList *opts_list, const char *str);
int net_init_clients(void);
void net_check_clients(void);
void net_cleanup(void);
void net_host_device_add(Monitor *mon, const QDict *qdict);
void net_host_device_remove(Monitor *mon, const QDict *qdict);
-int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
@@ -1721,3 +1721,31 @@
# Since: 0.14.0
##
{ 'command': 'device_del', 'data': {'id': 'str'} }
+
+##
+# @netdev_add:
+#
+# Add a network backend.
+#
+# @type: the type of network backend. Current valid values are 'user', 'tap',
+# 'vde', 'socket', 'dump' and 'bridge'
+#
+# @id: the name of the new network backend
+#
+# @props: #optional a list of properties to be passed to the backend in
+# the format 'name=value', like 'ifname=tap0,script=no'
+#
+# Notes: The semantics of @props is not well defined. Future commands will be
+# introduced that provide stronger typing for backend creation.
+#
+# Since: 0.14.0
+#
+# Returns: Nothing on success
+# If @type is not a valid network backend, DeviceNotFound
+# If @id is not a valid identifier, InvalidParameterValue
+# if @id already exists, DuplicateId
+# If @props contains an invalid parameter for this backend,
+# InvalidParameter
+##
+{ 'command': 'netdev_add',
+ 'data': {'type': 'str', 'id': 'str', '*props': '**'} }
@@ -606,10 +606,7 @@ EQMP
{
.name = "netdev_add",
.args_type = "netdev:O",
- .params = "[user|tap|socket],id=str[,prop=value][,...]",
- .help = "add host network device",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_netdev_add,
+ .mhandler.cmd_new = qmp_marshal_input_netdev_add,
},
SQMP