[libvirt] PATCH: Add --tree flag to virsh nodedev-list

The virsh noddev-list command is used to display node devices. It just prints out their names in a flat list. When detaching devices for purposes of PCI passthrough though, it is important to understand what devices are children of the PCI device about to be detached. It is tedious to find this out, the user having to run virsh nodedev-dumpxml and look at the parent field. # virsh -c qemu:///system nodedev-list computer DVD_GCC_4247N net_00_13_02_b9_f9_d3 net_00_13_02_b9_f9_d3_0 net_00_15_58_2f_e9_55 pci_1002_71c4 pci_104c_ac56 pci_8086_27c5_scsi_host pci_8086_27c5_scsi_host_0 pci_8086_27c5_scsi_host_1 pci_8086_27c5_scsi_host_2 pci_8086_27c5_scsi_host_scsi_device_lun0 pci_8086_27c5_scsi_host_scsi_host pci_8086_27c8 pci_8086_27da pci_8086_27df pci_8086_27df_scsi_host pci_8086_27df_scsi_host_0 pci_8086_27df_scsi_host_scsi_device_lun0 pci_8086_27df_scsi_host_scsi_host pci_8086_4227 storage_serial_SATA_HTS721010G9SA00_MPCZ12Y0GNGWSE usb_device_a5c_2110_noserial_if3 This patch adds a new '--tree' flag to the nodedev-list command, allowing a prettier format to be used # virsh -c qemu:///system nodedev-list --tree computer | +-pci_8086_2448 | | | +-pci_104c_ac56 | +-pci_8086_27a0 +-pci_8086_27a1 | | | +-pci_1002_71c4 | +-pci_8086_27b9 +-pci_8086_27c5 | | | +-pci_8086_27c5_scsi_host | | | | | +-pci_8086_27c5_scsi_host_scsi_device_lun0 | | | | | | | +-storage_serial_SATA_HTS721010G9SA00_MPCZ12Y0GNGWSE | | | | | +-pci_8086_27c5_scsi_host_scsi_host | | | +-pci_8086_27c5_scsi_host_0 | +-pci_8086_27c5_scsi_host_1 | +-pci_8086_27c5_scsi_host_2 | +-pci_8086_27c8 | | | +-usb_device_1d6b_1_0000_00_1d_0 | | | +-usb_device_1d6b_1_0000_00_1d_0_if0 | +-pci_8086_27c9 | | | +-usb_device_1d6b_1_0000_00_1d_1 | | | +-usb_device_1d6b_1_0000_00_1d_1_if0 | +-pci_8086_27ca | | | +-usb_device_1d6b_1_0000_00_1d_2 | | | +-usb_device_1d6b_1_0000_00_1d_2_if0 | +-pci_8086_27cb | | | +-usb_device_1d6b_1_0000_00_1d_3 | | | +-usb_device_1d6b_1_0000_00_1d_3_if0 | +-usb_device_483_2016_noserial | | | | | +-usb_device_483_2016_noserial_if0 | | | +-usb_device_a5c_2110_noserial | | | +-usb_device_a5c_2110_noserial_if0 | +-usb_device_a5c_2110_noserial_if1 | +-usb_device_a5c_2110_noserial_if2 | +-usb_device_a5c_2110_noserial_if3 | +-pci_8086_27cc | | | +-usb_device_1d6b_2_0000_00_1d_7 | | | +-usb_device_1d6b_2_0000_00_1d_7_if0 | +-pci_8086_27d0 | | | +-pci_8086_109a | | | +-net_00_15_58_2f_e9_55 | +-pci_8086_27d2 | | | +-pci_8086_4227 | | | +-net_00_13_02_b9_f9_d3 | +-net_00_13_02_b9_f9_d3_0 | +-pci_8086_27d4 +-pci_8086_27d6 +-pci_8086_27d8 +-pci_8086_27da +-pci_8086_27df | +-pci_8086_27df_scsi_host | | | +-pci_8086_27df_scsi_host_scsi_device_lun0 | | | | | +-DVD_GCC_4247N | | | +-pci_8086_27df_scsi_host_scsi_host | +-pci_8086_27df_scsi_host_0 Daniel diff -r b73fe666feff src/virsh.c --- a/src/virsh.c Fri Mar 27 16:14:49 2009 +0000 +++ b/src/virsh.c Mon Mar 30 14:37:45 2009 +0100 @@ -4391,16 +4391,77 @@ static const vshCmdInfo info_node_list_d }; static const vshCmdOptDef opts_node_list_devices[] = { + {"tree", VSH_OT_BOOL, 0, gettext_noop("list devices in a tree")}, {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, gettext_noop("capability name")}, {NULL, 0, 0, NULL} }; +#define MAX_INDENT 100 + +static void +cmdNodeListDevicesPrint(vshControl *ctl, + char **devices, + char **parents, + int num_devices, + int devid, + int lastdev, + unsigned int depth, + char *indent) +{ + int i; + int nextlastdev = -1; + + if (depth) { + indent[depth] = '+'; + indent[depth+1] = '-'; + } + + vshPrint(ctl, indent); + if (depth) { + if (devid == lastdev) + indent[depth] = ' '; + else + indent[depth] = '|'; + indent[depth+1] = ' '; + depth+=2; + } + vshPrint(ctl, "%s\n", devices[devid]); + + for (i = 0 ; i < num_devices ; i++) { + if (parents[i] && + STREQ(parents[i], devices[devid])) { + //fprintf(stderr, "%s %s %d %d\n", parents[i], devices[devid], i, devid); + nextlastdev = i; + } + } + + if (nextlastdev != -1) { + vshPrint(ctl, indent); + vshPrint(ctl, " |\n"); + } + + indent[depth] = ' '; + for (i = 0 ; i < num_devices ; i++) { + indent[depth] = ' '; + indent[depth+1] = ' '; + if (parents[i] && + STREQ(parents[i], devices[devid])) + cmdNodeListDevicesPrint(ctl, devices, parents, num_devices, i, nextlastdev, depth + 2, indent); + indent[depth] = '\0'; + } + if (nextlastdev == -1 && devid == lastdev) { + vshPrint(ctl, indent); + vshPrint(ctl, "\n"); + } +} + static int cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { char *cap; char **devices; int found, num_devices, i; + int tree = vshCommandOptBool(cmd, "tree"); if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; @@ -4426,9 +4487,39 @@ cmdNodeListDevices (vshControl *ctl, con return FALSE; } qsort(&devices[0], num_devices, sizeof(char*), namesorter); - for (i = 0; i < num_devices; i++) { - vshPrint(ctl, "%s\n", devices[i]); - free(devices[i]); + if (tree) { + char indent[MAX_INDENT]; + char **parents = vshMalloc(ctl, sizeof(char *) * num_devices); + for (i = 0; i < num_devices; i++) { + virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn, devices[i]); + if (dev && STRNEQ(devices[i], "computer")) { + const char *parent = virNodeDeviceGetParent(dev); + parents[i] = parent ? strdup(parent) : NULL; + } else { + parents[i] = NULL; + } + virNodeDeviceFree(dev); + } + for (i = 0 ; i < num_devices ; i++) { + memset(indent, '\0', sizeof indent); + if (parents[i] == NULL) + cmdNodeListDevicesPrint(ctl, + devices, + parents, + num_devices, + i, + i, + 0, + indent); + } + for (i = 0 ; i < num_devices ; i++) + free(parents[i]); + free(parents); + } else { + for (i = 0; i < num_devices; i++) { + vshPrint(ctl, "%s\n", devices[i]); + free(devices[i]); + } } free(devices); return TRUE; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Mon, Mar 30, 2009 at 02:44:31PM +0100, Daniel P. Berrange wrote:
The virsh noddev-list command is used to display node devices. It just prints out their names in a flat list. When detaching devices for purposes of PCI passthrough though, it is important to understand what devices are children of the PCI device about to be detached. It is tedious to find this out, the user having to run virsh nodedev-dumpxml and look at the parent field.
[...]
This patch adds a new '--tree' flag to the nodedev-list command, allowing a prettier format to be used
# virsh -c qemu:///system nodedev-list --tree computer | +-pci_8086_2448 | | | +-pci_104c_ac56
Very cool !
+#define MAX_INDENT 100 + +static void +cmdNodeListDevicesPrint(vshControl *ctl, + char **devices, + char **parents, + int num_devices, + int devid, + int lastdev, + unsigned int depth, + char *indent) +{ + int i; + int nextlastdev = -1;
Before even modifying indent[depth] here I would check that depth + 2 < MAX_INDENT and abort on an error here,
+ if (depth) { + indent[depth] = '+'; + indent[depth+1] = '-'; + }
otherwise looks fine, ACK :-) thanks ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Tue, Mar 31, 2009 at 12:09:21PM +0200, Daniel Veillard wrote:
+#define MAX_INDENT 100 + +static void +cmdNodeListDevicesPrint(vshControl *ctl, + char **devices, + char **parents, + int num_devices, + int devid, + int lastdev, + unsigned int depth, + char *indent) +{ + int i; + int nextlastdev = -1;
Before even modifying indent[depth] here I would check that depth + 2 < MAX_INDENT and abort on an error here,
Actually we have a 4 level indent here. This is all getting rather confusing, so I've separated the depth we've descended from the indentation used, and defined the buffer to be a multiple of max depth. Also fixed a minor leak in the virNodeDeviceGetParent() impl of the remote driver. In the wonderful world of XDR, we have to free the char**, but not the char *. Daniel diff -r 9a7241cce8db src/remote_internal.c --- a/src/remote_internal.c Tue Mar 31 11:43:13 2009 +0100 +++ b/src/remote_internal.c Tue Mar 31 14:55:22 2009 +0100 @@ -4821,6 +4821,7 @@ static char *remoteNodeDeviceGetParent(v /* Caller frees. */ rv = ret.parent ? *ret.parent : NULL; + VIR_FREE(ret.parent); done: remoteDriverUnlock(priv); diff -r 9a7241cce8db src/virsh.c --- a/src/virsh.c Tue Mar 31 11:43:13 2009 +0100 +++ b/src/virsh.c Tue Mar 31 14:55:22 2009 +0100 @@ -4391,16 +4391,96 @@ static const vshCmdInfo info_node_list_d }; static const vshCmdOptDef opts_node_list_devices[] = { + {"tree", VSH_OT_BOOL, 0, gettext_noop("list devices in a tree")}, {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, gettext_noop("capability name")}, {NULL, 0, 0, NULL} }; +#define MAX_DEPTH 100 +#define INDENT_SIZE 4 +#define INDENT_BUFLEN ((MAX_DEPTH * INDENT_SIZE) + 1) + +static void +cmdNodeListDevicesPrint(vshControl *ctl, + char **devices, + char **parents, + int num_devices, + int devid, + int lastdev, + unsigned int depth, + unsigned int indentIdx, + char *indentBuf) +{ + int i; + int nextlastdev = -1; + + /* Prepare indent for this device, but not if at root */ + if (depth && depth < MAX_DEPTH) { + indentBuf[indentIdx] = '+'; + indentBuf[indentIdx+1] = '-'; + } + + /* Print this device */ + vshPrint(ctl, indentBuf); + vshPrint(ctl, "%s\n", devices[devid]); + + + /* Update indent to show '|' or ' ' for child devices */ + if (depth && depth < MAX_DEPTH) { + if (devid == lastdev) + indentBuf[indentIdx] = ' '; + else + indentBuf[indentIdx] = '|'; + indentBuf[indentIdx+1] = ' '; + indentIdx+=2; + } + + /* Determine the index of the last child device */ + for (i = 0 ; i < num_devices ; i++) { + if (parents[i] && + STREQ(parents[i], devices[devid])) { + nextlastdev = i; + } + } + + /* If there is a child device, then print another blank line */ + if (nextlastdev != -1) { + vshPrint(ctl, indentBuf); + vshPrint(ctl, " |\n"); + } + + /* Finally print all children */ + if (depth < MAX_DEPTH) + indentBuf[indentIdx] = ' '; + for (i = 0 ; i < num_devices ; i++) { + if (depth < MAX_DEPTH) { + indentBuf[indentIdx] = ' '; + indentBuf[indentIdx+1] = ' '; + } + if (parents[i] && + STREQ(parents[i], devices[devid])) + cmdNodeListDevicesPrint(ctl, devices, parents, + num_devices, i, nextlastdev, + depth + 1, indentIdx + 2, indentBuf); + if (depth < MAX_DEPTH) + indentBuf[indentIdx] = '\0'; + } + + /* If there was no child device, and we're the last in + * a list of devices, then print another blank line */ + if (nextlastdev == -1 && devid == lastdev) { + vshPrint(ctl, indentBuf); + vshPrint(ctl, "\n"); + } +} + static int cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { char *cap; char **devices; int found, num_devices, i; + int tree = vshCommandOptBool(cmd, "tree"); if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; @@ -4426,9 +4506,42 @@ cmdNodeListDevices (vshControl *ctl, con return FALSE; } qsort(&devices[0], num_devices, sizeof(char*), namesorter); - for (i = 0; i < num_devices; i++) { - vshPrint(ctl, "%s\n", devices[i]); - free(devices[i]); + if (tree) { + char indentBuf[INDENT_BUFLEN]; + char **parents = vshMalloc(ctl, sizeof(char *) * num_devices); + for (i = 0; i < num_devices; i++) { + virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn, devices[i]); + if (dev && STRNEQ(devices[i], "computer")) { + const char *parent = virNodeDeviceGetParent(dev); + parents[i] = parent ? strdup(parent) : NULL; + } else { + parents[i] = NULL; + } + virNodeDeviceFree(dev); + } + for (i = 0 ; i < num_devices ; i++) { + memset(indentBuf, '\0', sizeof indentBuf); + if (parents[i] == NULL) + cmdNodeListDevicesPrint(ctl, + devices, + parents, + num_devices, + i, + i, + 0, + 0, + indentBuf); + } + for (i = 0 ; i < num_devices ; i++) { + free(devices[i]); + free(parents[i]); + } + free(parents); + } else { + for (i = 0; i < num_devices; i++) { + vshPrint(ctl, "%s\n", devices[i]); + free(devices[i]); + } } free(devices); return TRUE; Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Mar 31, 2009 at 02:58:37PM +0100, Daniel P. Berrange wrote:
On Tue, Mar 31, 2009 at 12:09:21PM +0200, Daniel Veillard wrote:
+#define MAX_INDENT 100 + +static void +cmdNodeListDevicesPrint(vshControl *ctl, + char **devices, + char **parents, + int num_devices, + int devid, + int lastdev, + unsigned int depth, + char *indent) +{ + int i; + int nextlastdev = -1;
Before even modifying indent[depth] here I would check that depth + 2 < MAX_INDENT and abort on an error here,
Actually we have a 4 level indent here. This is all getting rather confusing, so I've separated the depth we've descended from the indentation used, and defined the buffer to be a multiple of max depth.
Also fixed a minor leak in the virNodeDeviceGetParent() impl of the remote driver. In the wonderful world of XDR, we have to free the char**, but not the char *.
Okidoc, ACK ! thanks, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Tue, Mar 31, 2009 at 11:35:23PM +0200, Daniel Veillard wrote:
On Tue, Mar 31, 2009 at 02:58:37PM +0100, Daniel P. Berrange wrote:
On Tue, Mar 31, 2009 at 12:09:21PM +0200, Daniel Veillard wrote:
+#define MAX_INDENT 100 + +static void +cmdNodeListDevicesPrint(vshControl *ctl, + char **devices, + char **parents, + int num_devices, + int devid, + int lastdev, + unsigned int depth, + char *indent) +{ + int i; + int nextlastdev = -1;
Before even modifying indent[depth] here I would check that depth + 2 < MAX_INDENT and abort on an error here,
Actually we have a 4 level indent here. This is all getting rather confusing, so I've separated the depth we've descended from the indentation used, and defined the buffer to be a multiple of max depth.
Also fixed a minor leak in the virNodeDeviceGetParent() impl of the remote driver. In the wonderful world of XDR, we have to free the char**, but not the char *.
Okidoc, ACK !
Great, committed this Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (2)
-
Daniel P. Berrange
-
Daniel Veillard