mirror of https://gitee.com/openkylin/libvirt.git
2365 lines
65 KiB
C
2365 lines
65 KiB
C
/*
|
|
* virsh-network.c: Commands to manage network
|
|
*
|
|
* Copyright (C) 2005, 2007-2019 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "virsh-network.h"
|
|
#include "virsh-util.h"
|
|
|
|
#include "internal.h"
|
|
#include "viralloc.h"
|
|
#include "virfile.h"
|
|
#include "virtime.h"
|
|
#include "conf/network_conf.h"
|
|
#include "vsh-table.h"
|
|
#include "virenum.h"
|
|
|
|
#define VIRSH_COMMON_OPT_NETWORK(_helpstr, cflags) \
|
|
{.name = "network", \
|
|
.type = VSH_OT_DATA, \
|
|
.flags = VSH_OFLAG_REQ, \
|
|
.help = _helpstr, \
|
|
.completer = virshNetworkNameCompleter, \
|
|
.completer_flags = cflags, \
|
|
}
|
|
|
|
#define VIRSH_COMMON_OPT_NETWORK_FULL(cflags) \
|
|
VIRSH_COMMON_OPT_NETWORK(N_("network name or uuid"), cflags)
|
|
|
|
#define VIRSH_COMMON_OPT_NETWORK_OT_STRING(_helpstr, cflags) \
|
|
{.name = "network", \
|
|
.type = VSH_OT_STRING, \
|
|
.help = _helpstr, \
|
|
.completer = virshNetworkNameCompleter, \
|
|
.completer_flags = cflags, \
|
|
}
|
|
|
|
#define VIRSH_COMMON_OPT_NETWORK_OT_STRING_FULL(cflags) \
|
|
VIRSH_COMMON_OPT_NETWORK_OT_STRING(N_("network name or uuid"), cflags)
|
|
|
|
#define VIRSH_COMMON_OPT_NETWORK_PORT(cflags) \
|
|
{.name = "port", \
|
|
.type = VSH_OT_DATA, \
|
|
.flags = VSH_OFLAG_REQ, \
|
|
.help = N_("port UUID"), \
|
|
.completer = virshNetworkPortUUIDCompleter, \
|
|
.completer_flags = cflags, \
|
|
}
|
|
|
|
|
|
virNetworkPtr
|
|
virshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
|
|
const char **name, unsigned int flags)
|
|
{
|
|
virNetworkPtr network = NULL;
|
|
const char *n = NULL;
|
|
const char *optname = "network";
|
|
virshControl *priv = ctl->privData;
|
|
|
|
virCheckFlags(VIRSH_BYUUID | VIRSH_BYNAME, NULL);
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
|
|
return NULL;
|
|
|
|
vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
|
|
cmd->def->name, optname, n);
|
|
|
|
if (name)
|
|
*name = n;
|
|
|
|
/* try it by UUID */
|
|
if ((flags & VIRSH_BYUUID) && strlen(n) == VIR_UUID_STRING_BUFLEN-1) {
|
|
vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
|
|
cmd->def->name, optname);
|
|
network = virNetworkLookupByUUIDString(priv->conn, n);
|
|
}
|
|
/* try it by NAME */
|
|
if (!network && (flags & VIRSH_BYNAME)) {
|
|
vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network NAME\n",
|
|
cmd->def->name, optname);
|
|
network = virNetworkLookupByName(priv->conn, n);
|
|
}
|
|
|
|
if (!network)
|
|
vshError(ctl, _("failed to get network '%1$s'"), n);
|
|
|
|
return network;
|
|
}
|
|
|
|
|
|
virNetworkPortPtr
|
|
virshCommandOptNetworkPort(vshControl *ctl, const vshCmd *cmd,
|
|
virNetworkPtr net,
|
|
const char **name)
|
|
{
|
|
virNetworkPortPtr port = NULL;
|
|
const char *n = NULL;
|
|
const char *optname = "port";
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
|
|
return NULL;
|
|
|
|
vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
|
|
cmd->def->name, optname, n);
|
|
|
|
if (name)
|
|
*name = n;
|
|
|
|
vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
|
|
cmd->def->name, optname);
|
|
port = virNetworkPortLookupByUUIDString(net, n);
|
|
|
|
if (!port)
|
|
vshError(ctl, _("failed to get network port '%1$s'"), n);
|
|
|
|
return port;
|
|
}
|
|
|
|
/*
|
|
* "net-autostart" command
|
|
*/
|
|
static const vshCmdInfo info_network_autostart[] = {
|
|
{.name = "help",
|
|
.data = N_("autostart a network")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Configure a network to be automatically started at boot.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_autostart[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_PERSISTENT),
|
|
{.name = "disable",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("disable autostarting")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
const char *name;
|
|
int autostart;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
|
|
return false;
|
|
|
|
autostart = !vshCommandOptBool(cmd, "disable");
|
|
|
|
if (virNetworkSetAutostart(network, autostart) < 0) {
|
|
if (autostart)
|
|
vshError(ctl, _("failed to mark network %1$s as autostarted"), name);
|
|
else
|
|
vshError(ctl, _("failed to unmark network %1$s as autostarted"), name);
|
|
return false;
|
|
}
|
|
|
|
if (autostart)
|
|
vshPrintExtra(ctl, _("Network %1$s marked as autostarted\n"), name);
|
|
else
|
|
vshPrintExtra(ctl, _("Network %1$s unmarked as autostarted\n"), name);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "net-create" command
|
|
*/
|
|
static const vshCmdInfo info_network_create[] = {
|
|
{.name = "help",
|
|
.data = N_("create a network from an XML file")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Create a network.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_create[] = {
|
|
VIRSH_COMMON_OPT_FILE(N_("file containing an XML network description")),
|
|
{.name = "validate",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("validate the XML against the schema")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
const char *from = NULL;
|
|
g_autofree char *buffer = NULL;
|
|
unsigned int flags = 0;
|
|
virshControl *priv = ctl->privData;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
|
|
return false;
|
|
|
|
if (vshCommandOptBool(cmd, "validate"))
|
|
flags |= VIR_NETWORK_CREATE_VALIDATE;
|
|
|
|
if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
|
|
return false;
|
|
|
|
if (flags)
|
|
network = virNetworkCreateXMLFlags(priv->conn, buffer, flags);
|
|
else
|
|
network = virNetworkCreateXML(priv->conn, buffer);
|
|
|
|
if (!network) {
|
|
vshError(ctl, _("Failed to create network from %1$s"), from);
|
|
return false;
|
|
}
|
|
|
|
vshPrintExtra(ctl, _("Network %1$s created from %2$s\n"),
|
|
virNetworkGetName(network), from);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "net-define" command
|
|
*/
|
|
static const vshCmdInfo info_network_define[] = {
|
|
{.name = "help",
|
|
.data = N_("define an inactive persistent virtual network or modify "
|
|
"an existing persistent one from an XML file")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Define or modify a persistent virtual network.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_define[] = {
|
|
VIRSH_COMMON_OPT_FILE(N_("file containing an XML network description")),
|
|
{.name = "validate",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("validate the XML against the schema")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
const char *from = NULL;
|
|
g_autofree char *buffer = NULL;
|
|
unsigned int flags = 0;
|
|
virshControl *priv = ctl->privData;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
|
|
return false;
|
|
|
|
if (vshCommandOptBool(cmd, "validate"))
|
|
flags |= VIR_NETWORK_DEFINE_VALIDATE;
|
|
|
|
if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
|
|
return false;
|
|
|
|
if (flags)
|
|
network = virNetworkDefineXMLFlags(priv->conn, buffer, flags);
|
|
else
|
|
network = virNetworkDefineXML(priv->conn, buffer);
|
|
|
|
if (!network) {
|
|
vshError(ctl, _("Failed to define network from %1$s"), from);
|
|
return false;
|
|
}
|
|
|
|
vshPrintExtra(ctl, _("Network %1$s defined from %2$s\n"),
|
|
virNetworkGetName(network), from);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "net-destroy" command
|
|
*/
|
|
static const vshCmdInfo info_network_destroy[] = {
|
|
{.name = "help",
|
|
.data = N_("destroy (stop) a network")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Forcefully stop a given network.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_destroy[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
bool ret = true;
|
|
const char *name;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
|
|
return false;
|
|
|
|
if (virNetworkDestroy(network) == 0) {
|
|
vshPrintExtra(ctl, _("Network %1$s destroyed\n"), name);
|
|
} else {
|
|
vshError(ctl, _("Failed to destroy network %1$s"), name);
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "net-desc" command
|
|
*/
|
|
static const vshCmdInfo info_network_desc[] = {
|
|
{.name = "help",
|
|
.data = N_("show or set network's description or title")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Allows setting or modifying the description or title of a network.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_desc[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(0),
|
|
VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")),
|
|
VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")),
|
|
VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")),
|
|
{.name = "title",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("modify/get the title instead of description")
|
|
},
|
|
{.name = "edit",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("open an editor to modify the description")
|
|
},
|
|
{.name = "new-desc",
|
|
.type = VSH_OT_ARGV,
|
|
.help = N_("message")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
/* extract description or title from network xml */
|
|
static char *
|
|
virshGetNetworkDescription(vshControl *ctl, virNetworkPtr net,
|
|
bool title, unsigned int flags,
|
|
unsigned int queryflags)
|
|
{
|
|
char *desc = NULL;
|
|
g_autoptr(xmlDoc) doc = NULL;
|
|
g_autoptr(xmlXPathContext) ctxt = NULL;
|
|
int type;
|
|
|
|
if (title)
|
|
type = VIR_NETWORK_METADATA_TITLE;
|
|
else
|
|
type = VIR_NETWORK_METADATA_DESCRIPTION;
|
|
|
|
if ((desc = virNetworkGetMetadata(net, type, NULL, flags))) {
|
|
return desc;
|
|
} else {
|
|
int errCode = virGetLastErrorCode();
|
|
|
|
if (errCode == VIR_ERR_NO_NETWORK_METADATA) {
|
|
desc = g_strdup("");
|
|
vshResetLibvirtError();
|
|
return desc;
|
|
}
|
|
|
|
if (errCode != VIR_ERR_NO_SUPPORT)
|
|
return desc;
|
|
}
|
|
|
|
/* fall back to xml */
|
|
if (virshNetworkGetXMLFromNet(ctl, net, queryflags, &doc, &ctxt) < 0)
|
|
return NULL;
|
|
|
|
if (title)
|
|
desc = virXPathString("string(./title[1])", ctxt);
|
|
else
|
|
desc = virXPathString("string(./description[1])", ctxt);
|
|
|
|
if (!desc)
|
|
desc = g_strdup("");
|
|
|
|
return desc;
|
|
}
|
|
|
|
static bool
|
|
cmdNetworkDesc(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) net = NULL;
|
|
bool config = vshCommandOptBool(cmd, "config");
|
|
bool live = vshCommandOptBool(cmd, "live");
|
|
bool current = vshCommandOptBool(cmd, "current");
|
|
bool title = vshCommandOptBool(cmd, "title");
|
|
bool edit = vshCommandOptBool(cmd, "edit");
|
|
|
|
int type;
|
|
g_autofree char *descArg = NULL;
|
|
const vshCmdOpt *opt = NULL;
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
|
unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT;
|
|
unsigned int queryflags = 0;
|
|
|
|
VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
|
|
VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
|
|
|
|
if (config) {
|
|
flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG;
|
|
queryflags |= VIR_NETWORK_XML_INACTIVE;
|
|
}
|
|
if (live)
|
|
flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE;
|
|
|
|
if (!(net = virshCommandOptNetwork(ctl, cmd, NULL)))
|
|
return false;
|
|
|
|
if (title)
|
|
type = VIR_NETWORK_METADATA_TITLE;
|
|
else
|
|
type = VIR_NETWORK_METADATA_DESCRIPTION;
|
|
|
|
while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
|
|
virBufferAsprintf(&buf, "%s ", opt->data);
|
|
|
|
virBufferTrim(&buf, " ");
|
|
|
|
descArg = virBufferContentAndReset(&buf);
|
|
|
|
if (edit || descArg) {
|
|
g_autofree char *descNet = NULL;
|
|
g_autofree char *descNew = NULL;
|
|
|
|
if (!(descNet = virshGetNetworkDescription(ctl, net, title, flags, queryflags)))
|
|
return false;
|
|
|
|
if (!descArg)
|
|
descArg = g_strdup(descNet);
|
|
|
|
if (edit) {
|
|
g_autoptr(vshTempFile) tmp = NULL;
|
|
g_autofree char *desc_edited = NULL;
|
|
char *tmpstr;
|
|
|
|
/* Create and open the temporary file. */
|
|
if (!(tmp = vshEditWriteToTempFile(ctl, descArg)))
|
|
return false;
|
|
|
|
/* Start the editor. */
|
|
if (vshEditFile(ctl, tmp) == -1)
|
|
return false;
|
|
|
|
/* Read back the edited file. */
|
|
if (!(desc_edited = vshEditReadBackFile(ctl, tmp)))
|
|
return false;
|
|
|
|
/* strip a possible newline at the end of file; some
|
|
* editors enforce a newline, this makes editing the title
|
|
* more convenient */
|
|
if (title &&
|
|
(tmpstr = strrchr(desc_edited, '\n')) &&
|
|
*(tmpstr+1) == '\0')
|
|
*tmpstr = '\0';
|
|
|
|
/* Compare original XML with edited. Has it changed at all? */
|
|
if (STREQ(descNet, desc_edited)) {
|
|
if (title)
|
|
vshPrintExtra(ctl, "%s", _("Network title not changed\n"));
|
|
else
|
|
vshPrintExtra(ctl, "%s", _("Network description not changed\n"));
|
|
|
|
return true;
|
|
}
|
|
|
|
descNew = g_steal_pointer(&desc_edited);
|
|
} else {
|
|
descNew = g_steal_pointer(&descArg);
|
|
}
|
|
|
|
if (virNetworkSetMetadata(net, type, descNew, NULL, NULL, flags) < 0) {
|
|
if (title)
|
|
vshError(ctl, "%s", _("Failed to set new network title"));
|
|
else
|
|
vshError(ctl, "%s", _("Failed to set new network description"));
|
|
|
|
return false;
|
|
}
|
|
|
|
if (title)
|
|
vshPrintExtra(ctl, "%s", _("Network title updated successfully"));
|
|
else
|
|
vshPrintExtra(ctl, "%s", _("Network description updated successfully"));
|
|
|
|
} else {
|
|
g_autofree char *desc = virshGetNetworkDescription(ctl, net, title, flags, queryflags);
|
|
if (!desc)
|
|
return false;
|
|
|
|
if (strlen(desc) > 0) {
|
|
vshPrint(ctl, "%s", desc);
|
|
} else {
|
|
if (title)
|
|
vshPrintExtra(ctl, _("No title for network: %1$s"), virNetworkGetName(net));
|
|
else
|
|
vshPrintExtra(ctl, _("No description for network: %1$s"), virNetworkGetName(net));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "net-metadata" command
|
|
*/
|
|
static const vshCmdInfo info_network_metadata[] = {
|
|
{.name = "help",
|
|
.data = N_("show or set network's custom XML metadata")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Shows or modifies the XML metadata of a network.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_metadata[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(0),
|
|
{.name = "uri",
|
|
.type = VSH_OT_DATA,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.help = N_("URI of the namespace")
|
|
},
|
|
VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")),
|
|
VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")),
|
|
VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")),
|
|
{.name = "edit",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("use an editor to change the metadata")
|
|
},
|
|
{.name = "key",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("key to be used as a namespace identifier"),
|
|
},
|
|
{.name = "set",
|
|
.type = VSH_OT_STRING,
|
|
.completer = virshCompleteEmpty,
|
|
.help = N_("new metadata to set"),
|
|
},
|
|
{.name = "remove",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("remove the metadata corresponding to an uri")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
/* helper to add new metadata using the --edit option */
|
|
static char *
|
|
virshNetworkGetEditMetadata(vshControl *ctl G_GNUC_UNUSED,
|
|
virNetworkPtr net,
|
|
const char *uri,
|
|
unsigned int flags)
|
|
{
|
|
char *ret;
|
|
|
|
if (!(ret = virNetworkGetMetadata(net, VIR_NETWORK_METADATA_ELEMENT,
|
|
uri, flags))) {
|
|
vshResetLibvirtError();
|
|
ret = g_strdup("\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool
|
|
cmdNetworkMetadata(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) net = NULL;
|
|
bool config = vshCommandOptBool(cmd, "config");
|
|
bool live = vshCommandOptBool(cmd, "live");
|
|
bool current = vshCommandOptBool(cmd, "current");
|
|
bool edit = vshCommandOptBool(cmd, "edit");
|
|
bool rem = vshCommandOptBool(cmd, "remove");
|
|
const char *set = NULL;
|
|
const char *uri = NULL;
|
|
const char *key = NULL;
|
|
unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT;
|
|
bool ret = false;
|
|
|
|
VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
|
|
VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
|
|
VSH_EXCLUSIVE_OPTIONS("edit", "set");
|
|
VSH_EXCLUSIVE_OPTIONS("remove", "set");
|
|
VSH_EXCLUSIVE_OPTIONS("remove", "edit");
|
|
|
|
if (config)
|
|
flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG;
|
|
if (live)
|
|
flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE;
|
|
|
|
if (!(net = virshCommandOptNetwork(ctl, cmd, NULL)))
|
|
return false;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "uri", &uri) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "key", &key) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "set", &set) < 0)
|
|
return false;
|
|
|
|
if ((set || edit) && !key) {
|
|
vshError(ctl, "%s",
|
|
_("namespace key is required when modifying metadata"));
|
|
return false;
|
|
}
|
|
|
|
if (set || rem) {
|
|
if (virNetworkSetMetadata(net, VIR_NETWORK_METADATA_ELEMENT,
|
|
set, key, uri, flags))
|
|
return false;
|
|
|
|
if (rem)
|
|
vshPrintExtra(ctl, "%s\n", _("Metadata removed"));
|
|
else
|
|
vshPrintExtra(ctl, "%s\n", _("Metadata modified"));
|
|
} else if (edit) {
|
|
#define EDIT_GET_XML \
|
|
virshNetworkGetEditMetadata(ctl, net, uri, flags)
|
|
#define EDIT_NOT_CHANGED \
|
|
do { \
|
|
vshPrintExtra(ctl, "%s", _("Metadata not changed")); \
|
|
ret = true; \
|
|
goto edit_cleanup; \
|
|
} while (0)
|
|
|
|
#define EDIT_DEFINE \
|
|
(virNetworkSetMetadata(net, VIR_NETWORK_METADATA_ELEMENT, doc_edited, \
|
|
key, uri, flags) == 0)
|
|
#include "virsh-edit.c"
|
|
|
|
vshPrintExtra(ctl, "%s\n", _("Metadata modified"));
|
|
} else {
|
|
g_autofree char *data = NULL;
|
|
|
|
/* get */
|
|
if (!(data = virNetworkGetMetadata(net, VIR_NETWORK_METADATA_ELEMENT,
|
|
uri, flags)))
|
|
return false;
|
|
|
|
vshPrint(ctl, "%s\n", data);
|
|
}
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "net-dumpxml" command
|
|
*/
|
|
static const vshCmdInfo info_network_dumpxml[] = {
|
|
{.name = "help",
|
|
.data = N_("network information in XML")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Output the network information as an XML dump to stdout.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_dumpxml[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(0),
|
|
{.name = "inactive",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("show inactive defined XML")
|
|
},
|
|
{.name = "xpath",
|
|
.type = VSH_OT_STRING,
|
|
.flags = VSH_OFLAG_REQ_OPT,
|
|
.completer = virshCompleteEmpty,
|
|
.help = N_("xpath expression to filter the XML document")
|
|
},
|
|
{.name = "wrap",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("wrap xpath results in an common root element"),
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
g_autofree char *xml = NULL;
|
|
unsigned int flags = 0;
|
|
bool wrap = vshCommandOptBool(cmd, "wrap");
|
|
const char *xpath = NULL;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
|
|
return false;
|
|
|
|
if (vshCommandOptBool(cmd, "inactive"))
|
|
flags |= VIR_NETWORK_XML_INACTIVE;
|
|
|
|
if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
|
|
return false;
|
|
|
|
if (!(xml = virNetworkGetXMLDesc(network, flags)))
|
|
return false;
|
|
|
|
return virshDumpXML(ctl, xml, "network", xpath, wrap);
|
|
}
|
|
|
|
/*
|
|
* "net-info" command
|
|
*/
|
|
static const vshCmdInfo info_network_info[] = {
|
|
{.name = "help",
|
|
.data = N_("network information")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Returns basic information about the network")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_info[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(0),
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
char uuid[VIR_UUID_STRING_BUFLEN];
|
|
int autostart;
|
|
int persistent = -1;
|
|
int active = -1;
|
|
g_autofree char *bridge = NULL;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
|
|
return false;
|
|
|
|
vshPrint(ctl, "%-15s %s\n", _("Name:"), virNetworkGetName(network));
|
|
|
|
if (virNetworkGetUUIDString(network, uuid) == 0)
|
|
vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
|
|
|
|
active = virNetworkIsActive(network);
|
|
if (active >= 0)
|
|
vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no"));
|
|
|
|
persistent = virNetworkIsPersistent(network);
|
|
if (persistent < 0)
|
|
vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
|
|
else
|
|
vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
|
|
|
|
if (virNetworkGetAutostart(network, &autostart) < 0)
|
|
vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
|
|
else
|
|
vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
|
|
|
|
bridge = virNetworkGetBridgeName(network);
|
|
if (bridge)
|
|
vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge);
|
|
|
|
return true;
|
|
}
|
|
|
|
static int
|
|
virshNetworkSorter(const void *a,
|
|
const void *b,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
virNetworkPtr *na = (virNetworkPtr *) a;
|
|
virNetworkPtr *nb = (virNetworkPtr *) b;
|
|
|
|
if (*na && !*nb)
|
|
return -1;
|
|
|
|
if (!*na)
|
|
return *nb != NULL;
|
|
|
|
return vshStrcasecmp(virNetworkGetName(*na),
|
|
virNetworkGetName(*nb));
|
|
}
|
|
|
|
struct virshNetworkList {
|
|
virNetworkPtr *nets;
|
|
size_t nnets;
|
|
};
|
|
|
|
static void
|
|
virshNetworkListFree(struct virshNetworkList *list)
|
|
{
|
|
size_t i;
|
|
|
|
if (list && list->nets) {
|
|
for (i = 0; i < list->nnets; i++) {
|
|
virshNetworkFree(list->nets[i]);
|
|
}
|
|
g_free(list->nets);
|
|
}
|
|
g_free(list);
|
|
}
|
|
|
|
static struct virshNetworkList *
|
|
virshNetworkListCollect(vshControl *ctl,
|
|
unsigned int flags)
|
|
{
|
|
struct virshNetworkList *list = g_new0(struct virshNetworkList, 1);
|
|
size_t i;
|
|
int ret;
|
|
char **names = NULL;
|
|
virNetworkPtr net;
|
|
bool success = false;
|
|
size_t deleted = 0;
|
|
int persistent;
|
|
int autostart;
|
|
int nActiveNets = 0;
|
|
int nInactiveNets = 0;
|
|
int nAllNets = 0;
|
|
virshControl *priv = ctl->privData;
|
|
|
|
/* try the list with flags support (0.10.2 and later) */
|
|
if ((ret = virConnectListAllNetworks(priv->conn,
|
|
&list->nets,
|
|
flags)) >= 0) {
|
|
list->nnets = ret;
|
|
goto finished;
|
|
}
|
|
|
|
/* check if the command is actually supported */
|
|
if (last_error && last_error->code == VIR_ERR_NO_SUPPORT)
|
|
goto fallback;
|
|
|
|
if (last_error && last_error->code == VIR_ERR_INVALID_ARG) {
|
|
/* try the new API again but mask non-guaranteed flags */
|
|
unsigned int newflags = flags & (VIR_CONNECT_LIST_NETWORKS_ACTIVE |
|
|
VIR_CONNECT_LIST_NETWORKS_INACTIVE);
|
|
|
|
vshResetLibvirtError();
|
|
if ((ret = virConnectListAllNetworks(priv->conn, &list->nets,
|
|
newflags)) >= 0) {
|
|
list->nnets = ret;
|
|
goto filter;
|
|
}
|
|
}
|
|
|
|
/* there was an error during the first or second call */
|
|
vshError(ctl, "%s", _("Failed to list networks"));
|
|
goto cleanup;
|
|
|
|
|
|
fallback:
|
|
/* fall back to old method (0.10.1 and older) */
|
|
vshResetLibvirtError();
|
|
|
|
/* Get the number of active networks */
|
|
if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
|
|
VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
|
|
if ((nActiveNets = virConnectNumOfNetworks(priv->conn)) < 0) {
|
|
vshError(ctl, "%s", _("Failed to get the number of active networks"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Get the number of inactive networks */
|
|
if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
|
|
VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_INACTIVE)) {
|
|
if ((nInactiveNets = virConnectNumOfDefinedNetworks(priv->conn)) < 0) {
|
|
vshError(ctl, "%s", _("Failed to get the number of inactive networks"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
nAllNets = nActiveNets + nInactiveNets;
|
|
|
|
if (nAllNets == 0)
|
|
return list;
|
|
|
|
names = g_new0(char *, nAllNets);
|
|
|
|
/* Retrieve a list of active network names */
|
|
if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
|
|
VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
|
|
if (virConnectListNetworks(priv->conn,
|
|
names, nActiveNets) < 0) {
|
|
vshError(ctl, "%s", _("Failed to list active networks"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Add the inactive networks to the end of the name list */
|
|
if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
|
|
VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
|
|
if (virConnectListDefinedNetworks(priv->conn,
|
|
&names[nActiveNets],
|
|
nInactiveNets) < 0) {
|
|
vshError(ctl, "%s", _("Failed to list inactive networks"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
list->nets = g_new0(virNetworkPtr, nAllNets);
|
|
list->nnets = 0;
|
|
|
|
/* get active networks */
|
|
for (i = 0; i < nActiveNets; i++) {
|
|
if (!(net = virNetworkLookupByName(priv->conn, names[i])))
|
|
continue;
|
|
list->nets[list->nnets++] = net;
|
|
}
|
|
|
|
/* get inactive networks */
|
|
for (i = 0; i < nInactiveNets; i++) {
|
|
if (!(net = virNetworkLookupByName(priv->conn, names[i])))
|
|
continue;
|
|
list->nets[list->nnets++] = net;
|
|
}
|
|
|
|
/* truncate networks that weren't found */
|
|
deleted = nAllNets - list->nnets;
|
|
|
|
filter:
|
|
/* filter list the list if the list was acquired by fallback means */
|
|
for (i = 0; i < list->nnets; i++) {
|
|
net = list->nets[i];
|
|
|
|
/* persistence filter */
|
|
if (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT)) {
|
|
if ((persistent = virNetworkIsPersistent(net)) < 0) {
|
|
vshError(ctl, "%s", _("Failed to get network persistence info"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!((VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) && persistent) ||
|
|
(VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) && !persistent)))
|
|
goto remove_entry;
|
|
}
|
|
|
|
/* autostart filter */
|
|
if (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART)) {
|
|
if (virNetworkGetAutostart(net, &autostart) < 0) {
|
|
vshError(ctl, "%s", _("Failed to get network autostart state"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!((VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) && autostart) ||
|
|
(VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) && !autostart)))
|
|
goto remove_entry;
|
|
}
|
|
/* the pool matched all filters, it may stay */
|
|
continue;
|
|
|
|
remove_entry:
|
|
/* the pool has to be removed as it failed one of the filters */
|
|
g_clear_pointer(&list->nets[i], virshNetworkFree);
|
|
deleted++;
|
|
}
|
|
|
|
finished:
|
|
/* sort the list */
|
|
if (list->nets && list->nnets) {
|
|
g_qsort_with_data(list->nets, list->nnets,
|
|
sizeof(*list->nets), virshNetworkSorter, NULL);
|
|
}
|
|
|
|
/* truncate the list if filter simulation deleted entries */
|
|
if (deleted)
|
|
VIR_SHRINK_N(list->nets, list->nnets, deleted);
|
|
|
|
success = true;
|
|
|
|
cleanup:
|
|
for (i = 0; i < nAllNets; i++)
|
|
VIR_FREE(names[i]);
|
|
VIR_FREE(names);
|
|
|
|
if (!success) {
|
|
g_clear_pointer(&list, virshNetworkListFree);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/*
|
|
* "net-list" command
|
|
*/
|
|
static const vshCmdInfo info_network_list[] = {
|
|
{.name = "help",
|
|
.data = N_("list networks")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Returns list of networks.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_list[] = {
|
|
{.name = "inactive",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list inactive networks")
|
|
},
|
|
{.name = "all",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list inactive & active networks")
|
|
},
|
|
{.name = "persistent",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list persistent networks")
|
|
},
|
|
{.name = "transient",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list transient networks")
|
|
},
|
|
{.name = "autostart",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list networks with autostart enabled")
|
|
},
|
|
{.name = "no-autostart",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list networks with autostart disabled")
|
|
},
|
|
{.name = "uuid",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list uuid's only")
|
|
},
|
|
{.name = "name",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list network names only")
|
|
},
|
|
{.name = "table",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list table (default)")
|
|
},
|
|
{.name = "title",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("show network title")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
#define FILTER(NAME, FLAG) \
|
|
if (vshCommandOptBool(cmd, NAME)) \
|
|
flags |= (FLAG)
|
|
static bool
|
|
cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
|
|
{
|
|
struct virshNetworkList *list = NULL;
|
|
size_t i;
|
|
bool ret = false;
|
|
bool optName = vshCommandOptBool(cmd, "name");
|
|
bool optTitle = vshCommandOptBool(cmd, "title");
|
|
bool optTable = vshCommandOptBool(cmd, "table");
|
|
bool optUUID = vshCommandOptBool(cmd, "uuid");
|
|
char uuid[VIR_UUID_STRING_BUFLEN];
|
|
unsigned int flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE;
|
|
g_autoptr(vshTable) table = NULL;
|
|
|
|
if (vshCommandOptBool(cmd, "inactive"))
|
|
flags = VIR_CONNECT_LIST_NETWORKS_INACTIVE;
|
|
|
|
if (vshCommandOptBool(cmd, "all"))
|
|
flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE |
|
|
VIR_CONNECT_LIST_NETWORKS_INACTIVE;
|
|
|
|
FILTER("persistent", VIR_CONNECT_LIST_NETWORKS_PERSISTENT);
|
|
FILTER("transient", VIR_CONNECT_LIST_NETWORKS_TRANSIENT);
|
|
|
|
FILTER("autostart", VIR_CONNECT_LIST_NETWORKS_AUTOSTART);
|
|
FILTER("no-autostart", VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART);
|
|
|
|
if (optTable + optName + optUUID > 1) {
|
|
vshError(ctl, "%s",
|
|
_("Only one argument from --table, --name and --uuid may be specified."));
|
|
return false;
|
|
}
|
|
|
|
if (!optUUID && !optName)
|
|
optTable = true;
|
|
|
|
if (!(list = virshNetworkListCollect(ctl, flags)))
|
|
return false;
|
|
|
|
if (optTable) {
|
|
if (optTitle)
|
|
table = vshTableNew(_("Name"), _("State"), _("Autostart"),
|
|
_("Persistent"), _("Title"), NULL);
|
|
else
|
|
table = vshTableNew(_("Name"), _("State"), _("Autostart"),
|
|
_("Persistent"), NULL);
|
|
if (!table)
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < list->nnets; i++) {
|
|
virNetworkPtr network = list->nets[i];
|
|
const char *autostartStr;
|
|
int is_autostart = 0;
|
|
|
|
if (optTable) {
|
|
if (virNetworkGetAutostart(network, &is_autostart) < 0)
|
|
autostartStr = _("no autostart");
|
|
else
|
|
autostartStr = is_autostart ? _("yes") : _("no");
|
|
|
|
if (optTitle) {
|
|
g_autofree char *title = NULL;
|
|
|
|
if (!(title = virshGetNetworkDescription(ctl, network, true, 0, 0)))
|
|
goto cleanup;
|
|
if (vshTableRowAppend(table,
|
|
virNetworkGetName(network),
|
|
virNetworkIsActive(network) ?
|
|
_("active") : _("inactive"),
|
|
autostartStr,
|
|
virNetworkIsPersistent(network) ?
|
|
_("yes") : _("no"),
|
|
title,
|
|
NULL) < 0)
|
|
goto cleanup;
|
|
|
|
} else {
|
|
if (vshTableRowAppend(table,
|
|
virNetworkGetName(network),
|
|
virNetworkIsActive(network) ?
|
|
_("active") : _("inactive"),
|
|
autostartStr,
|
|
virNetworkIsPersistent(network) ?
|
|
_("yes") : _("no"),
|
|
NULL) < 0)
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else if (optUUID) {
|
|
|
|
if (virNetworkGetUUIDString(network, uuid) < 0) {
|
|
vshError(ctl, "%s", _("Failed to get network's UUID"));
|
|
goto cleanup;
|
|
}
|
|
vshPrint(ctl, "%s\n", uuid);
|
|
} else if (optName) {
|
|
vshPrint(ctl, "%s\n", virNetworkGetName(network));
|
|
}
|
|
}
|
|
|
|
if (optTable)
|
|
vshTablePrintToStdout(table, ctl);
|
|
|
|
ret = true;
|
|
cleanup:
|
|
virshNetworkListFree(list);
|
|
return ret;
|
|
}
|
|
#undef FILTER
|
|
|
|
/*
|
|
* "net-name" command
|
|
*/
|
|
static const vshCmdInfo info_network_name[] = {
|
|
{.name = "help",
|
|
.data = N_("convert a network UUID to network name")
|
|
},
|
|
{.name = "desc",
|
|
.data = ""
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_name[] = {
|
|
{.name = "network",
|
|
.type = VSH_OT_DATA,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.completer = virshNetworkUUIDCompleter,
|
|
.help = N_("network uuid")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
|
|
if (!(network = virshCommandOptNetworkBy(ctl, cmd, NULL,
|
|
VIRSH_BYUUID)))
|
|
return false;
|
|
|
|
vshPrint(ctl, "%s\n", virNetworkGetName(network));
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "net-start" command
|
|
*/
|
|
static const vshCmdInfo info_network_start[] = {
|
|
{.name = "help",
|
|
.data = N_("start a (previously defined) inactive network")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Start a network.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_start[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_INACTIVE),
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
bool ret = true;
|
|
const char *name = NULL;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
|
|
return false;
|
|
|
|
if (virNetworkCreate(network) == 0) {
|
|
vshPrintExtra(ctl, _("Network %1$s started\n"), name);
|
|
} else {
|
|
vshError(ctl, _("Failed to start network %1$s"), name);
|
|
ret = false;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "net-undefine" command
|
|
*/
|
|
static const vshCmdInfo info_network_undefine[] = {
|
|
{.name = "help",
|
|
.data = N_("undefine a persistent network")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Undefine the configuration for a persistent network.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_undefine[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_PERSISTENT),
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
bool ret = true;
|
|
const char *name;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
|
|
return false;
|
|
|
|
if (virNetworkUndefine(network) == 0) {
|
|
vshPrintExtra(ctl, _("Network %1$s has been undefined\n"), name);
|
|
} else {
|
|
vshError(ctl, _("Failed to undefine network %1$s"), name);
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "net-update" command
|
|
*/
|
|
static const vshCmdInfo info_network_update[] = {
|
|
{.name = "help",
|
|
.data = N_("update parts of an existing network's configuration")
|
|
},
|
|
{.name = "desc",
|
|
.data = ""
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_update[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(0),
|
|
{.name = "command",
|
|
.type = VSH_OT_DATA,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.completer = virshNetworkUpdateCommandCompleter,
|
|
.help = N_("type of update (add-first, add-last (add), delete, or modify)")
|
|
},
|
|
{.name = "section",
|
|
.type = VSH_OT_DATA,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.completer = virshNetworkUpdateSectionCompleter,
|
|
.help = N_("which section of network configuration to update")
|
|
},
|
|
{.name = "xml",
|
|
.type = VSH_OT_DATA,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.completer = virshCompletePathLocalExisting,
|
|
.help = N_("name of file containing xml (or, if it starts with '<', the complete "
|
|
"xml element itself) to add/modify, or to be matched for search")
|
|
},
|
|
{.name = "parent-index",
|
|
.type = VSH_OT_INT,
|
|
.help = N_("which parent object to search through")
|
|
},
|
|
VIRSH_COMMON_OPT_CONFIG(N_("affect next network startup")),
|
|
VIRSH_COMMON_OPT_LIVE(N_("affect running network")),
|
|
VIRSH_COMMON_OPT_CURRENT(N_("affect current state of network")),
|
|
{.name = NULL}
|
|
};
|
|
|
|
VIR_ENUM_IMPL(virshNetworkUpdateCommand,
|
|
VIR_NETWORK_UPDATE_COMMAND_LAST,
|
|
"none", "modify", "delete", "add-last", "add-first");
|
|
|
|
VIR_ENUM_IMPL(virshNetworkSection,
|
|
VIR_NETWORK_SECTION_LAST,
|
|
"none", "bridge", "domain", "ip", "ip-dhcp-host",
|
|
"ip-dhcp-range", "forward", "forward-interface",
|
|
"forward-pf", "portgroup", "dns-host", "dns-txt",
|
|
"dns-srv");
|
|
|
|
static bool
|
|
cmdNetworkUpdate(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
bool ret = false;
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
const char *commandStr = NULL;
|
|
const char *sectionStr = NULL;
|
|
int command, section, parentIndex = -1;
|
|
const char *xml = NULL;
|
|
g_autofree char *xmlFromFile = NULL;
|
|
bool config = vshCommandOptBool(cmd, "config");
|
|
bool live = vshCommandOptBool(cmd, "live");
|
|
unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT;
|
|
|
|
VSH_EXCLUSIVE_OPTIONS("current", "live");
|
|
VSH_EXCLUSIVE_OPTIONS("current", "config");
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
|
|
return false;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "command", &commandStr) < 0)
|
|
goto cleanup;
|
|
|
|
if (STREQ(commandStr, "add")) {
|
|
/* "add" is a synonym for "add-last" */
|
|
command = VIR_NETWORK_UPDATE_COMMAND_ADD_LAST;
|
|
} else {
|
|
command = virshNetworkUpdateCommandTypeFromString(commandStr);
|
|
if (command <= 0) {
|
|
vshError(ctl, _("unrecognized command name '%1$s'"), commandStr);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "section", §ionStr) < 0)
|
|
goto cleanup;
|
|
|
|
section = virshNetworkSectionTypeFromString(sectionStr);
|
|
if (section <= 0) {
|
|
vshError(ctl, _("unrecognized section name '%1$s'"), sectionStr);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (vshCommandOptInt(ctl, cmd, "parent-index", &parentIndex) < 0)
|
|
goto cleanup;
|
|
|
|
/* The goal is to have a full xml element in the "xml"
|
|
* string. This is provided in the --xml option, either directly
|
|
* (detected by the first character being "<"), or indirectly by
|
|
* supplying a filename (first character isn't "<") that contains
|
|
* the desired xml.
|
|
*/
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "xml", &xml) < 0)
|
|
goto cleanup;
|
|
|
|
if (*xml != '<') {
|
|
/* contents of xmldata is actually the name of a file that
|
|
* contains the xml.
|
|
*/
|
|
if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlFromFile) < 0)
|
|
goto cleanup;
|
|
/* NB: the original xml is just a const char * that points
|
|
* to a string owned by the vshCmd object, and will be freed
|
|
* by vshCommandFree, so it's safe to lose its pointer here.
|
|
*/
|
|
xml = xmlFromFile;
|
|
}
|
|
|
|
if (config)
|
|
flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG;
|
|
if (live)
|
|
flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE;
|
|
|
|
if (virNetworkUpdate(network, command,
|
|
section, parentIndex, xml, flags) < 0) {
|
|
vshError(ctl, _("Failed to update network %1$s"),
|
|
virNetworkGetName(network));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (config) {
|
|
if (live)
|
|
vshPrintExtra(ctl, _("Updated network %1$s persistent config and live state"),
|
|
virNetworkGetName(network));
|
|
else
|
|
vshPrintExtra(ctl, _("Updated network %1$s persistent config"),
|
|
virNetworkGetName(network));
|
|
} else if (live) {
|
|
vshPrintExtra(ctl, _("Updated network %1$s live state"),
|
|
virNetworkGetName(network));
|
|
} else if (virNetworkIsActive(network)) {
|
|
vshPrintExtra(ctl, _("Updated network %1$s live state"),
|
|
virNetworkGetName(network));
|
|
} else {
|
|
vshPrintExtra(ctl, _("Updated network %1$s persistent config"),
|
|
virNetworkGetName(network));
|
|
}
|
|
|
|
ret = true;
|
|
cleanup:
|
|
vshReportError(ctl);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "net-uuid" command
|
|
*/
|
|
static const vshCmdInfo info_network_uuid[] = {
|
|
{.name = "help",
|
|
.data = N_("convert a network name to network UUID")
|
|
},
|
|
{.name = "desc",
|
|
.data = ""
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_uuid[] = {
|
|
VIRSH_COMMON_OPT_NETWORK(N_("network name"), 0),
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
char uuid[VIR_UUID_STRING_BUFLEN];
|
|
|
|
if (!(network = virshCommandOptNetworkBy(ctl, cmd, NULL,
|
|
VIRSH_BYNAME)))
|
|
return false;
|
|
|
|
if (virNetworkGetUUIDString(network, uuid) != -1)
|
|
vshPrint(ctl, "%s\n", uuid);
|
|
else
|
|
vshError(ctl, "%s", _("failed to get network UUID"));
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "net-edit" command
|
|
*/
|
|
static const vshCmdInfo info_network_edit[] = {
|
|
{.name = "help",
|
|
.data = N_("edit XML configuration for a network")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Edit the XML configuration for a network.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_edit[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(0),
|
|
{.name = NULL}
|
|
};
|
|
|
|
static char *virshNetworkGetXMLDesc(virNetworkPtr network)
|
|
{
|
|
unsigned int flags = VIR_NETWORK_XML_INACTIVE;
|
|
char *doc = virNetworkGetXMLDesc(network, flags);
|
|
|
|
if (!doc && last_error->code == VIR_ERR_INVALID_ARG) {
|
|
/* The server side libvirt doesn't support
|
|
* VIR_NETWORK_XML_INACTIVE, so retry without it.
|
|
*/
|
|
vshResetLibvirtError();
|
|
flags &= ~VIR_NETWORK_XML_INACTIVE;
|
|
doc = virNetworkGetXMLDesc(network, flags);
|
|
}
|
|
return doc;
|
|
}
|
|
|
|
static bool
|
|
cmdNetworkEdit(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
bool ret = false;
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
g_autoptr(virshNetwork) network_edited = NULL;
|
|
virshControl *priv = ctl->privData;
|
|
|
|
network = virshCommandOptNetwork(ctl, cmd, NULL);
|
|
if (network == NULL)
|
|
goto cleanup;
|
|
|
|
#define EDIT_GET_XML virshNetworkGetXMLDesc(network)
|
|
#define EDIT_NOT_CHANGED \
|
|
do { \
|
|
vshPrintExtra(ctl, _("Network %1$s XML configuration not changed.\n"), \
|
|
virNetworkGetName(network)); \
|
|
ret = true; \
|
|
goto edit_cleanup; \
|
|
} while (0)
|
|
#define EDIT_DEFINE \
|
|
(network_edited = virNetworkDefineXML(priv->conn, doc_edited))
|
|
#include "virsh-edit.c"
|
|
|
|
vshPrintExtra(ctl, _("Network %1$s XML configuration edited.\n"),
|
|
virNetworkGetName(network_edited));
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* "net-event" command
|
|
*/
|
|
VIR_ENUM_DECL(virshNetworkEvent);
|
|
VIR_ENUM_IMPL(virshNetworkEvent,
|
|
VIR_NETWORK_EVENT_LAST,
|
|
N_("Defined"),
|
|
N_("Undefined"),
|
|
N_("Started"),
|
|
N_("Stopped"));
|
|
|
|
static const char *
|
|
virshNetworkEventToString(int event)
|
|
{
|
|
const char *str = virshNetworkEventTypeToString(event);
|
|
return str ? _(str) : _("unknown");
|
|
}
|
|
|
|
struct virshNetEventData {
|
|
vshControl *ctl;
|
|
bool loop;
|
|
bool timestamp;
|
|
int count;
|
|
virshNetworkEventCallback *cb;
|
|
};
|
|
typedef struct virshNetEventData virshNetEventData;
|
|
|
|
VIR_ENUM_DECL(virshNetworkEventId);
|
|
VIR_ENUM_IMPL(virshNetworkEventId,
|
|
VIR_NETWORK_EVENT_ID_LAST,
|
|
"lifecycle",
|
|
"metadata-change");
|
|
|
|
static void
|
|
vshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
|
|
virNetworkPtr net,
|
|
int event,
|
|
int detail G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virshNetEventData *data = opaque;
|
|
|
|
if (!data->loop && data->count)
|
|
return;
|
|
|
|
if (data->timestamp) {
|
|
char timestamp[VIR_TIME_STRING_BUFLEN];
|
|
|
|
if (virTimeStringNowRaw(timestamp) < 0)
|
|
timestamp[0] = '\0';
|
|
|
|
vshPrint(data->ctl, _("%1$s: event 'lifecycle' for network %2$s: %3$s\n"),
|
|
timestamp,
|
|
virNetworkGetName(net), virshNetworkEventToString(event));
|
|
} else {
|
|
vshPrint(data->ctl, _("event 'lifecycle' for network %1$s: %2$s\n"),
|
|
virNetworkGetName(net), virshNetworkEventToString(event));
|
|
}
|
|
|
|
data->count++;
|
|
if (!data->loop)
|
|
vshEventDone(data->ctl);
|
|
}
|
|
|
|
VIR_ENUM_DECL(virshNetworkEventMetadataChangeType);
|
|
VIR_ENUM_IMPL(virshNetworkEventMetadataChangeType,
|
|
VIR_NETWORK_METADATA_LAST,
|
|
N_("description"),
|
|
N_("title"),
|
|
N_("element"));
|
|
|
|
#define UNKNOWNSTR(str) (str ? str : N_("unsupported value"))
|
|
|
|
static void
|
|
vshEventMetadataChangePrint(virConnectPtr conn G_GNUC_UNUSED,
|
|
virNetworkPtr net,
|
|
int type,
|
|
const char *nsuri,
|
|
void *opaque)
|
|
{
|
|
virshNetEventData *data = opaque;
|
|
|
|
if (!data->loop && data->count)
|
|
return;
|
|
|
|
if (data->timestamp) {
|
|
char timestamp[VIR_TIME_STRING_BUFLEN];
|
|
|
|
if (virTimeStringNowRaw(timestamp) < 0)
|
|
timestamp[0] = '\0';
|
|
|
|
vshPrint(data->ctl, _("%1$s: event 'metadata-change' for network %2$s: type %3$s, uri %4$s\n"),
|
|
timestamp, virNetworkGetName(net),
|
|
UNKNOWNSTR(virshNetworkEventMetadataChangeTypeTypeToString(type)), NULLSTR(nsuri));
|
|
} else {
|
|
vshPrint(data->ctl, _("event 'metadata-change' for network %1$s: type %2$s, uri %3$s\n"),
|
|
virNetworkGetName(net),
|
|
UNKNOWNSTR(virshNetworkEventMetadataChangeTypeTypeToString(type)), NULLSTR(nsuri));
|
|
}
|
|
|
|
data->count++;
|
|
if (!data->loop)
|
|
vshEventDone(data->ctl);
|
|
}
|
|
|
|
virshNetworkEventCallback virshNetworkEventCallbacks[] = {
|
|
{ "lifecycle",
|
|
VIR_NETWORK_EVENT_CALLBACK(vshEventLifecyclePrint), },
|
|
{ "metadata-change",
|
|
VIR_NETWORK_EVENT_CALLBACK(vshEventMetadataChangePrint), },
|
|
};
|
|
G_STATIC_ASSERT(VIR_NETWORK_EVENT_ID_LAST == G_N_ELEMENTS(virshNetworkEventCallbacks));
|
|
|
|
static const vshCmdInfo info_network_event[] = {
|
|
{.name = "help",
|
|
.data = N_("Network Events")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("List event types, or wait for network events to occur")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_event[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_OT_STRING(N_("filter by network name or uuid"), 0),
|
|
{.name = "event",
|
|
.type = VSH_OT_STRING,
|
|
.completer = virshNetworkEventNameCompleter,
|
|
.help = N_("which event type to wait for")
|
|
},
|
|
{.name = "loop",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("loop until timeout or interrupt, rather than one-shot")
|
|
},
|
|
{.name = "timeout",
|
|
.type = VSH_OT_INT,
|
|
.help = N_("timeout seconds")
|
|
},
|
|
{.name = "list",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list valid event types")
|
|
},
|
|
{.name = "timestamp",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("show timestamp for each printed event")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkEvent(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) net = NULL;
|
|
bool ret = false;
|
|
int eventId = -1;
|
|
int timeout = 0;
|
|
virshNetEventData data;
|
|
const char *eventName = NULL;
|
|
int event;
|
|
virshControl *priv = ctl->privData;
|
|
|
|
if (vshCommandOptBool(cmd, "list")) {
|
|
size_t i;
|
|
|
|
for (i = 0; i < VIR_NETWORK_EVENT_ID_LAST; i++)
|
|
vshPrint(ctl, "%s\n", virshNetworkEventCallbacks[i].name);
|
|
return true;
|
|
}
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
|
|
return false;
|
|
if (!eventName) {
|
|
vshError(ctl, "%s", _("either --list or --event <type> is required"));
|
|
return false;
|
|
}
|
|
for (event = 0; event < VIR_NETWORK_EVENT_ID_LAST; event++)
|
|
if (STREQ(eventName, virshNetworkEventCallbacks[event].name))
|
|
break;
|
|
if (event == VIR_NETWORK_EVENT_ID_LAST) {
|
|
vshError(ctl, _("unknown event type %1$s"), eventName);
|
|
return false;
|
|
}
|
|
|
|
data.ctl = ctl;
|
|
data.loop = vshCommandOptBool(cmd, "loop");
|
|
data.timestamp = vshCommandOptBool(cmd, "timestamp");
|
|
data.count = 0;
|
|
data.cb = &virshNetworkEventCallbacks[event];
|
|
if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
|
|
return false;
|
|
|
|
if (vshCommandOptBool(cmd, "network"))
|
|
net = virshCommandOptNetwork(ctl, cmd, NULL);
|
|
if (vshEventStart(ctl, timeout) < 0)
|
|
goto cleanup;
|
|
|
|
if ((eventId = virConnectNetworkEventRegisterAny(priv->conn, net, event,
|
|
data.cb->cb,
|
|
&data, NULL)) < 0)
|
|
goto cleanup;
|
|
switch (vshEventWait(ctl)) {
|
|
case VSH_EVENT_INTERRUPT:
|
|
vshPrint(ctl, "%s", _("event loop interrupted\n"));
|
|
break;
|
|
case VSH_EVENT_TIMEOUT:
|
|
vshPrint(ctl, "%s", _("event loop timed out\n"));
|
|
break;
|
|
case VSH_EVENT_DONE:
|
|
break;
|
|
default:
|
|
goto cleanup;
|
|
}
|
|
vshPrint(ctl, _("events received: %1$d\n"), data.count);
|
|
if (data.count)
|
|
ret = true;
|
|
|
|
cleanup:
|
|
vshEventCleanup(ctl);
|
|
if (eventId >= 0 &&
|
|
virConnectNetworkEventDeregisterAny(priv->conn, eventId) < 0)
|
|
ret = false;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* "net-dhcp-leases" command
|
|
*/
|
|
static const vshCmdInfo info_network_dhcp_leases[] = {
|
|
{.name = "help",
|
|
.data = N_("print lease info for a given network")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Print lease info for a given network")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_dhcp_leases[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
|
|
{.name = "mac",
|
|
.type = VSH_OT_STRING,
|
|
.flags = VSH_OFLAG_NONE,
|
|
.help = N_("MAC address"),
|
|
.completer = virshNetworkDhcpMacCompleter,
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static int
|
|
virshNetworkDHCPLeaseSorter(const void *a,
|
|
const void *b,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
virNetworkDHCPLeasePtr *lease1 = (virNetworkDHCPLeasePtr *) a;
|
|
virNetworkDHCPLeasePtr *lease2 = (virNetworkDHCPLeasePtr *) b;
|
|
|
|
if (*lease1 && !*lease2)
|
|
return -1;
|
|
|
|
if (!*lease1)
|
|
return *lease2 != NULL;
|
|
|
|
return vshStrcasecmp((*lease1)->mac, (*lease2)->mac);
|
|
}
|
|
|
|
static bool
|
|
cmdNetworkDHCPLeases(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
const char *name = NULL;
|
|
const char *mac = NULL;
|
|
virNetworkDHCPLeasePtr *leases = NULL;
|
|
int nleases = 0;
|
|
bool ret = false;
|
|
size_t i;
|
|
unsigned int flags = 0;
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
g_autoptr(vshTable) table = NULL;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "mac", &mac) < 0)
|
|
return false;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
|
|
return false;
|
|
|
|
if ((nleases = virNetworkGetDHCPLeases(network, mac, &leases, flags)) < 0) {
|
|
vshError(ctl, _("Failed to get leases info for %1$s"), name);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Sort the list according to MAC Address/IAID */
|
|
g_qsort_with_data(leases, nleases,
|
|
sizeof(*leases), virshNetworkDHCPLeaseSorter, NULL);
|
|
|
|
table = vshTableNew(_("Expiry Time"), _("MAC address"), _("Protocol"),
|
|
_("IP address"), _("Hostname"), _("Client ID or DUID"),
|
|
NULL);
|
|
if (!table)
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < nleases; i++) {
|
|
const char *typestr = NULL;
|
|
g_autofree char *cidr_format = NULL;
|
|
virNetworkDHCPLeasePtr lease = leases[i];
|
|
g_autoptr(GDateTime) then = g_date_time_new_from_unix_local(lease->expirytime);
|
|
g_autofree char *thenstr = NULL;
|
|
|
|
thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S");
|
|
|
|
if (lease->type == VIR_IP_ADDR_TYPE_IPV4)
|
|
typestr = "ipv4";
|
|
else if (lease->type == VIR_IP_ADDR_TYPE_IPV6)
|
|
typestr = "ipv6";
|
|
|
|
cidr_format = g_strdup_printf("%s/%d", lease->ipaddr, lease->prefix);
|
|
|
|
if (vshTableRowAppend(table,
|
|
thenstr,
|
|
NULLSTR_MINUS(lease->mac),
|
|
NULLSTR_MINUS(typestr),
|
|
NULLSTR_MINUS(cidr_format),
|
|
NULLSTR_MINUS(lease->hostname),
|
|
NULLSTR_MINUS(lease->clientid),
|
|
NULL) < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
vshTablePrintToStdout(table, ctl);
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
if (leases) {
|
|
for (i = 0; i < nleases; i++)
|
|
virNetworkDHCPLeaseFree(leases[i]);
|
|
VIR_FREE(leases);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "net-port-create" command
|
|
*/
|
|
static const vshCmdInfo info_network_port_create[] = {
|
|
{.name = "help",
|
|
.data = N_("create a network port from an XML file")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Create a network port.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_port_create[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
|
|
VIRSH_COMMON_OPT_FILE(N_("file containing an XML network port description")),
|
|
{.name = "validate",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("validate the XML against the schema")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkPortCreate(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
virNetworkPortPtr port = NULL;
|
|
const char *from = NULL;
|
|
bool ret = false;
|
|
g_autofree char *buffer = NULL;
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
unsigned int flags = 0;
|
|
|
|
network = virshCommandOptNetwork(ctl, cmd, NULL);
|
|
if (network == NULL)
|
|
goto cleanup;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
|
|
goto cleanup;
|
|
|
|
if (vshCommandOptBool(cmd, "validate"))
|
|
flags |= VIR_NETWORK_PORT_CREATE_VALIDATE;
|
|
|
|
if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
|
|
vshSaveLibvirtError();
|
|
goto cleanup;
|
|
}
|
|
|
|
port = virNetworkPortCreateXML(network, buffer, flags);
|
|
|
|
if (port != NULL) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
virNetworkPortGetUUIDString(port, uuidstr);
|
|
vshPrintExtra(ctl, _("Network port %1$s created from %2$s\n"),
|
|
uuidstr, from);
|
|
} else {
|
|
vshError(ctl, _("Failed to create network from %1$s"), from);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = true;
|
|
cleanup:
|
|
if (port)
|
|
virNetworkPortFree(port);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "net-port-dumpxml" command
|
|
*/
|
|
static const vshCmdInfo info_network_port_dumpxml[] = {
|
|
{.name = "help",
|
|
.data = N_("network port information in XML")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Output the network port information as an XML dump to stdout.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_port_dumpxml[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
|
|
VIRSH_COMMON_OPT_NETWORK_PORT(0),
|
|
{.name = "xpath",
|
|
.type = VSH_OT_STRING,
|
|
.flags = VSH_OFLAG_REQ_OPT,
|
|
.completer = virshCompleteEmpty,
|
|
.help = N_("xpath expression to filter the XML document")
|
|
},
|
|
{.name = "wrap",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("wrap xpath results in an common root element"),
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkPortDumpXML(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
virNetworkPortPtr port = NULL;
|
|
bool ret = true;
|
|
g_autofree char *xml = NULL;
|
|
unsigned int flags = 0;
|
|
bool wrap = vshCommandOptBool(cmd, "wrap");
|
|
const char *xpath = NULL;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
|
|
goto cleanup;
|
|
|
|
if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL)))
|
|
goto cleanup;
|
|
|
|
if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
|
|
return false;
|
|
|
|
if (!(xml = virNetworkPortGetXMLDesc(port, flags)))
|
|
goto cleanup;
|
|
|
|
ret = virshDumpXML(ctl, xml, "network-port", xpath, wrap);
|
|
|
|
cleanup:
|
|
if (port)
|
|
virNetworkPortFree(port);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* "net-port-delete" command
|
|
*/
|
|
static const vshCmdInfo info_network_port_delete[] = {
|
|
{.name = "help",
|
|
.data = N_("delete the specified network port")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Delete the specified network port.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_port_delete[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
|
|
VIRSH_COMMON_OPT_NETWORK_PORT(0),
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkPortDelete(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
virNetworkPortPtr port = NULL;
|
|
bool ret = true;
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
|
|
goto cleanup;
|
|
|
|
if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL)))
|
|
goto cleanup;
|
|
|
|
if (virNetworkPortGetUUIDString(port, uuidstr) < 0)
|
|
goto cleanup;
|
|
|
|
if (virNetworkPortDelete(port, 0) < 0) {
|
|
vshError(ctl, _("Failed to delete network port %1$s"), uuidstr);
|
|
goto cleanup;
|
|
} else {
|
|
vshPrintExtra(ctl, _("Network port %1$s deleted\n"), uuidstr);
|
|
}
|
|
|
|
ret = true;
|
|
cleanup:
|
|
if (port)
|
|
virNetworkPortFree(port);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virshNetworkPortSorter(const void *a,
|
|
const void *b,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
virNetworkPortPtr *na = (virNetworkPortPtr *) a;
|
|
virNetworkPortPtr *nb = (virNetworkPortPtr *) b;
|
|
unsigned char uuida[VIR_UUID_BUFLEN];
|
|
unsigned char uuidb[VIR_UUID_BUFLEN];
|
|
|
|
if (*na && !*nb)
|
|
return -1;
|
|
|
|
if (!*na)
|
|
return *nb != NULL;
|
|
|
|
if (virNetworkPortGetUUID(*na, uuida) < 0 ||
|
|
virNetworkPortGetUUID(*nb, uuidb) < 0)
|
|
return -1;
|
|
|
|
return memcmp(uuida, uuidb, VIR_UUID_BUFLEN);
|
|
}
|
|
|
|
struct virshNetworkPortList {
|
|
virNetworkPortPtr *ports;
|
|
size_t nports;
|
|
};
|
|
|
|
static void
|
|
virshNetworkPortListFree(struct virshNetworkPortList *list)
|
|
{
|
|
size_t i;
|
|
|
|
if (list && list->ports) {
|
|
for (i = 0; i < list->nports; i++) {
|
|
if (list->ports[i])
|
|
virNetworkPortFree(list->ports[i]);
|
|
}
|
|
g_free(list->ports);
|
|
}
|
|
g_free(list);
|
|
}
|
|
|
|
static struct virshNetworkPortList *
|
|
virshNetworkPortListCollect(vshControl *ctl,
|
|
const vshCmd *cmd,
|
|
unsigned int flags)
|
|
{
|
|
struct virshNetworkPortList *list = g_new0(struct virshNetworkPortList, 1);
|
|
int ret;
|
|
g_autoptr(virshNetwork) network = NULL;
|
|
bool success = false;
|
|
|
|
if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
|
|
goto cleanup;
|
|
|
|
if ((ret = virNetworkListAllPorts(network,
|
|
&list->ports,
|
|
flags)) < 0)
|
|
goto cleanup;
|
|
|
|
list->nports = ret;
|
|
|
|
/* sort the list */
|
|
if (list->ports && list->nports) {
|
|
g_qsort_with_data(list->ports, list->nports,
|
|
sizeof(*list->ports), virshNetworkPortSorter, NULL);
|
|
}
|
|
|
|
success = true;
|
|
|
|
cleanup:
|
|
if (!success) {
|
|
g_clear_pointer(&list, virshNetworkPortListFree);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/*
|
|
* "net-list" command
|
|
*/
|
|
static const vshCmdInfo info_network_port_list[] = {
|
|
{.name = "help",
|
|
.data = N_("list network ports")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Returns list of network ports.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_network_port_list[] = {
|
|
VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
|
|
{.name = "uuid",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list uuid's only")
|
|
},
|
|
{.name = "table",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("list table (default)")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNetworkPortList(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
struct virshNetworkPortList *list = NULL;
|
|
size_t i;
|
|
bool ret = false;
|
|
bool optTable = vshCommandOptBool(cmd, "table");
|
|
bool optUUID = vshCommandOptBool(cmd, "uuid");
|
|
char uuid[VIR_UUID_STRING_BUFLEN];
|
|
unsigned int flags = 0;
|
|
g_autoptr(vshTable) table = NULL;
|
|
|
|
if (optTable + optUUID > 1) {
|
|
vshError(ctl, "%s",
|
|
_("Only one argument from --table and --uuid may be specified."));
|
|
return false;
|
|
}
|
|
|
|
if (!optUUID)
|
|
optTable = true;
|
|
|
|
if (!(list = virshNetworkPortListCollect(ctl, cmd, flags)))
|
|
return false;
|
|
|
|
if (optTable) {
|
|
table = vshTableNew(_("UUID"), NULL);
|
|
if (!table)
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < list->nports; i++) {
|
|
virNetworkPortPtr port = list->ports[i];
|
|
|
|
if (virNetworkPortGetUUIDString(port, uuid) < 0) {
|
|
vshError(ctl, "%s", _("Failed to get network's UUID"));
|
|
goto cleanup;
|
|
}
|
|
if (optTable) {
|
|
if (vshTableRowAppend(table, uuid, NULL) < 0)
|
|
goto cleanup;
|
|
} else if (optUUID) {
|
|
vshPrint(ctl, "%s\n", uuid);
|
|
}
|
|
}
|
|
|
|
if (optTable)
|
|
vshTablePrintToStdout(table, ctl);
|
|
|
|
ret = true;
|
|
cleanup:
|
|
virshNetworkPortListFree(list);
|
|
return ret;
|
|
}
|
|
|
|
|
|
const vshCmdDef networkCmds[] = {
|
|
{.name = "net-autostart",
|
|
.handler = cmdNetworkAutostart,
|
|
.opts = opts_network_autostart,
|
|
.info = info_network_autostart,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-create",
|
|
.handler = cmdNetworkCreate,
|
|
.opts = opts_network_create,
|
|
.info = info_network_create,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-define",
|
|
.handler = cmdNetworkDefine,
|
|
.opts = opts_network_define,
|
|
.info = info_network_define,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-desc",
|
|
.handler = cmdNetworkDesc,
|
|
.opts = opts_network_desc,
|
|
.info = info_network_desc,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-destroy",
|
|
.handler = cmdNetworkDestroy,
|
|
.opts = opts_network_destroy,
|
|
.info = info_network_destroy,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-dhcp-leases",
|
|
.handler = cmdNetworkDHCPLeases,
|
|
.opts = opts_network_dhcp_leases,
|
|
.info = info_network_dhcp_leases,
|
|
.flags = 0,
|
|
},
|
|
{.name = "net-dumpxml",
|
|
.handler = cmdNetworkDumpXML,
|
|
.opts = opts_network_dumpxml,
|
|
.info = info_network_dumpxml,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-edit",
|
|
.handler = cmdNetworkEdit,
|
|
.opts = opts_network_edit,
|
|
.info = info_network_edit,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-event",
|
|
.handler = cmdNetworkEvent,
|
|
.opts = opts_network_event,
|
|
.info = info_network_event,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-info",
|
|
.handler = cmdNetworkInfo,
|
|
.opts = opts_network_info,
|
|
.info = info_network_info,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-list",
|
|
.handler = cmdNetworkList,
|
|
.opts = opts_network_list,
|
|
.info = info_network_list,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-metadata",
|
|
.handler = cmdNetworkMetadata,
|
|
.opts = opts_network_metadata,
|
|
.info = info_network_metadata,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-name",
|
|
.handler = cmdNetworkName,
|
|
.opts = opts_network_name,
|
|
.info = info_network_name,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-start",
|
|
.handler = cmdNetworkStart,
|
|
.opts = opts_network_start,
|
|
.info = info_network_start,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-undefine",
|
|
.handler = cmdNetworkUndefine,
|
|
.opts = opts_network_undefine,
|
|
.info = info_network_undefine,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-update",
|
|
.handler = cmdNetworkUpdate,
|
|
.opts = opts_network_update,
|
|
.info = info_network_update,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-uuid",
|
|
.handler = cmdNetworkUuid,
|
|
.opts = opts_network_uuid,
|
|
.info = info_network_uuid,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-port-list",
|
|
.handler = cmdNetworkPortList,
|
|
.opts = opts_network_port_list,
|
|
.info = info_network_port_list,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-port-create",
|
|
.handler = cmdNetworkPortCreate,
|
|
.opts = opts_network_port_create,
|
|
.info = info_network_port_create,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-port-dumpxml",
|
|
.handler = cmdNetworkPortDumpXML,
|
|
.opts = opts_network_port_dumpxml,
|
|
.info = info_network_port_dumpxml,
|
|
.flags = 0
|
|
},
|
|
{.name = "net-port-delete",
|
|
.handler = cmdNetworkPortDelete,
|
|
.opts = opts_network_port_delete,
|
|
.info = info_network_port_delete,
|
|
.flags = 0
|
|
},
|
|
{.name = NULL}
|
|
};
|