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:
Daniel P. Berrange 2010-04-15 14:52:03 +01:00
parent 152ccceb61
commit ff45b4c26f
7 changed files with 269 additions and 71 deletions

View File

@ -7194,12 +7194,17 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &net->info) < 0)
goto cleanup;
vlan = qemuDomainNetVLAN(net);
if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
vlan = -1;
} else {
vlan = qemuDomainNetVLAN(net);
if (vlan < 0) {
qemuReportError(VIR_ERR_NO_SUPPORT, "%s",
_("Unable to attach network devices without vlan"));
goto cleanup;
if (vlan < 0) {
qemuReportError(VIR_ERR_NO_SUPPORT, "%s",
_("Unable to attach network devices without vlan"));
goto cleanup;
}
}
if (tapfd != -1) {
@ -7227,9 +7232,17 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
}
qemuDomainObjEnterMonitorWithDriver(driver, vm);
if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) {
qemuDomainObjExitMonitorWithDriver(driver, vm);
goto try_tapfd_close;
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);
@ -7286,7 +7299,20 @@ cleanup:
try_remove:
if (vlan < 0) {
VIR_WARN0(_("Unable to remove network backend"));
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,9 +8163,17 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver,
}
}
if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
qemuDomainObjExitMonitorWithDriver(driver, vm);
goto cleanup;
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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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:

View File

@ -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);