mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-11 15:27:47 +00:00
Add support for NIC hotplug using netdev_add in QEMU
QEMU is gaining a new monitor command netdev_add for hotplugging NICs using the netdev backend code. We already support this on the command this, though it is disabled. This adds support for hotplug too, also to remain disabled until 0.13 QEMU is released * src/qemu/qemu_driver.c: Support netdev hotplug for NICs * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h, src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add support for netdev_add and netdev_remove commands
This commit is contained in:
parent
152ccceb61
commit
ff45b4c26f
@ -7194,6 +7194,10 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
|
||||
qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &net->info) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
|
||||
(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
|
||||
vlan = -1;
|
||||
} else {
|
||||
vlan = qemuDomainNetVLAN(net);
|
||||
|
||||
if (vlan < 0) {
|
||||
@ -7201,6 +7205,7 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
|
||||
_("Unable to attach network devices without vlan"));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (tapfd != -1) {
|
||||
if (virAsprintf(&tapfd_name, "fd-%s", net->info.alias) < 0)
|
||||
@ -7227,10 +7232,18 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
|
||||
}
|
||||
|
||||
qemuDomainObjEnterMonitorWithDriver(driver, vm);
|
||||
if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
|
||||
(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
|
||||
if (qemuMonitorAddNetdev(priv->mon, netstr) < 0) {
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
goto try_tapfd_close;
|
||||
}
|
||||
} else {
|
||||
if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) {
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
goto try_tapfd_close;
|
||||
}
|
||||
}
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
|
||||
if (tapfd != -1)
|
||||
@ -7286,7 +7299,20 @@ cleanup:
|
||||
|
||||
try_remove:
|
||||
if (vlan < 0) {
|
||||
if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
|
||||
(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
|
||||
char *netdev_name;
|
||||
if (virAsprintf(&netdev_name, "host%s", net->info.alias) < 0)
|
||||
goto no_memory;
|
||||
qemuDomainObjEnterMonitorWithDriver(driver, vm);
|
||||
if (qemuMonitorRemoveNetdev(priv->mon, netdev_name) < 0)
|
||||
VIR_WARN(_("Failed to remove network backend for netdev %s"),
|
||||
netdev_name);
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
VIR_FREE(netdev_name);
|
||||
} else {
|
||||
VIR_WARN0(_("Unable to remove network backend"));
|
||||
}
|
||||
} else {
|
||||
char *hostnet_name;
|
||||
if (virAsprintf(&hostnet_name, "host%s", net->info.alias) < 0)
|
||||
@ -8137,10 +8163,18 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver,
|
||||
}
|
||||
}
|
||||
|
||||
if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
|
||||
(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
|
||||
if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) {
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
|
||||
virNWFilterTearNWFilter(detach);
|
||||
|
@ -1425,6 +1425,37 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon,
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorAddNetdev(qemuMonitorPtr mon,
|
||||
const char *netdevstr)
|
||||
{
|
||||
int ret;
|
||||
DEBUG("mon=%p, fd=%d netdevstr=%s",
|
||||
mon, mon->fd, netdevstr);
|
||||
|
||||
if (mon->json)
|
||||
ret = qemuMonitorJSONAddNetdev(mon, netdevstr);
|
||||
else
|
||||
ret = qemuMonitorTextAddNetdev(mon, netdevstr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorRemoveNetdev(qemuMonitorPtr mon,
|
||||
const char *alias)
|
||||
{
|
||||
int ret;
|
||||
DEBUG("mon=%p, fd=%d alias=%s",
|
||||
mon, mon->fd, alias);
|
||||
|
||||
if (mon->json)
|
||||
ret = qemuMonitorJSONRemoveNetdev(mon, alias);
|
||||
else
|
||||
ret = qemuMonitorTextRemoveNetdev(mon, alias);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorGetPtyPaths(qemuMonitorPtr mon,
|
||||
virHashTablePtr paths)
|
||||
{
|
||||
|
@ -327,6 +327,12 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon,
|
||||
int vlan,
|
||||
const char *netname);
|
||||
|
||||
int qemuMonitorAddNetdev(qemuMonitorPtr mon,
|
||||
const char *netdevstr);
|
||||
|
||||
int qemuMonitorRemoveNetdev(qemuMonitorPtr mon,
|
||||
const char *alias);
|
||||
|
||||
int qemuMonitorGetPtyPaths(qemuMonitorPtr mon,
|
||||
virHashTablePtr paths);
|
||||
|
||||
|
@ -453,6 +453,65 @@ error:
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qemuFreeKeywords(int nkeywords, char **keywords, char **values)
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < nkeywords ; i++) {
|
||||
VIR_FREE(keywords[i]);
|
||||
VIR_FREE(values[i]);
|
||||
}
|
||||
VIR_FREE(keywords);
|
||||
VIR_FREE(values);
|
||||
}
|
||||
|
||||
static virJSONValuePtr
|
||||
qemuMonitorJSONKeywordStringToJSON(const char *str, const char *firstkeyword)
|
||||
{
|
||||
virJSONValuePtr ret = NULL;
|
||||
char **keywords = NULL;
|
||||
char **values = NULL;
|
||||
int nkeywords = 0;
|
||||
int i;
|
||||
|
||||
if (!(ret = virJSONValueNewObject()))
|
||||
goto no_memory;
|
||||
|
||||
nkeywords = qemuParseKeywords(str, &keywords, &values, 1);
|
||||
|
||||
if (nkeywords < 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0 ; i < nkeywords ; i++) {
|
||||
if (values[i] == NULL) {
|
||||
if (i != 0) {
|
||||
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("unexpected empty keyword in %s"), str);
|
||||
goto error;
|
||||
} else {
|
||||
/* This 3rd arg isn't a typo - the way the parser works is
|
||||
* that the value ended up in the keyword field */
|
||||
if (virJSONValueObjectAppendString(ret, firstkeyword, keywords[i]) < 0)
|
||||
goto no_memory;
|
||||
}
|
||||
} else {
|
||||
if (virJSONValueObjectAppendString(ret, keywords[i], values[i]) < 0)
|
||||
goto no_memory;
|
||||
}
|
||||
}
|
||||
|
||||
qemuFreeKeywords(nkeywords, keywords, values);
|
||||
return ret;
|
||||
|
||||
no_memory:
|
||||
virReportOOMError();
|
||||
error:
|
||||
qemuFreeKeywords(nkeywords, keywords, values);
|
||||
virJSONValueFree(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
qemuMonitorEmitShutdown(mon);
|
||||
@ -1754,6 +1813,63 @@ int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon,
|
||||
const char *netdevstr)
|
||||
{
|
||||
int ret = -1;
|
||||
virJSONValuePtr cmd = NULL;
|
||||
virJSONValuePtr reply = NULL;
|
||||
virJSONValuePtr args = NULL;
|
||||
|
||||
cmd = qemuMonitorJSONMakeCommand("netdev_add", NULL);
|
||||
if (!cmd)
|
||||
return -1;
|
||||
|
||||
args = qemuMonitorJSONKeywordStringToJSON(netdevstr, "type");
|
||||
if (!args)
|
||||
goto cleanup;
|
||||
|
||||
if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) {
|
||||
virReportOOMError();
|
||||
goto cleanup;
|
||||
}
|
||||
args = NULL; /* obj owns reference to args now */
|
||||
|
||||
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
||||
|
||||
if (ret == 0)
|
||||
ret = qemuMonitorJSONCheckError(cmd, reply);
|
||||
|
||||
cleanup:
|
||||
virJSONValueFree(args);
|
||||
virJSONValueFree(cmd);
|
||||
virJSONValueFree(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon,
|
||||
const char *alias)
|
||||
{
|
||||
int ret;
|
||||
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("netdev_del",
|
||||
"s:id", alias,
|
||||
NULL);
|
||||
virJSONValuePtr reply = NULL;
|
||||
if (!cmd)
|
||||
return -1;
|
||||
|
||||
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
||||
|
||||
if (ret == 0)
|
||||
ret = qemuMonitorJSONCheckError(cmd, reply);
|
||||
|
||||
virJSONValueFree(cmd);
|
||||
virJSONValueFree(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Example return data
|
||||
*
|
||||
@ -1964,65 +2080,6 @@ int qemuMonitorJSONDelDevice(qemuMonitorPtr mon,
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qemuFreeKeywords(int nkeywords, char **keywords, char **values)
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < nkeywords ; i++) {
|
||||
VIR_FREE(keywords[i]);
|
||||
VIR_FREE(values[i]);
|
||||
}
|
||||
VIR_FREE(keywords);
|
||||
VIR_FREE(values);
|
||||
}
|
||||
|
||||
static virJSONValuePtr
|
||||
qemuMonitorJSONKeywordStringToJSON(const char *str, const char *firstkeyword)
|
||||
{
|
||||
virJSONValuePtr ret = NULL;
|
||||
char **keywords = NULL;
|
||||
char **values = NULL;
|
||||
int nkeywords = 0;
|
||||
int i;
|
||||
|
||||
if (!(ret = virJSONValueNewObject()))
|
||||
goto no_memory;
|
||||
|
||||
nkeywords = qemuParseKeywords(str, &keywords, &values, 1);
|
||||
|
||||
if (nkeywords < 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0 ; i < nkeywords ; i++) {
|
||||
if (values[i] == NULL) {
|
||||
if (i != 0) {
|
||||
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("unexpected empty keyword in %s"), str);
|
||||
goto error;
|
||||
} else {
|
||||
/* This 3rd arg isn't a typo - the way the parser works is
|
||||
* that the value ended up in the keyword field */
|
||||
if (virJSONValueObjectAppendString(ret, firstkeyword, keywords[i]) < 0)
|
||||
goto no_memory;
|
||||
}
|
||||
} else {
|
||||
if (virJSONValueObjectAppendString(ret, keywords[i], values[i]) < 0)
|
||||
goto no_memory;
|
||||
}
|
||||
}
|
||||
|
||||
qemuFreeKeywords(nkeywords, keywords, values);
|
||||
return ret;
|
||||
|
||||
no_memory:
|
||||
virReportOOMError();
|
||||
error:
|
||||
qemuFreeKeywords(nkeywords, keywords, values);
|
||||
virJSONValueFree(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorJSONAddDevice(qemuMonitorPtr mon,
|
||||
const char *devicestr)
|
||||
{
|
||||
|
@ -155,6 +155,12 @@ int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
|
||||
int vlan,
|
||||
const char *netname);
|
||||
|
||||
int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon,
|
||||
const char *netdevstr);
|
||||
|
||||
int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon,
|
||||
const char *alias);
|
||||
|
||||
int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon,
|
||||
virHashTablePtr paths);
|
||||
|
||||
|
@ -1820,6 +1820,64 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorTextAddNetdev(qemuMonitorPtr mon,
|
||||
const char *netdevstr)
|
||||
{
|
||||
char *cmd;
|
||||
char *reply = NULL;
|
||||
int ret = -1;
|
||||
|
||||
if (virAsprintf(&cmd, "netdev_add %s", netdevstr) < 0) {
|
||||
virReportOOMError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
|
||||
qemuReportError(VIR_ERR_OPERATION_FAILED,
|
||||
_("failed to add netdev with '%s'"), cmd);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* XXX error messages here ? */
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(cmd);
|
||||
VIR_FREE(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon,
|
||||
const char *alias)
|
||||
{
|
||||
char *cmd;
|
||||
char *reply = NULL;
|
||||
int ret = -1;
|
||||
|
||||
if (virAsprintf(&cmd, "netdev_del %s", alias) < 0) {
|
||||
virReportOOMError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
|
||||
qemuReportError(VIR_ERR_OPERATION_FAILED,
|
||||
_("failed to remove netdev in qemu with '%s'"), cmd);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* XXX error messages here ? */
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(cmd);
|
||||
VIR_FREE(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Parse the output of "info chardev" and return a hash of pty paths.
|
||||
*
|
||||
* Output is:
|
||||
|
@ -154,6 +154,12 @@ int qemuMonitorTextRemoveHostNetwork(qemuMonitorPtr mon,
|
||||
int vlan,
|
||||
const char *netname);
|
||||
|
||||
int qemuMonitorTextAddNetdev(qemuMonitorPtr mon,
|
||||
const char *netdevstr);
|
||||
|
||||
int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon,
|
||||
const char *alias);
|
||||
|
||||
int qemuMonitorTextGetPtyPaths(qemuMonitorPtr mon,
|
||||
virHashTablePtr paths);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user