1 /*
2 * virsh-network.c: Commands to manage network
3 *
4 * Copyright (C) 2005, 2007-2019 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22 #include "virsh-network.h"
23 #include "virsh-util.h"
24
25 #include "internal.h"
26 #include "viralloc.h"
27 #include "virfile.h"
28 #include "virtime.h"
29 #include "conf/network_conf.h"
30 #include "vsh-table.h"
31 #include "virenum.h"
32
33 #define VIRSH_COMMON_OPT_NETWORK(_helpstr, cflags) \
34 {.name = "network", \
35 .type = VSH_OT_DATA, \
36 .flags = VSH_OFLAG_REQ, \
37 .help = _helpstr, \
38 .completer = virshNetworkNameCompleter, \
39 .completer_flags = cflags, \
40 }
41
42 #define VIRSH_COMMON_OPT_NETWORK_FULL(cflags) \
43 VIRSH_COMMON_OPT_NETWORK(N_("network name or uuid"), cflags)
44
45 #define VIRSH_COMMON_OPT_NETWORK_OT_STRING(_helpstr, cflags) \
46 {.name = "network", \
47 .type = VSH_OT_STRING, \
48 .help = _helpstr, \
49 .completer = virshNetworkNameCompleter, \
50 .completer_flags = cflags, \
51 }
52
53 #define VIRSH_COMMON_OPT_NETWORK_OT_STRING_FULL(cflags) \
54 VIRSH_COMMON_OPT_NETWORK_OT_STRING(N_("network name or uuid"), cflags)
55
56 #define VIRSH_COMMON_OPT_NETWORK_PORT(cflags) \
57 {.name = "port", \
58 .type = VSH_OT_DATA, \
59 .flags = VSH_OFLAG_REQ, \
60 .help = N_("port UUID"), \
61 .completer = virshNetworkPortUUIDCompleter, \
62 .completer_flags = cflags, \
63 }
64
65
66 virNetworkPtr
virshCommandOptNetworkBy(vshControl * ctl,const vshCmd * cmd,const char ** name,unsigned int flags)67 virshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd,
68 const char **name, unsigned int flags)
69 {
70 virNetworkPtr network = NULL;
71 const char *n = NULL;
72 const char *optname = "network";
73 virshControl *priv = ctl->privData;
74
75 virCheckFlags(VIRSH_BYUUID | VIRSH_BYNAME, NULL);
76
77 if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
78 return NULL;
79
80 vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
81 cmd->def->name, optname, n);
82
83 if (name)
84 *name = n;
85
86 /* try it by UUID */
87 if ((flags & VIRSH_BYUUID) && strlen(n) == VIR_UUID_STRING_BUFLEN-1) {
88 vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
89 cmd->def->name, optname);
90 network = virNetworkLookupByUUIDString(priv->conn, n);
91 }
92 /* try it by NAME */
93 if (!network && (flags & VIRSH_BYNAME)) {
94 vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network NAME\n",
95 cmd->def->name, optname);
96 network = virNetworkLookupByName(priv->conn, n);
97 }
98
99 if (!network)
100 vshError(ctl, _("failed to get network '%s'"), n);
101
102 return network;
103 }
104
105
106 virNetworkPortPtr
virshCommandOptNetworkPort(vshControl * ctl,const vshCmd * cmd,virNetworkPtr net,const char ** name)107 virshCommandOptNetworkPort(vshControl *ctl, const vshCmd *cmd,
108 virNetworkPtr net,
109 const char **name)
110 {
111 virNetworkPortPtr port = NULL;
112 const char *n = NULL;
113 const char *optname = "port";
114
115 if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
116 return NULL;
117
118 vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
119 cmd->def->name, optname, n);
120
121 if (name)
122 *name = n;
123
124 vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n",
125 cmd->def->name, optname);
126 port = virNetworkPortLookupByUUIDString(net, n);
127
128 if (!port)
129 vshError(ctl, _("failed to get network port '%s'"), n);
130
131 return port;
132 }
133
134 /*
135 * "net-autostart" command
136 */
137 static const vshCmdInfo info_network_autostart[] = {
138 {.name = "help",
139 .data = N_("autostart a network")
140 },
141 {.name = "desc",
142 .data = N_("Configure a network to be automatically started at boot.")
143 },
144 {.name = NULL}
145 };
146
147 static const vshCmdOptDef opts_network_autostart[] = {
148 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_PERSISTENT),
149 {.name = "disable",
150 .type = VSH_OT_BOOL,
151 .help = N_("disable autostarting")
152 },
153 {.name = NULL}
154 };
155
156 static bool
cmdNetworkAutostart(vshControl * ctl,const vshCmd * cmd)157 cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
158 {
159 g_autoptr(virshNetwork) network = NULL;
160 const char *name;
161 int autostart;
162
163 if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
164 return false;
165
166 autostart = !vshCommandOptBool(cmd, "disable");
167
168 if (virNetworkSetAutostart(network, autostart) < 0) {
169 if (autostart)
170 vshError(ctl, _("failed to mark network %s as autostarted"), name);
171 else
172 vshError(ctl, _("failed to unmark network %s as autostarted"), name);
173 return false;
174 }
175
176 if (autostart)
177 vshPrintExtra(ctl, _("Network %s marked as autostarted\n"), name);
178 else
179 vshPrintExtra(ctl, _("Network %s unmarked as autostarted\n"), name);
180
181 return true;
182 }
183
184 /*
185 * "net-create" command
186 */
187 static const vshCmdInfo info_network_create[] = {
188 {.name = "help",
189 .data = N_("create a network from an XML file")
190 },
191 {.name = "desc",
192 .data = N_("Create a network.")
193 },
194 {.name = NULL}
195 };
196
197 static const vshCmdOptDef opts_network_create[] = {
198 VIRSH_COMMON_OPT_FILE(N_("file containing an XML network description")),
199 {.name = "validate",
200 .type = VSH_OT_BOOL,
201 .help = N_("validate the XML against the schema")
202 },
203 {.name = NULL}
204 };
205
206 static bool
cmdNetworkCreate(vshControl * ctl,const vshCmd * cmd)207 cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
208 {
209 g_autoptr(virshNetwork) network = NULL;
210 const char *from = NULL;
211 g_autofree char *buffer = NULL;
212 unsigned int flags = 0;
213 virshControl *priv = ctl->privData;
214
215 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
216 return false;
217
218 if (vshCommandOptBool(cmd, "validate"))
219 flags |= VIR_NETWORK_CREATE_VALIDATE;
220
221 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
222 return false;
223
224 if (flags)
225 network = virNetworkCreateXMLFlags(priv->conn, buffer, flags);
226 else
227 network = virNetworkCreateXML(priv->conn, buffer);
228
229 if (!network) {
230 vshError(ctl, _("Failed to create network from %s"), from);
231 return false;
232 }
233
234 vshPrintExtra(ctl, _("Network %s created from %s\n"),
235 virNetworkGetName(network), from);
236 return true;
237 }
238
239 /*
240 * "net-define" command
241 */
242 static const vshCmdInfo info_network_define[] = {
243 {.name = "help",
244 .data = N_("define an inactive persistent virtual network or modify "
245 "an existing persistent one from an XML file")
246 },
247 {.name = "desc",
248 .data = N_("Define or modify a persistent virtual network.")
249 },
250 {.name = NULL}
251 };
252
253 static const vshCmdOptDef opts_network_define[] = {
254 VIRSH_COMMON_OPT_FILE(N_("file containing an XML network description")),
255 {.name = "validate",
256 .type = VSH_OT_BOOL,
257 .help = N_("validate the XML against the schema")
258 },
259 {.name = NULL}
260 };
261
262 static bool
cmdNetworkDefine(vshControl * ctl,const vshCmd * cmd)263 cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
264 {
265 g_autoptr(virshNetwork) network = NULL;
266 const char *from = NULL;
267 g_autofree char *buffer = NULL;
268 unsigned int flags = 0;
269 virshControl *priv = ctl->privData;
270
271 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
272 return false;
273
274 if (vshCommandOptBool(cmd, "validate"))
275 flags |= VIR_NETWORK_DEFINE_VALIDATE;
276
277 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
278 return false;
279
280 if (flags)
281 network = virNetworkDefineXMLFlags(priv->conn, buffer, flags);
282 else
283 network = virNetworkDefineXML(priv->conn, buffer);
284
285 if (!network) {
286 vshError(ctl, _("Failed to define network from %s"), from);
287 return false;
288 }
289
290 vshPrintExtra(ctl, _("Network %s defined from %s\n"),
291 virNetworkGetName(network), from);
292 return true;
293 }
294
295 /*
296 * "net-destroy" command
297 */
298 static const vshCmdInfo info_network_destroy[] = {
299 {.name = "help",
300 .data = N_("destroy (stop) a network")
301 },
302 {.name = "desc",
303 .data = N_("Forcefully stop a given network.")
304 },
305 {.name = NULL}
306 };
307
308 static const vshCmdOptDef opts_network_destroy[] = {
309 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
310 {.name = NULL}
311 };
312
313 static bool
cmdNetworkDestroy(vshControl * ctl,const vshCmd * cmd)314 cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
315 {
316 g_autoptr(virshNetwork) network = NULL;
317 bool ret = true;
318 const char *name;
319
320 if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
321 return false;
322
323 if (virNetworkDestroy(network) == 0) {
324 vshPrintExtra(ctl, _("Network %s destroyed\n"), name);
325 } else {
326 vshError(ctl, _("Failed to destroy network %s"), name);
327 ret = false;
328 }
329
330 return ret;
331 }
332
333 /*
334 * "net-dumpxml" command
335 */
336 static const vshCmdInfo info_network_dumpxml[] = {
337 {.name = "help",
338 .data = N_("network information in XML")
339 },
340 {.name = "desc",
341 .data = N_("Output the network information as an XML dump to stdout.")
342 },
343 {.name = NULL}
344 };
345
346 static const vshCmdOptDef opts_network_dumpxml[] = {
347 VIRSH_COMMON_OPT_NETWORK_FULL(0),
348 {.name = "inactive",
349 .type = VSH_OT_BOOL,
350 .help = N_("show inactive defined XML")
351 },
352 {.name = NULL}
353 };
354
355 static bool
cmdNetworkDumpXML(vshControl * ctl,const vshCmd * cmd)356 cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
357 {
358 g_autoptr(virshNetwork) network = NULL;
359 g_autofree char *dump = NULL;
360 unsigned int flags = 0;
361
362 if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
363 return false;
364
365 if (vshCommandOptBool(cmd, "inactive"))
366 flags |= VIR_NETWORK_XML_INACTIVE;
367
368 if (!(dump = virNetworkGetXMLDesc(network, flags))) {
369 return false;
370 }
371
372 vshPrint(ctl, "%s", dump);
373 return true;
374 }
375
376 /*
377 * "net-info" command
378 */
379 static const vshCmdInfo info_network_info[] = {
380 {.name = "help",
381 .data = N_("network information")
382 },
383 {.name = "desc",
384 .data = N_("Returns basic information about the network")
385 },
386 {.name = NULL}
387 };
388
389 static const vshCmdOptDef opts_network_info[] = {
390 VIRSH_COMMON_OPT_NETWORK_FULL(0),
391 {.name = NULL}
392 };
393
394 static bool
cmdNetworkInfo(vshControl * ctl,const vshCmd * cmd)395 cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd)
396 {
397 g_autoptr(virshNetwork) network = NULL;
398 char uuid[VIR_UUID_STRING_BUFLEN];
399 int autostart;
400 int persistent = -1;
401 int active = -1;
402 char *bridge = NULL;
403
404 if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
405 return false;
406
407 vshPrint(ctl, "%-15s %s\n", _("Name:"), virNetworkGetName(network));
408
409 if (virNetworkGetUUIDString(network, uuid) == 0)
410 vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
411
412 active = virNetworkIsActive(network);
413 if (active >= 0)
414 vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no"));
415
416 persistent = virNetworkIsPersistent(network);
417 if (persistent < 0)
418 vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
419 else
420 vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
421
422 if (virNetworkGetAutostart(network, &autostart) < 0)
423 vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
424 else
425 vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
426
427 bridge = virNetworkGetBridgeName(network);
428 if (bridge)
429 vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge);
430
431 VIR_FREE(bridge);
432 return true;
433 }
434
435 static int
virshNetworkSorter(const void * a,const void * b)436 virshNetworkSorter(const void *a, const void *b)
437 {
438 virNetworkPtr *na = (virNetworkPtr *) a;
439 virNetworkPtr *nb = (virNetworkPtr *) b;
440
441 if (*na && !*nb)
442 return -1;
443
444 if (!*na)
445 return *nb != NULL;
446
447 return vshStrcasecmp(virNetworkGetName(*na),
448 virNetworkGetName(*nb));
449 }
450
451 struct virshNetworkList {
452 virNetworkPtr *nets;
453 size_t nnets;
454 };
455
456 static void
virshNetworkListFree(struct virshNetworkList * list)457 virshNetworkListFree(struct virshNetworkList *list)
458 {
459 size_t i;
460
461 if (list && list->nets) {
462 for (i = 0; i < list->nnets; i++) {
463 virshNetworkFree(list->nets[i]);
464 }
465 g_free(list->nets);
466 }
467 g_free(list);
468 }
469
470 static struct virshNetworkList *
virshNetworkListCollect(vshControl * ctl,unsigned int flags)471 virshNetworkListCollect(vshControl *ctl,
472 unsigned int flags)
473 {
474 struct virshNetworkList *list = g_new0(struct virshNetworkList, 1);
475 size_t i;
476 int ret;
477 char **names = NULL;
478 virNetworkPtr net;
479 bool success = false;
480 size_t deleted = 0;
481 int persistent;
482 int autostart;
483 int nActiveNets = 0;
484 int nInactiveNets = 0;
485 int nAllNets = 0;
486 virshControl *priv = ctl->privData;
487
488 /* try the list with flags support (0.10.2 and later) */
489 if ((ret = virConnectListAllNetworks(priv->conn,
490 &list->nets,
491 flags)) >= 0) {
492 list->nnets = ret;
493 goto finished;
494 }
495
496 /* check if the command is actually supported */
497 if (last_error && last_error->code == VIR_ERR_NO_SUPPORT)
498 goto fallback;
499
500 if (last_error && last_error->code == VIR_ERR_INVALID_ARG) {
501 /* try the new API again but mask non-guaranteed flags */
502 unsigned int newflags = flags & (VIR_CONNECT_LIST_NETWORKS_ACTIVE |
503 VIR_CONNECT_LIST_NETWORKS_INACTIVE);
504
505 vshResetLibvirtError();
506 if ((ret = virConnectListAllNetworks(priv->conn, &list->nets,
507 newflags)) >= 0) {
508 list->nnets = ret;
509 goto filter;
510 }
511 }
512
513 /* there was an error during the first or second call */
514 vshError(ctl, "%s", _("Failed to list networks"));
515 goto cleanup;
516
517
518 fallback:
519 /* fall back to old method (0.10.1 and older) */
520 vshResetLibvirtError();
521
522 /* Get the number of active networks */
523 if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
524 VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
525 if ((nActiveNets = virConnectNumOfNetworks(priv->conn)) < 0) {
526 vshError(ctl, "%s", _("Failed to get the number of active networks"));
527 goto cleanup;
528 }
529 }
530
531 /* Get the number of inactive networks */
532 if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
533 VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_INACTIVE)) {
534 if ((nInactiveNets = virConnectNumOfDefinedNetworks(priv->conn)) < 0) {
535 vshError(ctl, "%s", _("Failed to get the number of inactive networks"));
536 goto cleanup;
537 }
538 }
539
540 nAllNets = nActiveNets + nInactiveNets;
541
542 if (nAllNets == 0)
543 return list;
544
545 names = g_new0(char *, nAllNets);
546
547 /* Retrieve a list of active network names */
548 if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
549 VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
550 if (virConnectListNetworks(priv->conn,
551 names, nActiveNets) < 0) {
552 vshError(ctl, "%s", _("Failed to list active networks"));
553 goto cleanup;
554 }
555 }
556
557 /* Add the inactive networks to the end of the name list */
558 if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) ||
559 VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) {
560 if (virConnectListDefinedNetworks(priv->conn,
561 &names[nActiveNets],
562 nInactiveNets) < 0) {
563 vshError(ctl, "%s", _("Failed to list inactive networks"));
564 goto cleanup;
565 }
566 }
567
568 list->nets = g_new0(virNetworkPtr, nAllNets);
569 list->nnets = 0;
570
571 /* get active networks */
572 for (i = 0; i < nActiveNets; i++) {
573 if (!(net = virNetworkLookupByName(priv->conn, names[i])))
574 continue;
575 list->nets[list->nnets++] = net;
576 }
577
578 /* get inactive networks */
579 for (i = 0; i < nInactiveNets; i++) {
580 if (!(net = virNetworkLookupByName(priv->conn, names[i])))
581 continue;
582 list->nets[list->nnets++] = net;
583 }
584
585 /* truncate networks that weren't found */
586 deleted = nAllNets - list->nnets;
587
588 filter:
589 /* filter list the list if the list was acquired by fallback means */
590 for (i = 0; i < list->nnets; i++) {
591 net = list->nets[i];
592
593 /* persistence filter */
594 if (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT)) {
595 if ((persistent = virNetworkIsPersistent(net)) < 0) {
596 vshError(ctl, "%s", _("Failed to get network persistence info"));
597 goto cleanup;
598 }
599
600 if (!((VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) && persistent) ||
601 (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) && !persistent)))
602 goto remove_entry;
603 }
604
605 /* autostart filter */
606 if (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART)) {
607 if (virNetworkGetAutostart(net, &autostart) < 0) {
608 vshError(ctl, "%s", _("Failed to get network autostart state"));
609 goto cleanup;
610 }
611
612 if (!((VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) && autostart) ||
613 (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) && !autostart)))
614 goto remove_entry;
615 }
616 /* the pool matched all filters, it may stay */
617 continue;
618
619 remove_entry:
620 /* the pool has to be removed as it failed one of the filters */
621 g_clear_pointer(&list->nets[i], virshNetworkFree);
622 deleted++;
623 }
624
625 finished:
626 /* sort the list */
627 if (list->nets && list->nnets)
628 qsort(list->nets, list->nnets,
629 sizeof(*list->nets), virshNetworkSorter);
630
631 /* truncate the list if filter simulation deleted entries */
632 if (deleted)
633 VIR_SHRINK_N(list->nets, list->nnets, deleted);
634
635 success = true;
636
637 cleanup:
638 for (i = 0; i < nAllNets; i++)
639 VIR_FREE(names[i]);
640 VIR_FREE(names);
641
642 if (!success) {
643 virshNetworkListFree(list);
644 list = NULL;
645 }
646
647 return list;
648 }
649
650 /*
651 * "net-list" command
652 */
653 static const vshCmdInfo info_network_list[] = {
654 {.name = "help",
655 .data = N_("list networks")
656 },
657 {.name = "desc",
658 .data = N_("Returns list of networks.")
659 },
660 {.name = NULL}
661 };
662
663 static const vshCmdOptDef opts_network_list[] = {
664 {.name = "inactive",
665 .type = VSH_OT_BOOL,
666 .help = N_("list inactive networks")
667 },
668 {.name = "all",
669 .type = VSH_OT_BOOL,
670 .help = N_("list inactive & active networks")
671 },
672 {.name = "persistent",
673 .type = VSH_OT_BOOL,
674 .help = N_("list persistent networks")
675 },
676 {.name = "transient",
677 .type = VSH_OT_BOOL,
678 .help = N_("list transient networks")
679 },
680 {.name = "autostart",
681 .type = VSH_OT_BOOL,
682 .help = N_("list networks with autostart enabled")
683 },
684 {.name = "no-autostart",
685 .type = VSH_OT_BOOL,
686 .help = N_("list networks with autostart disabled")
687 },
688 {.name = "uuid",
689 .type = VSH_OT_BOOL,
690 .help = N_("list uuid's only")
691 },
692 {.name = "name",
693 .type = VSH_OT_BOOL,
694 .help = N_("list network names only")
695 },
696 {.name = "table",
697 .type = VSH_OT_BOOL,
698 .help = N_("list table (default)")
699 },
700 {.name = NULL}
701 };
702
703 #define FILTER(NAME, FLAG) \
704 if (vshCommandOptBool(cmd, NAME)) \
705 flags |= (FLAG)
706 static bool
cmdNetworkList(vshControl * ctl,const vshCmd * cmd G_GNUC_UNUSED)707 cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
708 {
709 struct virshNetworkList *list = NULL;
710 size_t i;
711 bool ret = false;
712 bool optName = vshCommandOptBool(cmd, "name");
713 bool optTable = vshCommandOptBool(cmd, "table");
714 bool optUUID = vshCommandOptBool(cmd, "uuid");
715 char uuid[VIR_UUID_STRING_BUFLEN];
716 unsigned int flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE;
717 g_autoptr(vshTable) table = NULL;
718
719 if (vshCommandOptBool(cmd, "inactive"))
720 flags = VIR_CONNECT_LIST_NETWORKS_INACTIVE;
721
722 if (vshCommandOptBool(cmd, "all"))
723 flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE |
724 VIR_CONNECT_LIST_NETWORKS_INACTIVE;
725
726 FILTER("persistent", VIR_CONNECT_LIST_NETWORKS_PERSISTENT);
727 FILTER("transient", VIR_CONNECT_LIST_NETWORKS_TRANSIENT);
728
729 FILTER("autostart", VIR_CONNECT_LIST_NETWORKS_AUTOSTART);
730 FILTER("no-autostart", VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART);
731
732 if (optTable + optName + optUUID > 1) {
733 vshError(ctl, "%s",
734 _("Only one argument from --table, --name and --uuid "
735 "may be specified."));
736 return false;
737 }
738
739 if (!optUUID && !optName)
740 optTable = true;
741
742 if (!(list = virshNetworkListCollect(ctl, flags)))
743 return false;
744
745 if (optTable) {
746 table = vshTableNew(_("Name"), _("State"), _("Autostart"),
747 _("Persistent"), NULL);
748 if (!table)
749 goto cleanup;
750 }
751
752 for (i = 0; i < list->nnets; i++) {
753 virNetworkPtr network = list->nets[i];
754 const char *autostartStr;
755 int is_autostart = 0;
756
757 if (optTable) {
758 if (virNetworkGetAutostart(network, &is_autostart) < 0)
759 autostartStr = _("no autostart");
760 else
761 autostartStr = is_autostart ? _("yes") : _("no");
762
763 if (vshTableRowAppend(table,
764 virNetworkGetName(network),
765 virNetworkIsActive(network) ?
766 _("active") : _("inactive"),
767 autostartStr,
768 virNetworkIsPersistent(network) ?
769 _("yes") : _("no"),
770 NULL) < 0)
771 goto cleanup;
772 } else if (optUUID) {
773 if (virNetworkGetUUIDString(network, uuid) < 0) {
774 vshError(ctl, "%s", _("Failed to get network's UUID"));
775 goto cleanup;
776 }
777 vshPrint(ctl, "%s\n", uuid);
778 } else if (optName) {
779 vshPrint(ctl, "%s\n", virNetworkGetName(network));
780 }
781 }
782
783 if (optTable)
784 vshTablePrintToStdout(table, ctl);
785
786 ret = true;
787 cleanup:
788 virshNetworkListFree(list);
789 return ret;
790 }
791 #undef FILTER
792
793 /*
794 * "net-name" command
795 */
796 static const vshCmdInfo info_network_name[] = {
797 {.name = "help",
798 .data = N_("convert a network UUID to network name")
799 },
800 {.name = "desc",
801 .data = ""
802 },
803 {.name = NULL}
804 };
805
806 static const vshCmdOptDef opts_network_name[] = {
807 {.name = "network",
808 .type = VSH_OT_DATA,
809 .flags = VSH_OFLAG_REQ,
810 .completer = virshNetworkUUIDCompleter,
811 .help = N_("network uuid")
812 },
813 {.name = NULL}
814 };
815
816 static bool
cmdNetworkName(vshControl * ctl,const vshCmd * cmd)817 cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
818 {
819 g_autoptr(virshNetwork) network = NULL;
820
821 if (!(network = virshCommandOptNetworkBy(ctl, cmd, NULL,
822 VIRSH_BYUUID)))
823 return false;
824
825 vshPrint(ctl, "%s\n", virNetworkGetName(network));
826 return true;
827 }
828
829 /*
830 * "net-start" command
831 */
832 static const vshCmdInfo info_network_start[] = {
833 {.name = "help",
834 .data = N_("start a (previously defined) inactive network")
835 },
836 {.name = "desc",
837 .data = N_("Start a network.")
838 },
839 {.name = NULL}
840 };
841
842 static const vshCmdOptDef opts_network_start[] = {
843 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_INACTIVE),
844 {.name = NULL}
845 };
846
847 static bool
cmdNetworkStart(vshControl * ctl,const vshCmd * cmd)848 cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
849 {
850 g_autoptr(virshNetwork) network = NULL;
851 bool ret = true;
852 const char *name = NULL;
853
854 if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
855 return false;
856
857 if (virNetworkCreate(network) == 0) {
858 vshPrintExtra(ctl, _("Network %s started\n"), name);
859 } else {
860 vshError(ctl, _("Failed to start network %s"), name);
861 ret = false;
862 }
863 return ret;
864 }
865
866 /*
867 * "net-undefine" command
868 */
869 static const vshCmdInfo info_network_undefine[] = {
870 {.name = "help",
871 .data = N_("undefine a persistent network")
872 },
873 {.name = "desc",
874 .data = N_("Undefine the configuration for a persistent network.")
875 },
876 {.name = NULL}
877 };
878
879 static const vshCmdOptDef opts_network_undefine[] = {
880 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_PERSISTENT),
881 {.name = NULL}
882 };
883
884 static bool
cmdNetworkUndefine(vshControl * ctl,const vshCmd * cmd)885 cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
886 {
887 g_autoptr(virshNetwork) network = NULL;
888 bool ret = true;
889 const char *name;
890
891 if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
892 return false;
893
894 if (virNetworkUndefine(network) == 0) {
895 vshPrintExtra(ctl, _("Network %s has been undefined\n"), name);
896 } else {
897 vshError(ctl, _("Failed to undefine network %s"), name);
898 ret = false;
899 }
900
901 return ret;
902 }
903
904 /*
905 * "net-update" command
906 */
907 static const vshCmdInfo info_network_update[] = {
908 {.name = "help",
909 .data = N_("update parts of an existing network's configuration")
910 },
911 {.name = "desc",
912 .data = ""
913 },
914 {.name = NULL}
915 };
916
917 static const vshCmdOptDef opts_network_update[] = {
918 VIRSH_COMMON_OPT_NETWORK_FULL(0),
919 {.name = "command",
920 .type = VSH_OT_DATA,
921 .flags = VSH_OFLAG_REQ,
922 .help = N_("type of update (add-first, add-last (add), delete, or modify)")
923 },
924 {.name = "section",
925 .type = VSH_OT_DATA,
926 .flags = VSH_OFLAG_REQ,
927 .help = N_("which section of network configuration to update")
928 },
929 {.name = "xml",
930 .type = VSH_OT_DATA,
931 .flags = VSH_OFLAG_REQ,
932 .completer = virshCompletePathLocalExisting,
933 .help = N_("name of file containing xml (or, if it starts with '<', the complete "
934 "xml element itself) to add/modify, or to be matched for search")
935 },
936 {.name = "parent-index",
937 .type = VSH_OT_INT,
938 .help = N_("which parent object to search through")
939 },
940 VIRSH_COMMON_OPT_CONFIG(N_("affect next network startup")),
941 VIRSH_COMMON_OPT_LIVE(N_("affect running network")),
942 VIRSH_COMMON_OPT_CURRENT(N_("affect current state of network")),
943 {.name = NULL}
944 };
945
946 VIR_ENUM_DECL(virshNetworkUpdateCommand);
947 VIR_ENUM_IMPL(virshNetworkUpdateCommand,
948 VIR_NETWORK_UPDATE_COMMAND_LAST,
949 "none", "modify", "delete", "add-last", "add-first");
950
951 VIR_ENUM_DECL(virshNetworkSection);
952 VIR_ENUM_IMPL(virshNetworkSection,
953 VIR_NETWORK_SECTION_LAST,
954 "none", "bridge", "domain", "ip", "ip-dhcp-host",
955 "ip-dhcp-range", "forward", "forward-interface",
956 "forward-pf", "portgroup", "dns-host", "dns-txt",
957 "dns-srv");
958
959 static bool
cmdNetworkUpdate(vshControl * ctl,const vshCmd * cmd)960 cmdNetworkUpdate(vshControl *ctl, const vshCmd *cmd)
961 {
962 bool ret = false;
963 g_autoptr(virshNetwork) network = NULL;
964 const char *commandStr = NULL;
965 const char *sectionStr = NULL;
966 int command, section, parentIndex = -1;
967 const char *xml = NULL;
968 g_autofree char *xmlFromFile = NULL;
969 bool config = vshCommandOptBool(cmd, "config");
970 bool live = vshCommandOptBool(cmd, "live");
971 unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT;
972
973 VSH_EXCLUSIVE_OPTIONS("current", "live");
974 VSH_EXCLUSIVE_OPTIONS("current", "config");
975
976 if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
977 return false;
978
979 if (vshCommandOptStringReq(ctl, cmd, "command", &commandStr) < 0)
980 goto cleanup;
981
982 if (STREQ(commandStr, "add")) {
983 /* "add" is a synonym for "add-last" */
984 command = VIR_NETWORK_UPDATE_COMMAND_ADD_LAST;
985 } else {
986 command = virshNetworkUpdateCommandTypeFromString(commandStr);
987 if (command <= 0 || command >= VIR_NETWORK_UPDATE_COMMAND_LAST) {
988 vshError(ctl, _("unrecognized command name '%s'"), commandStr);
989 goto cleanup;
990 }
991 }
992
993 if (vshCommandOptStringReq(ctl, cmd, "section", §ionStr) < 0)
994 goto cleanup;
995
996 section = virshNetworkSectionTypeFromString(sectionStr);
997 if (section <= 0 || section >= VIR_NETWORK_SECTION_LAST) {
998 vshError(ctl, _("unrecognized section name '%s'"), sectionStr);
999 goto cleanup;
1000 }
1001
1002 if (vshCommandOptInt(ctl, cmd, "parent-index", &parentIndex) < 0)
1003 goto cleanup;
1004
1005 /* The goal is to have a full xml element in the "xml"
1006 * string. This is provided in the --xml option, either directly
1007 * (detected by the first character being "<"), or indirectly by
1008 * supplying a filename (first character isn't "<") that contains
1009 * the desired xml.
1010 */
1011
1012 if (vshCommandOptStringReq(ctl, cmd, "xml", &xml) < 0)
1013 goto cleanup;
1014
1015 if (*xml != '<') {
1016 /* contents of xmldata is actually the name of a file that
1017 * contains the xml.
1018 */
1019 if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlFromFile) < 0)
1020 goto cleanup;
1021 /* NB: the original xml is just a const char * that points
1022 * to a string owned by the vshCmd object, and will be freed
1023 * by vshCommandFree, so it's safe to lose its pointer here.
1024 */
1025 xml = xmlFromFile;
1026 }
1027
1028 if (config)
1029 flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG;
1030 if (live)
1031 flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE;
1032
1033 if (virNetworkUpdate(network, command,
1034 section, parentIndex, xml, flags) < 0) {
1035 vshError(ctl, _("Failed to update network %s"),
1036 virNetworkGetName(network));
1037 goto cleanup;
1038 }
1039
1040 if (config) {
1041 if (live)
1042 vshPrintExtra(ctl, _("Updated network %s persistent config and "
1043 "live state"),
1044 virNetworkGetName(network));
1045 else
1046 vshPrintExtra(ctl, _("Updated network %s persistent config"),
1047 virNetworkGetName(network));
1048 } else if (live) {
1049 vshPrintExtra(ctl, _("Updated network %s live state"),
1050 virNetworkGetName(network));
1051 } else if (virNetworkIsActive(network)) {
1052 vshPrintExtra(ctl, _("Updated network %s live state"),
1053 virNetworkGetName(network));
1054 } else {
1055 vshPrintExtra(ctl, _("Updated network %s persistent config"),
1056 virNetworkGetName(network));
1057 }
1058
1059 ret = true;
1060 cleanup:
1061 vshReportError(ctl);
1062 return ret;
1063 }
1064
1065 /*
1066 * "net-uuid" command
1067 */
1068 static const vshCmdInfo info_network_uuid[] = {
1069 {.name = "help",
1070 .data = N_("convert a network name to network UUID")
1071 },
1072 {.name = "desc",
1073 .data = ""
1074 },
1075 {.name = NULL}
1076 };
1077
1078 static const vshCmdOptDef opts_network_uuid[] = {
1079 VIRSH_COMMON_OPT_NETWORK(N_("network name"), 0),
1080 {.name = NULL}
1081 };
1082
1083 static bool
cmdNetworkUuid(vshControl * ctl,const vshCmd * cmd)1084 cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
1085 {
1086 g_autoptr(virshNetwork) network = NULL;
1087 char uuid[VIR_UUID_STRING_BUFLEN];
1088
1089 if (!(network = virshCommandOptNetworkBy(ctl, cmd, NULL,
1090 VIRSH_BYNAME)))
1091 return false;
1092
1093 if (virNetworkGetUUIDString(network, uuid) != -1)
1094 vshPrint(ctl, "%s\n", uuid);
1095 else
1096 vshError(ctl, "%s", _("failed to get network UUID"));
1097
1098 return true;
1099 }
1100
1101 /*
1102 * "net-edit" command
1103 */
1104 static const vshCmdInfo info_network_edit[] = {
1105 {.name = "help",
1106 .data = N_("edit XML configuration for a network")
1107 },
1108 {.name = "desc",
1109 .data = N_("Edit the XML configuration for a network.")
1110 },
1111 {.name = NULL}
1112 };
1113
1114 static const vshCmdOptDef opts_network_edit[] = {
1115 VIRSH_COMMON_OPT_NETWORK_FULL(0),
1116 {.name = NULL}
1117 };
1118
virshNetworkGetXMLDesc(virNetworkPtr network)1119 static char *virshNetworkGetXMLDesc(virNetworkPtr network)
1120 {
1121 unsigned int flags = VIR_NETWORK_XML_INACTIVE;
1122 char *doc = virNetworkGetXMLDesc(network, flags);
1123
1124 if (!doc && last_error->code == VIR_ERR_INVALID_ARG) {
1125 /* The server side libvirt doesn't support
1126 * VIR_NETWORK_XML_INACTIVE, so retry without it.
1127 */
1128 vshResetLibvirtError();
1129 flags &= ~VIR_NETWORK_XML_INACTIVE;
1130 doc = virNetworkGetXMLDesc(network, flags);
1131 }
1132 return doc;
1133 }
1134
1135 static bool
cmdNetworkEdit(vshControl * ctl,const vshCmd * cmd)1136 cmdNetworkEdit(vshControl *ctl, const vshCmd *cmd)
1137 {
1138 bool ret = false;
1139 g_autoptr(virshNetwork) network = NULL;
1140 g_autoptr(virshNetwork) network_edited = NULL;
1141 virshControl *priv = ctl->privData;
1142
1143 network = virshCommandOptNetwork(ctl, cmd, NULL);
1144 if (network == NULL)
1145 goto cleanup;
1146
1147 #define EDIT_GET_XML virshNetworkGetXMLDesc(network)
1148 #define EDIT_NOT_CHANGED \
1149 do { \
1150 vshPrintExtra(ctl, _("Network %s XML configuration not changed.\n"), \
1151 virNetworkGetName(network)); \
1152 ret = true; \
1153 goto edit_cleanup; \
1154 } while (0)
1155 #define EDIT_DEFINE \
1156 (network_edited = virNetworkDefineXML(priv->conn, doc_edited))
1157 #include "virsh-edit.c"
1158
1159 vshPrintExtra(ctl, _("Network %s XML configuration edited.\n"),
1160 virNetworkGetName(network_edited));
1161
1162 ret = true;
1163
1164 cleanup:
1165 return ret;
1166 }
1167
1168
1169 /*
1170 * "net-event" command
1171 */
1172 VIR_ENUM_DECL(virshNetworkEvent);
1173 VIR_ENUM_IMPL(virshNetworkEvent,
1174 VIR_NETWORK_EVENT_LAST,
1175 N_("Defined"),
1176 N_("Undefined"),
1177 N_("Started"),
1178 N_("Stopped"));
1179
1180 static const char *
virshNetworkEventToString(int event)1181 virshNetworkEventToString(int event)
1182 {
1183 const char *str = virshNetworkEventTypeToString(event);
1184 return str ? _(str) : _("unknown");
1185 }
1186
1187 struct virshNetEventData {
1188 vshControl *ctl;
1189 bool loop;
1190 bool timestamp;
1191 int count;
1192 virshNetworkEventCallback *cb;
1193 };
1194 typedef struct virshNetEventData virshNetEventData;
1195
1196 VIR_ENUM_DECL(virshNetworkEventId);
1197 VIR_ENUM_IMPL(virshNetworkEventId,
1198 VIR_NETWORK_EVENT_ID_LAST,
1199 "lifecycle");
1200
1201 static void
vshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,virNetworkPtr net,int event,int detail G_GNUC_UNUSED,void * opaque)1202 vshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
1203 virNetworkPtr net,
1204 int event,
1205 int detail G_GNUC_UNUSED,
1206 void *opaque)
1207 {
1208 virshNetEventData *data = opaque;
1209
1210 if (!data->loop && data->count)
1211 return;
1212
1213 if (data->timestamp) {
1214 char timestamp[VIR_TIME_STRING_BUFLEN];
1215
1216 if (virTimeStringNowRaw(timestamp) < 0)
1217 timestamp[0] = '\0';
1218
1219 vshPrint(data->ctl, _("%s: event 'lifecycle' for network %s: %s\n"),
1220 timestamp,
1221 virNetworkGetName(net), virshNetworkEventToString(event));
1222 } else {
1223 vshPrint(data->ctl, _("event 'lifecycle' for network %s: %s\n"),
1224 virNetworkGetName(net), virshNetworkEventToString(event));
1225 }
1226
1227 data->count++;
1228 if (!data->loop)
1229 vshEventDone(data->ctl);
1230 }
1231
1232 virshNetworkEventCallback virshNetworkEventCallbacks[] = {
1233 { "lifecycle",
1234 VIR_NETWORK_EVENT_CALLBACK(vshEventLifecyclePrint), },
1235 };
1236 G_STATIC_ASSERT(VIR_NETWORK_EVENT_ID_LAST == G_N_ELEMENTS(virshNetworkEventCallbacks));
1237
1238 static const vshCmdInfo info_network_event[] = {
1239 {.name = "help",
1240 .data = N_("Network Events")
1241 },
1242 {.name = "desc",
1243 .data = N_("List event types, or wait for network events to occur")
1244 },
1245 {.name = NULL}
1246 };
1247
1248 static const vshCmdOptDef opts_network_event[] = {
1249 VIRSH_COMMON_OPT_NETWORK_OT_STRING(N_("filter by network name or uuid"), 0),
1250 {.name = "event",
1251 .type = VSH_OT_STRING,
1252 .completer = virshNetworkEventNameCompleter,
1253 .help = N_("which event type to wait for")
1254 },
1255 {.name = "loop",
1256 .type = VSH_OT_BOOL,
1257 .help = N_("loop until timeout or interrupt, rather than one-shot")
1258 },
1259 {.name = "timeout",
1260 .type = VSH_OT_INT,
1261 .help = N_("timeout seconds")
1262 },
1263 {.name = "list",
1264 .type = VSH_OT_BOOL,
1265 .help = N_("list valid event types")
1266 },
1267 {.name = "timestamp",
1268 .type = VSH_OT_BOOL,
1269 .help = N_("show timestamp for each printed event")
1270 },
1271 {.name = NULL}
1272 };
1273
1274 static bool
cmdNetworkEvent(vshControl * ctl,const vshCmd * cmd)1275 cmdNetworkEvent(vshControl *ctl, const vshCmd *cmd)
1276 {
1277 g_autoptr(virshNetwork) net = NULL;
1278 bool ret = false;
1279 int eventId = -1;
1280 int timeout = 0;
1281 virshNetEventData data;
1282 const char *eventName = NULL;
1283 int event;
1284 virshControl *priv = ctl->privData;
1285
1286 if (vshCommandOptBool(cmd, "list")) {
1287 size_t i;
1288
1289 for (i = 0; i < VIR_NETWORK_EVENT_ID_LAST; i++)
1290 vshPrint(ctl, "%s\n", virshNetworkEventCallbacks[i].name);
1291 return true;
1292 }
1293
1294 if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
1295 return false;
1296 if (!eventName) {
1297 vshError(ctl, "%s", _("either --list or --event <type> is required"));
1298 return false;
1299 }
1300 for (event = 0; event < VIR_NETWORK_EVENT_ID_LAST; event++)
1301 if (STREQ(eventName, virshNetworkEventCallbacks[event].name))
1302 break;
1303 if (event == VIR_NETWORK_EVENT_ID_LAST) {
1304 vshError(ctl, _("unknown event type %s"), eventName);
1305 return false;
1306 }
1307
1308 data.ctl = ctl;
1309 data.loop = vshCommandOptBool(cmd, "loop");
1310 data.timestamp = vshCommandOptBool(cmd, "timestamp");
1311 data.count = 0;
1312 data.cb = &virshNetworkEventCallbacks[event];
1313 if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
1314 return false;
1315
1316 if (vshCommandOptBool(cmd, "network"))
1317 net = virshCommandOptNetwork(ctl, cmd, NULL);
1318 if (vshEventStart(ctl, timeout) < 0)
1319 goto cleanup;
1320
1321 if ((eventId = virConnectNetworkEventRegisterAny(priv->conn, net, event,
1322 data.cb->cb,
1323 &data, NULL)) < 0)
1324 goto cleanup;
1325 switch (vshEventWait(ctl)) {
1326 case VSH_EVENT_INTERRUPT:
1327 vshPrint(ctl, "%s", _("event loop interrupted\n"));
1328 break;
1329 case VSH_EVENT_TIMEOUT:
1330 vshPrint(ctl, "%s", _("event loop timed out\n"));
1331 break;
1332 case VSH_EVENT_DONE:
1333 break;
1334 default:
1335 goto cleanup;
1336 }
1337 vshPrint(ctl, _("events received: %d\n"), data.count);
1338 if (data.count)
1339 ret = true;
1340
1341 cleanup:
1342 vshEventCleanup(ctl);
1343 if (eventId >= 0 &&
1344 virConnectNetworkEventDeregisterAny(priv->conn, eventId) < 0)
1345 ret = false;
1346 return ret;
1347 }
1348
1349
1350 /*
1351 * "net-dhcp-leases" command
1352 */
1353 static const vshCmdInfo info_network_dhcp_leases[] = {
1354 {.name = "help",
1355 .data = N_("print lease info for a given network")
1356 },
1357 {.name = "desc",
1358 .data = N_("Print lease info for a given network")
1359 },
1360 {.name = NULL}
1361 };
1362
1363 static const vshCmdOptDef opts_network_dhcp_leases[] = {
1364 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1365 {.name = "mac",
1366 .type = VSH_OT_STRING,
1367 .flags = VSH_OFLAG_NONE,
1368 .help = N_("MAC address"),
1369 .completer = virshNetworkDhcpMacCompleter,
1370 },
1371 {.name = NULL}
1372 };
1373
1374 static int
virshNetworkDHCPLeaseSorter(const void * a,const void * b)1375 virshNetworkDHCPLeaseSorter(const void *a, const void *b)
1376 {
1377 virNetworkDHCPLeasePtr *lease1 = (virNetworkDHCPLeasePtr *) a;
1378 virNetworkDHCPLeasePtr *lease2 = (virNetworkDHCPLeasePtr *) b;
1379
1380 if (*lease1 && !*lease2)
1381 return -1;
1382
1383 if (!*lease1)
1384 return *lease2 != NULL;
1385
1386 return vshStrcasecmp((*lease1)->mac, (*lease2)->mac);
1387 }
1388
1389 static bool
cmdNetworkDHCPLeases(vshControl * ctl,const vshCmd * cmd)1390 cmdNetworkDHCPLeases(vshControl *ctl, const vshCmd *cmd)
1391 {
1392 const char *name = NULL;
1393 const char *mac = NULL;
1394 virNetworkDHCPLeasePtr *leases = NULL;
1395 int nleases = 0;
1396 bool ret = false;
1397 size_t i;
1398 unsigned int flags = 0;
1399 g_autoptr(virshNetwork) network = NULL;
1400 g_autoptr(vshTable) table = NULL;
1401
1402 if (vshCommandOptStringReq(ctl, cmd, "mac", &mac) < 0)
1403 return false;
1404
1405 if (!(network = virshCommandOptNetwork(ctl, cmd, &name)))
1406 return false;
1407
1408 if ((nleases = virNetworkGetDHCPLeases(network, mac, &leases, flags)) < 0) {
1409 vshError(ctl, _("Failed to get leases info for %s"), name);
1410 goto cleanup;
1411 }
1412
1413 /* Sort the list according to MAC Address/IAID */
1414 qsort(leases, nleases, sizeof(*leases), virshNetworkDHCPLeaseSorter);
1415
1416 table = vshTableNew(_("Expiry Time"), _("MAC address"), _("Protocol"),
1417 _("IP address"), _("Hostname"), _("Client ID or DUID"),
1418 NULL);
1419 if (!table)
1420 goto cleanup;
1421
1422 for (i = 0; i < nleases; i++) {
1423 const char *typestr = NULL;
1424 g_autofree char *cidr_format = NULL;
1425 virNetworkDHCPLeasePtr lease = leases[i];
1426 g_autoptr(GDateTime) then = g_date_time_new_from_unix_local(lease->expirytime);
1427 g_autofree char *thenstr = NULL;
1428
1429 thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S");
1430
1431 if (lease->type == VIR_IP_ADDR_TYPE_IPV4)
1432 typestr = "ipv4";
1433 else if (lease->type == VIR_IP_ADDR_TYPE_IPV6)
1434 typestr = "ipv6";
1435
1436 cidr_format = g_strdup_printf("%s/%d", lease->ipaddr, lease->prefix);
1437
1438 if (vshTableRowAppend(table,
1439 thenstr,
1440 NULLSTR_MINUS(lease->mac),
1441 NULLSTR_MINUS(typestr),
1442 NULLSTR_MINUS(cidr_format),
1443 NULLSTR_MINUS(lease->hostname),
1444 NULLSTR_MINUS(lease->clientid),
1445 NULL) < 0)
1446 goto cleanup;
1447 }
1448
1449 vshTablePrintToStdout(table, ctl);
1450
1451 ret = true;
1452
1453 cleanup:
1454 if (leases) {
1455 for (i = 0; i < nleases; i++)
1456 virNetworkDHCPLeaseFree(leases[i]);
1457 VIR_FREE(leases);
1458 }
1459 return ret;
1460 }
1461
1462 /*
1463 * "net-port-create" command
1464 */
1465 static const vshCmdInfo info_network_port_create[] = {
1466 {.name = "help",
1467 .data = N_("create a network port from an XML file")
1468 },
1469 {.name = "desc",
1470 .data = N_("Create a network port.")
1471 },
1472 {.name = NULL}
1473 };
1474
1475 static const vshCmdOptDef opts_network_port_create[] = {
1476 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1477 VIRSH_COMMON_OPT_FILE(N_("file containing an XML network port description")),
1478 {.name = "validate",
1479 .type = VSH_OT_BOOL,
1480 .help = N_("validate the XML against the schema")
1481 },
1482 {.name = NULL}
1483 };
1484
1485 static bool
cmdNetworkPortCreate(vshControl * ctl,const vshCmd * cmd)1486 cmdNetworkPortCreate(vshControl *ctl, const vshCmd *cmd)
1487 {
1488 virNetworkPortPtr port = NULL;
1489 const char *from = NULL;
1490 bool ret = false;
1491 char *buffer = NULL;
1492 g_autoptr(virshNetwork) network = NULL;
1493 unsigned int flags = 0;
1494
1495 network = virshCommandOptNetwork(ctl, cmd, NULL);
1496 if (network == NULL)
1497 goto cleanup;
1498
1499 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
1500 goto cleanup;
1501
1502 if (vshCommandOptBool(cmd, "validate"))
1503 flags |= VIR_NETWORK_PORT_CREATE_VALIDATE;
1504
1505 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
1506 vshSaveLibvirtError();
1507 goto cleanup;
1508 }
1509
1510 port = virNetworkPortCreateXML(network, buffer, flags);
1511
1512 if (port != NULL) {
1513 char uuidstr[VIR_UUID_STRING_BUFLEN];
1514 virNetworkPortGetUUIDString(port, uuidstr);
1515 vshPrintExtra(ctl, _("Network port %s created from %s\n"),
1516 uuidstr, from);
1517 } else {
1518 vshError(ctl, _("Failed to create network from %s"), from);
1519 goto cleanup;
1520 }
1521
1522 ret = true;
1523 cleanup:
1524 VIR_FREE(buffer);
1525 if (port)
1526 virNetworkPortFree(port);
1527 return ret;
1528 }
1529
1530 /*
1531 * "net-port-dumpxml" command
1532 */
1533 static const vshCmdInfo info_network_port_dumpxml[] = {
1534 {.name = "help",
1535 .data = N_("network port information in XML")
1536 },
1537 {.name = "desc",
1538 .data = N_("Output the network port information as an XML dump to stdout.")
1539 },
1540 {.name = NULL}
1541 };
1542
1543 static const vshCmdOptDef opts_network_port_dumpxml[] = {
1544 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1545 VIRSH_COMMON_OPT_NETWORK_PORT(0),
1546 {.name = NULL}
1547 };
1548
1549 static bool
cmdNetworkPortDumpXML(vshControl * ctl,const vshCmd * cmd)1550 cmdNetworkPortDumpXML(vshControl *ctl, const vshCmd *cmd)
1551 {
1552 g_autoptr(virshNetwork) network = NULL;
1553 virNetworkPortPtr port = NULL;
1554 bool ret = true;
1555 g_autofree char *dump = NULL;
1556 unsigned int flags = 0;
1557
1558 if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
1559 goto cleanup;
1560
1561 if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL)))
1562 goto cleanup;
1563
1564 dump = virNetworkPortGetXMLDesc(port, flags);
1565
1566 if (dump != NULL) {
1567 vshPrint(ctl, "%s", dump);
1568 } else {
1569 ret = false;
1570 }
1571
1572 cleanup:
1573 if (port)
1574 virNetworkPortFree(port);
1575 return ret;
1576 }
1577
1578
1579 /*
1580 * "net-port-delete" command
1581 */
1582 static const vshCmdInfo info_network_port_delete[] = {
1583 {.name = "help",
1584 .data = N_("delete the specified network port")
1585 },
1586 {.name = "desc",
1587 .data = N_("Delete the specified network port.")
1588 },
1589 {.name = NULL}
1590 };
1591
1592 static const vshCmdOptDef opts_network_port_delete[] = {
1593 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1594 VIRSH_COMMON_OPT_NETWORK_PORT(0),
1595 {.name = NULL}
1596 };
1597
1598 static bool
cmdNetworkPortDelete(vshControl * ctl,const vshCmd * cmd)1599 cmdNetworkPortDelete(vshControl *ctl, const vshCmd *cmd)
1600 {
1601 g_autoptr(virshNetwork) network = NULL;
1602 virNetworkPortPtr port = NULL;
1603 bool ret = true;
1604 char uuidstr[VIR_UUID_STRING_BUFLEN];
1605
1606 if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
1607 goto cleanup;
1608
1609 if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL)))
1610 goto cleanup;
1611
1612 if (virNetworkPortGetUUIDString(port, uuidstr) < 0)
1613 goto cleanup;
1614
1615 if (virNetworkPortDelete(port, 0) < 0) {
1616 vshError(ctl, _("Failed to delete network port %s"), uuidstr);
1617 goto cleanup;
1618 } else {
1619 vshPrintExtra(ctl, _("Network port %s deleted\n"), uuidstr);
1620 }
1621
1622 ret = true;
1623 cleanup:
1624 if (port)
1625 virNetworkPortFree(port);
1626 return ret;
1627 }
1628
1629
1630 static int
virshNetworkPortSorter(const void * a,const void * b)1631 virshNetworkPortSorter(const void *a, const void *b)
1632 {
1633 virNetworkPortPtr *na = (virNetworkPortPtr *) a;
1634 virNetworkPortPtr *nb = (virNetworkPortPtr *) b;
1635 unsigned char uuida[VIR_UUID_BUFLEN];
1636 unsigned char uuidb[VIR_UUID_BUFLEN];
1637
1638 if (*na && !*nb)
1639 return -1;
1640
1641 if (!*na)
1642 return *nb != NULL;
1643
1644 if (virNetworkPortGetUUID(*na, uuida) < 0 ||
1645 virNetworkPortGetUUID(*nb, uuidb) < 0)
1646 return -1;
1647
1648 return memcmp(uuida, uuidb, VIR_UUID_BUFLEN);
1649 }
1650
1651 struct virshNetworkPortList {
1652 virNetworkPortPtr *ports;
1653 size_t nports;
1654 };
1655
1656 static void
virshNetworkPortListFree(struct virshNetworkPortList * list)1657 virshNetworkPortListFree(struct virshNetworkPortList *list)
1658 {
1659 size_t i;
1660
1661 if (list && list->ports) {
1662 for (i = 0; i < list->nports; i++) {
1663 if (list->ports[i])
1664 virNetworkPortFree(list->ports[i]);
1665 }
1666 g_free(list->ports);
1667 }
1668 g_free(list);
1669 }
1670
1671 static struct virshNetworkPortList *
virshNetworkPortListCollect(vshControl * ctl,const vshCmd * cmd,unsigned int flags)1672 virshNetworkPortListCollect(vshControl *ctl,
1673 const vshCmd *cmd,
1674 unsigned int flags)
1675 {
1676 struct virshNetworkPortList *list = g_new0(struct virshNetworkPortList, 1);
1677 int ret;
1678 g_autoptr(virshNetwork) network = NULL;
1679 bool success = false;
1680
1681 if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
1682 goto cleanup;
1683
1684 if ((ret = virNetworkListAllPorts(network,
1685 &list->ports,
1686 flags)) < 0)
1687 goto cleanup;
1688
1689 list->nports = ret;
1690
1691 /* sort the list */
1692 if (list->ports && list->nports)
1693 qsort(list->ports, list->nports,
1694 sizeof(*list->ports), virshNetworkPortSorter);
1695
1696 success = true;
1697
1698 cleanup:
1699 if (!success) {
1700 virshNetworkPortListFree(list);
1701 list = NULL;
1702 }
1703
1704 return list;
1705 }
1706
1707 /*
1708 * "net-list" command
1709 */
1710 static const vshCmdInfo info_network_port_list[] = {
1711 {.name = "help",
1712 .data = N_("list network ports")
1713 },
1714 {.name = "desc",
1715 .data = N_("Returns list of network ports.")
1716 },
1717 {.name = NULL}
1718 };
1719
1720 static const vshCmdOptDef opts_network_port_list[] = {
1721 VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
1722 {.name = "uuid",
1723 .type = VSH_OT_BOOL,
1724 .help = N_("list uuid's only")
1725 },
1726 {.name = "table",
1727 .type = VSH_OT_BOOL,
1728 .help = N_("list table (default)")
1729 },
1730 {.name = NULL}
1731 };
1732
1733 static bool
cmdNetworkPortList(vshControl * ctl,const vshCmd * cmd)1734 cmdNetworkPortList(vshControl *ctl, const vshCmd *cmd)
1735 {
1736 struct virshNetworkPortList *list = NULL;
1737 size_t i;
1738 bool ret = false;
1739 bool optTable = vshCommandOptBool(cmd, "table");
1740 bool optUUID = vshCommandOptBool(cmd, "uuid");
1741 char uuid[VIR_UUID_STRING_BUFLEN];
1742 unsigned int flags = 0;
1743 g_autoptr(vshTable) table = NULL;
1744
1745 if (optTable + optUUID > 1) {
1746 vshError(ctl, "%s",
1747 _("Only one argument from --table and --uuid "
1748 "may be specified."));
1749 return false;
1750 }
1751
1752 if (!optUUID)
1753 optTable = true;
1754
1755 if (!(list = virshNetworkPortListCollect(ctl, cmd, flags)))
1756 return false;
1757
1758 if (optTable) {
1759 table = vshTableNew(_("UUID"), NULL);
1760 if (!table)
1761 goto cleanup;
1762 }
1763
1764 for (i = 0; i < list->nports; i++) {
1765 virNetworkPortPtr port = list->ports[i];
1766
1767 if (virNetworkPortGetUUIDString(port, uuid) < 0) {
1768 vshError(ctl, "%s", _("Failed to get network's UUID"));
1769 goto cleanup;
1770 }
1771 if (optTable) {
1772 if (vshTableRowAppend(table, uuid, NULL) < 0)
1773 goto cleanup;
1774 } else if (optUUID) {
1775 vshPrint(ctl, "%s\n", uuid);
1776 }
1777 }
1778
1779 if (optTable)
1780 vshTablePrintToStdout(table, ctl);
1781
1782 ret = true;
1783 cleanup:
1784 virshNetworkPortListFree(list);
1785 return ret;
1786 }
1787
1788
1789 const vshCmdDef networkCmds[] = {
1790 {.name = "net-autostart",
1791 .handler = cmdNetworkAutostart,
1792 .opts = opts_network_autostart,
1793 .info = info_network_autostart,
1794 .flags = 0
1795 },
1796 {.name = "net-create",
1797 .handler = cmdNetworkCreate,
1798 .opts = opts_network_create,
1799 .info = info_network_create,
1800 .flags = 0
1801 },
1802 {.name = "net-define",
1803 .handler = cmdNetworkDefine,
1804 .opts = opts_network_define,
1805 .info = info_network_define,
1806 .flags = 0
1807 },
1808 {.name = "net-destroy",
1809 .handler = cmdNetworkDestroy,
1810 .opts = opts_network_destroy,
1811 .info = info_network_destroy,
1812 .flags = 0
1813 },
1814 {.name = "net-dhcp-leases",
1815 .handler = cmdNetworkDHCPLeases,
1816 .opts = opts_network_dhcp_leases,
1817 .info = info_network_dhcp_leases,
1818 .flags = 0,
1819 },
1820 {.name = "net-dumpxml",
1821 .handler = cmdNetworkDumpXML,
1822 .opts = opts_network_dumpxml,
1823 .info = info_network_dumpxml,
1824 .flags = 0
1825 },
1826 {.name = "net-edit",
1827 .handler = cmdNetworkEdit,
1828 .opts = opts_network_edit,
1829 .info = info_network_edit,
1830 .flags = 0
1831 },
1832 {.name = "net-event",
1833 .handler = cmdNetworkEvent,
1834 .opts = opts_network_event,
1835 .info = info_network_event,
1836 .flags = 0
1837 },
1838 {.name = "net-info",
1839 .handler = cmdNetworkInfo,
1840 .opts = opts_network_info,
1841 .info = info_network_info,
1842 .flags = 0
1843 },
1844 {.name = "net-list",
1845 .handler = cmdNetworkList,
1846 .opts = opts_network_list,
1847 .info = info_network_list,
1848 .flags = 0
1849 },
1850 {.name = "net-name",
1851 .handler = cmdNetworkName,
1852 .opts = opts_network_name,
1853 .info = info_network_name,
1854 .flags = 0
1855 },
1856 {.name = "net-start",
1857 .handler = cmdNetworkStart,
1858 .opts = opts_network_start,
1859 .info = info_network_start,
1860 .flags = 0
1861 },
1862 {.name = "net-undefine",
1863 .handler = cmdNetworkUndefine,
1864 .opts = opts_network_undefine,
1865 .info = info_network_undefine,
1866 .flags = 0
1867 },
1868 {.name = "net-update",
1869 .handler = cmdNetworkUpdate,
1870 .opts = opts_network_update,
1871 .info = info_network_update,
1872 .flags = 0
1873 },
1874 {.name = "net-uuid",
1875 .handler = cmdNetworkUuid,
1876 .opts = opts_network_uuid,
1877 .info = info_network_uuid,
1878 .flags = 0
1879 },
1880 {.name = "net-port-list",
1881 .handler = cmdNetworkPortList,
1882 .opts = opts_network_port_list,
1883 .info = info_network_port_list,
1884 .flags = 0
1885 },
1886 {.name = "net-port-create",
1887 .handler = cmdNetworkPortCreate,
1888 .opts = opts_network_port_create,
1889 .info = info_network_port_create,
1890 .flags = 0
1891 },
1892 {.name = "net-port-dumpxml",
1893 .handler = cmdNetworkPortDumpXML,
1894 .opts = opts_network_port_dumpxml,
1895 .info = info_network_port_dumpxml,
1896 .flags = 0
1897 },
1898 {.name = "net-port-delete",
1899 .handler = cmdNetworkPortDelete,
1900 .opts = opts_network_port_delete,
1901 .info = info_network_port_delete,
1902 .flags = 0
1903 },
1904 {.name = NULL}
1905 };
1906