diff --git a/collins/commands/BUILD.bazel b/collins/commands/BUILD.bazel index 15c781a..d68ca94 100644 --- a/collins/commands/BUILD.bazel +++ b/collins/commands/BUILD.bazel @@ -41,6 +41,7 @@ go_test( "assets/TestQueryCLIBasicWorkflow.json", "assets/TestQueryCLIGetByAttribute.json", "assets/TestQueryCLIGetGPU.json", + "assets/TestQueryIssue50.json", ], embed = ["//collins/commands:library"], deps = [ diff --git a/collins/commands/assets/TestQueryIssue50.json b/collins/commands/assets/TestQueryIssue50.json new file mode 100644 index 0000000..c04aa1a --- /dev/null +++ b/collins/commands/assets/TestQueryIssue50.json @@ -0,0 +1,762 @@ +{ + "status": "success:ok", + "data": { + "Pagination": { + "PreviousPage": 0, + "CurrentPage": 0, + "NextPage": 0, + "TotalResults": 2 + }, + "Data": [ + { + "ASSET": { + "ID": 13, + "TAG": "tag30", + "STATE": { + "ID": 1, + "STATUS": null, + "NAME": "NEW", + "LABEL": "New", + "DESCRIPTION": "A service in this state is inactive. It does minimal work and consumes minimal resources." + }, + "STATUS": "New", + "TYPE": "SERVER_NODE", + "CREATED": "2015-10-06T10:58:41", + "UPDATED": "2015-10-06T10:58:41", + "DELETED": null + }, + "HARDWARE": { + "CPU": [ + { + "CORES": 6, + "THREADS": 12, + "SPEED_GHZ": 1.6, + "DESCRIPTION": "Intel(R) Xeon(R) CPU X5675 @ 3.07GHz Intel Corp.", + "PRODUCT": "", + "VENDOR": "" + }, + { + "CORES": 6, + "THREADS": 12, + "SPEED_GHZ": 1.6, + "DESCRIPTION": "Intel(R) Xeon(R) CPU X5675 @ 3.07GHz Intel Corp.", + "PRODUCT": "", + "VENDOR": "" + } + ], + "MEMORY": [ + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 0, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 1, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 2, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 3, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 4, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 5, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 6, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 7, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 8, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 9, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 10, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 11, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 12, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 13, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 14, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 15, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 16, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 4294967296, + "SIZE_S": "4294967296", + "SIZE_HUMAN": "4.00 GB", + "BANK": 17, + "DESCRIPTION": "DIMM DDR3 800 MHz (1.2 ns) - Hyundai HMT351R7BFR8C-H9", + "PRODUCT": "", + "VENDOR": "" + } + ], + "NIC": [ + { + "SPEED": 1000000000, + "SPEED_S": "1000000000", + "SPEED_HUMAN": "1.00 Gb", + "MAC_ADDRESS": "00:25:90:2b:0c:94", + "DESCRIPTION": "82576 Gigabit Network Connection - Intel Corporation", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SPEED": 1000000000, + "SPEED_S": "1000000000", + "SPEED_HUMAN": "1.00 Gb", + "MAC_ADDRESS": "00:25:90:2b:0c:95", + "DESCRIPTION": "82576 Gigabit Network Connection - Intel Corporation", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SPEED": 1000000000, + "SPEED_S": "1000000000", + "SPEED_HUMAN": "1.00 Gb", + "MAC_ADDRESS": "00:25:90:2b:0c:94", + "DESCRIPTION": "82575EB Gigabit Network Connection - Intel Corporation", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SPEED": 1000000000, + "SPEED_S": "1000000000", + "SPEED_HUMAN": "1.00 Gb", + "MAC_ADDRESS": "00:25:90:2b:0c:95", + "DESCRIPTION": "82575EB Gigabit Network Connection - Intel Corporation", + "PRODUCT": "", + "VENDOR": "" + } + ], + "DISK": [], + "BASE": { + "DESCRIPTION": "System", + "PRODUCT": "X8DTN (1234567890)", + "VENDOR": "Supermicro", + "SERIAL": "1234567890" + } + }, + "CLASSIFICATION": { + "ID": 1305, + "TAG": "app", + "STATE": null, + "STATUS": "Incomplete", + "TYPE": "CONFIGURATION", + "CREATED": "2014-09-17T13:47:15", + "UPDATED": "2014-09-17T13:50:00", + "DELETED": null + }, + "LLDP": { + "INTERFACES": [ + { + "NAME": "eth0", + "CHASSIS": { + "NAME": "core01.dfw01", + "ID": { + "TYPE": "mac", + "VALUE": "78:19:f7:88:60:c0" + }, + "DESCRIPTION": "Juniper Networks, Inc. ex4500-40f , version 11.1S1 Build date: 2011-04-21 08:03:12 UTC " + }, + "PORT": { + "ID": { + "TYPE": "local", + "VALUE": "616" + }, + "DESCRIPTION": "ge-0/0/7.0" + }, + "VLANS": [ + { + "ID": 106, + "NAME": "DFW-LOGGING" + } + ] + } + ] + }, + "IPMI": { + "ASSET_ID": 13, + "ASSET_TAG": "tag30", + "IPMI_USERNAME": "root", + "IPMI_PASSWORD": "uGpYhjtnM6X2qtff", + "IPMI_GATEWAY": "172.16.32.1", + "IPMI_ADDRESS": "172.16.32.31", + "IPMI_NETMASK": "255.255.240.0", + "ID": 13 + }, + "ADDRESSES": [], + "POWER": [], + "ATTRIBS": { + "0": { + "BASE_SERIAL": "1234567890" + } + } + }, + { + "ASSET": { + "ID": 12, + "TAG": "tag31", + "STATE": { + "ID": 1, + "STATUS": null, + "NAME": "NEW", + "LABEL": "New", + "DESCRIPTION": "A service in this state is inactive. It does minimal work and consumes minimal resources." + }, + "STATUS": "New", + "TYPE": "SERVER_NODE", + "CREATED": "2015-10-06T10:58:30", + "UPDATED": "2015-10-06T10:58:31", + "DELETED": null + }, + "HARDWARE": { + "CPU": [ + { + "CORES": 8, + "THREADS": 16, + "SPEED_GHZ": 2, + "DESCRIPTION": "Intel(R) Xeon(R) CPU E5-2650 0 @ 2.00GHz Intel Corp.", + "PRODUCT": "", + "VENDOR": "" + } + ], + "MEMORY": [ + { + "SIZE": 8589934592, + "SIZE_S": "8589934592", + "SIZE_HUMAN": "8.00 GB", + "BANK": 0, + "DESCRIPTION": "DIMM DDR3 Synchronous 1333 MHz (0.8 ns) - 00AD04B300AD HMT31GR7CFR4A-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 8589934592, + "SIZE_S": "8589934592", + "SIZE_HUMAN": "8.00 GB", + "BANK": 1, + "DESCRIPTION": "DIMM DDR3 Synchronous 1333 MHz (0.8 ns) - 00AD04B300AD HMT31GR7CFR4A-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 8589934592, + "SIZE_S": "8589934592", + "SIZE_HUMAN": "8.00 GB", + "BANK": 2, + "DESCRIPTION": "DIMM DDR3 Synchronous 1333 MHz (0.8 ns) - 00AD04B300AD HMT31GR7CFR4A-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 8589934592, + "SIZE_S": "8589934592", + "SIZE_HUMAN": "8.00 GB", + "BANK": 3, + "DESCRIPTION": "DIMM DDR3 Synchronous 1333 MHz (0.8 ns) - 00AD04B300AD HMT31GR7CFR4A-H9", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 4, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 5, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 6, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 7, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 8, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 9, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 10, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 11, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 12, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 13, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 14, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 15, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 16, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 17, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 18, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 19, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 20, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 21, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 22, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 0, + "SIZE_S": "0", + "SIZE_HUMAN": "0 Bytes", + "BANK": 23, + "DESCRIPTION": "Empty Memory Bank", + "PRODUCT": "", + "VENDOR": "" + } + ], + "NIC": [ + { + "SPEED": 1000000000, + "SPEED_S": "1000000000", + "SPEED_HUMAN": "1.00 Gb", + "MAC_ADDRESS": "[REMOVED]", + "DESCRIPTION": "NetXtreme BCM5720 Gigabit Ethernet PCIe - Broadcom Corporation", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SPEED": 1000000000, + "SPEED_S": "1000000000", + "SPEED_HUMAN": "1.00 Gb", + "MAC_ADDRESS": "[REMOVED]", + "DESCRIPTION": "NetXtreme BCM5720 Gigabit Ethernet PCIe - Broadcom Corporation", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SPEED": 1000000000, + "SPEED_S": "1000000000", + "SPEED_HUMAN": "1.00 Gb", + "MAC_ADDRESS": "[REMOVED]", + "DESCRIPTION": "NetXtreme BCM5720 Gigabit Ethernet PCIe - Broadcom Corporation", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SPEED": 1000000000, + "SPEED_S": "1000000000", + "SPEED_HUMAN": "1.00 Gb", + "MAC_ADDRESS": "[REMOVED]", + "DESCRIPTION": "NetXtreme BCM5720 Gigabit Ethernet PCIe - Broadcom Corporation", + "PRODUCT": "", + "VENDOR": "" + } + ], + "DISK": [ + { + "SIZE": 10995116277760, + "SIZE_S": "10995116277760", + "SIZE_HUMAN": "10.00 TB", + "TYPE": "SCSI", + "DESCRIPTION": " ST9250610NS", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 10995116277760, + "SIZE_S": "10995116277760", + "SIZE_HUMAN": "10.00 TB", + "TYPE": "SCSI", + "DESCRIPTION": " INTEL SSDSA2BW16", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 10995116277760, + "SIZE_S": "10995116277760", + "SIZE_HUMAN": "10.00 TB", + "TYPE": "SCSI", + "DESCRIPTION": " INTEL SSDSA2BW16", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 10995116277760, + "SIZE_S": "10995116277760", + "SIZE_HUMAN": "10.00 TB", + "TYPE": "SCSI", + "DESCRIPTION": " INTEL SSDSA2BW16", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 10995116277760, + "SIZE_S": "10995116277760", + "SIZE_HUMAN": "10.00 TB", + "TYPE": "SCSI", + "DESCRIPTION": " INTEL SSDSA2BW16", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 10995116277760, + "SIZE_S": "10995116277760", + "SIZE_HUMAN": "10.00 TB", + "TYPE": "SCSI", + "DESCRIPTION": " INTEL SSDSA2BW16", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 10995116277760, + "SIZE_S": "10995116277760", + "SIZE_HUMAN": "10.00 TB", + "TYPE": "SCSI", + "DESCRIPTION": " INTEL SSDSA2BW16", + "PRODUCT": "", + "VENDOR": "" + }, + { + "SIZE": 10995116277760, + "SIZE_S": "10995116277760", + "SIZE_HUMAN": "10.00 TB", + "TYPE": "SCSI", + "DESCRIPTION": " INTEL SSDSA2BW16", + "PRODUCT": "", + "VENDOR": "" + } + ], + "BASE": { + "DESCRIPTION": "Rack Mount Chassis", + "PRODUCT": "PowerEdge R620 (SKU=NotProvided;ModelName=PowerEdge R620)", + "VENDOR": "Dell Inc.", + "SERIAL": "" + } + }, + "CLASSIFICATION": { + "ID": 1305, + "TAG": "app", + "STATE": null, + "STATUS": "Incomplete", + "TYPE": "CONFIGURATION", + "CREATED": "2014-09-17T13:47:15", + "UPDATED": "2014-09-17T13:50:00", + "DELETED": null + }, + "LLDP": { + "INTERFACES": [ + { + "NAME": "eth0", + "CHASSIS": { + "NAME": "core01.dfw01", + "ID": { + "TYPE": "mac", + "VALUE": "78:19:f7:88:60:c0" + }, + "DESCRIPTION": "Juniper Networks, Inc. ex4500-40f , version 11.1S1 Build date: 2011-04-21 08:03:12 UTC " + }, + "PORT": { + "ID": { + "TYPE": "local", + "VALUE": "608" + }, + "DESCRIPTION": "ge-0/0/3.0" + }, + "VLANS": [ + { + "ID": 106, + "NAME": "DFW-LOGGING" + } + ] + }, + { + "NAME": "eth1", + "CHASSIS": { + "NAME": "core02.dfw01", + "ID": { + "TYPE": "mac", + "VALUE": "5c:5e:ab:68:a5:80" + }, + "DESCRIPTION": "Juniper Networks, Inc. ex4500-40f , version 11.1R1.10 Build date: 2011-03-16 08:17:03 UTC " + }, + "PORT": { + "ID": { + "TYPE": "local", + "VALUE": "608" + }, + "DESCRIPTION": "ge-0/0/3.0" + }, + "VLANS": [ + { + "ID": 106, + "NAME": "DFW-LOGGING" + } + ] + } + ] + }, + "IPMI": { + "ASSET_ID": 12, + "ASSET_TAG": "tag31", + "IPMI_USERNAME": "root", + "IPMI_PASSWORD": "V4PjGWUlMbfVkcyB", + "IPMI_GATEWAY": "172.16.32.1", + "IPMI_ADDRESS": "172.16.32.30", + "IPMI_NETMASK": "255.255.240.0", + "ID": 12 + }, + "ADDRESSES": [], + "POWER": [], + "ATTRIBS": { + "0": { + "DISK_STORAGE_TOTAL": "410101235712", + "BASE_SERIAL": "", + "CHASSIS_TAG": "Testing this" + } + } + } + ] + } +} diff --git a/collins/commands/format.go b/collins/commands/format.go index 639123e..558fb29 100644 --- a/collins/commands/format.go +++ b/collins/commands/format.go @@ -26,11 +26,11 @@ func formatAssets(format string, separator string, showHeaders bool, url string, } } -func emptyOrValue(sliceSize int, fn func() string) string { +func emptyOrValue(sliceSize int, def string, fn func() string) string { if sliceSize > 0 { return fn() } else { - return "" + return def } } @@ -65,7 +65,7 @@ func fieldToAssetStruct(field string, asset collins.Asset) string { case "ipmi_username": return asset.IPMI.Username case "ip_address": - return emptyOrValue(len(asset.Addresses), func() string { + return emptyOrValue(len(asset.Addresses), "", func() string { ips := []string{} for _, address := range asset.Addresses { ips = append(ips, address.Address) @@ -73,47 +73,47 @@ func fieldToAssetStruct(field string, asset collins.Asset) string { return strings.Join(ips, ",") }) case "cpu_cores": - return emptyOrValue(len(asset.CPUs), func() string { + return emptyOrValue(len(asset.CPUs), "0", func() string { return strconv.Itoa(asset.CPUs[0].Cores * len(asset.CPUs)) }) case "gpu_count": - return emptyOrValue(len(asset.GPUs), func() string { + return emptyOrValue(len(asset.GPUs), "0", func() string { return strconv.Itoa(len(asset.GPUs)) }) case "cpu_threads": - return emptyOrValue(len(asset.CPUs), func() string { + return emptyOrValue(len(asset.CPUs), "0", func() string { return strconv.Itoa(asset.CPUs[0].Threads * len(asset.CPUs)) }) case "cpu_speed_ghz": - return emptyOrValue(len(asset.CPUs), func() string { + return emptyOrValue(len(asset.CPUs), "0", func() string { return strconv.FormatFloat(float64(asset.CPUs[0].SpeedGhz), 'f', 4, 32) }) case "cpu_description": - return emptyOrValue(len(asset.CPUs), func() string { + return emptyOrValue(len(asset.CPUs), "", func() string { return asset.CPUs[0].Description }) case "gpu_description": - return emptyOrValue(len(asset.GPUs), func() string { + return emptyOrValue(len(asset.GPUs), "", func() string { return asset.GPUs[0].Description }) case "cpu_product": - return emptyOrValue(len(asset.CPUs), func() string { + return emptyOrValue(len(asset.CPUs), "", func() string { return asset.CPUs[0].Product }) case "gpu_product": - return emptyOrValue(len(asset.GPUs), func() string { + return emptyOrValue(len(asset.GPUs), "", func() string { return asset.GPUs[0].Product }) case "cpu_vendor": - return emptyOrValue(len(asset.CPUs), func() string { + return emptyOrValue(len(asset.CPUs), "", func() string { return asset.CPUs[0].Vendor }) case "gpu_vendor": - return emptyOrValue(len(asset.GPUs), func() string { + return emptyOrValue(len(asset.GPUs), "", func() string { return asset.GPUs[0].Vendor }) case "memory_size_bytes": - return emptyOrValue(len(asset.Memory), func() string { + return emptyOrValue(len(asset.Memory), "0", func() string { bytes := 0 for _, v := range asset.Memory { bytes = bytes + v.Size @@ -121,7 +121,7 @@ func fieldToAssetStruct(field string, asset collins.Asset) string { return strconv.Itoa(bytes) }) case "memory_size_total": - return emptyOrValue(len(asset.Memory), func() string { + return emptyOrValue(len(asset.Memory), "0", func() string { var size float64 format := "" for _, v := range asset.Memory { @@ -133,15 +133,15 @@ func fieldToAssetStruct(field string, asset collins.Asset) string { return strconv.FormatFloat(size, 'f', 2, 64) + " " + format }) case "memory_description": - return emptyOrValue(len(asset.Memory), func() string { + return emptyOrValue(len(asset.Memory), "", func() string { return asset.Memory[0].Description }) case "memory_banks_total": - return emptyOrValue(len(asset.Memory), func() string { + return emptyOrValue(len(asset.Memory), "0", func() string { return strconv.Itoa(len(asset.Memory)) }) case "disk_storage_human": - return emptyOrValue(len(asset.Disks), func() string { + return emptyOrValue(len(asset.Disks), "0", func() string { var size float64 for _, v := range asset.Disks { size = size + float64(v.Size) @@ -150,7 +150,7 @@ func fieldToAssetStruct(field string, asset collins.Asset) string { return BytesToHumanSize(size) }) case "disk_types": - return emptyOrValue(len(asset.Disks), func() string { + return emptyOrValue(len(asset.Disks), "", func() string { disks := UniqueOrderedSet{} for _, v := range asset.Disks { disks = disks.Add(v.Description) diff --git a/collins/commands/query_test.go b/collins/commands/query_test.go index e7853c5..890c436 100644 --- a/collins/commands/query_test.go +++ b/collins/commands/query_test.go @@ -548,3 +548,37 @@ func TestQueryCLIGetGPU(t *testing.T) { } }, []string{"cmd", "query", "-a", "gpu_vendor:nvidia", "-x", "gpu_product"}) } + +// https://github.com/michaeljs1990/collins-go-cli/issues/50 +func TestQueryIssue50(t *testing.T) { + client := setup() + monkey.Patch(getCollinsClient, func(c *cli.Context) *collins.Client { + return client + }) + defer teardown() + + SetupGET(201, "/api/assets", "assets/TestQueryIssue50.json", t) + + queryContext(func(ctx *cli.Context) { + c, o, w := captureStdout() + err := queryRunCommand(ctx) + result := returnStdout(c, o, w) + if err != nil { + t.Error(err.Error()) + } + + rows := map[int][]string{ + 0: []string{"tag30", "", "", "New", "", "", "", "0"}, + 1: []string{"tag31", "", "", "New", "", "", "", "80 TB"}, + } + + for i, line := range strings.Split(result, "\n") { + parts := strings.Split(line, "\t") + for idx, value := range parts { + if strings.TrimSpace(value) != rows[i][idx] { + t.Error("Expected ", value, " got ", rows[i][idx]) + } + } + } + }, []string{"cmd", "query", "-x", "disk_storage_human"}) +} diff --git a/collins/commands/util.go b/collins/commands/util.go index 335cd7c..902d166 100644 --- a/collins/commands/util.go +++ b/collins/commands/util.go @@ -54,13 +54,25 @@ func Round(val float64, roundOn float64, places int) (newVal float64) { // BytesToHumanSize takes an int and treats it as if it was bytes // converting it to the largest human readable size. func BytesToHumanSize(size float64) string { - suffix := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + suffix := []string{ + "B", "KB", "MB", + "GB", "TB", "PB", + "EB", "ZB", "YB", + } base := math.Log(size) / math.Log(1024) getSize := Round(math.Pow(1024, base-math.Floor(base)), .5, 2) - getSuffix := suffix[int(math.Floor(base))] - return strconv.FormatFloat(getSize, 'f', -1, 64) + " " + string(getSuffix) + var getSuffix string + if int(math.Floor(base)) > len(suffix) { + // Wow you have more than a YB of storage/memory good for you + // your asset likely is messed up :P + getSuffix = "Unknown" + } else { + getSuffix = suffix[int(math.Floor(base))] + } + + return strconv.FormatFloat(getSize, 'f', -1, 64) + " " + string(getSuffix) } func getCollinsClient(c *cli.Context) *collins.Client { @@ -74,7 +86,7 @@ func getCollinsClient(c *cli.Context) *collins.Client { if collins.Password == "" { fmt.Print("Enter Password: ") bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) - fmt.Println("") + fmt.Println("") if err != nil { fmt.Println("error reading password from terminal") logAndDie(err.Error()) diff --git a/collins/commands/util_test.go b/collins/commands/util_test.go index 6afe7c0..239cf6d 100644 --- a/collins/commands/util_test.go +++ b/collins/commands/util_test.go @@ -53,4 +53,10 @@ func TestBytesToHumanSize(t *testing.T) { if out != "1.3 PB" { t.Error("Bytes to human was suppose to be 1.3 PB but was " + out) } + + // I don't want to support this use case + out = BytesToHumanSize(146163105792000000000000000000000) + if out != "115.3 Unknown" { + t.Error("Bytes to human was suppose to be 115.3 Unknown but was " + out) + } }