func printHostList(hostList []photon.Host, w io.Writer, c *cli.Context) error { if c.GlobalIsSet("non-interactive") { for _, host := range hostList { tag := strings.Trim(fmt.Sprint(host.Tags), "[]") scriptTag := strings.Replace(tag, " ", ",", -1) fmt.Printf("%s\t%s\t%s\t%s\n", host.ID, host.State, host.Address, scriptTag) } } else if c.GlobalString("output") != "" { utils.FormatObjects(hostList, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tState\tIP\tTags\n") for _, host := range hostList { fmt.Fprintf(w, "%s\t%v\t%s\t%s\n", host.ID, host.State, host.Address, strings.Trim(fmt.Sprint(host.Tags), "[]")) } err := w.Flush() if err != nil { return err } fmt.Printf("\nTotal: %d\n", len(hostList)) } return nil }
func printClusterList(clusterList []photon.Cluster, w io.Writer, c *cli.Context, summaryView bool) error { stateCount := make(map[string]int) for _, cluster := range clusterList { stateCount[cluster.State]++ } if c.GlobalIsSet("non-interactive") { if !summaryView { for _, cluster := range clusterList { fmt.Printf("%s\t%s\t%s\t%s\t%d\n", cluster.ID, cluster.Name, cluster.Type, cluster.State, cluster.WorkerCount) } } } else if c.GlobalString("output") != "" { utils.FormatObjects(clusterList, w, c) } else { if !summaryView { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tType\tState\tWorker Count\n") for _, cluster := range clusterList { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\n", cluster.ID, cluster.Name, cluster.Type, cluster.State, cluster.WorkerCount) } err := w.Flush() if err != nil { return err } } fmt.Printf("\nTotal: %d\n", len(clusterList)) for key, value := range stateCount { fmt.Printf("%s: %d\n", key, value) } } return nil }
// Retrieves a list of projects, returns an error if one occurred func listProjects(c *cli.Context, w io.Writer) error { err := checkArgCount(c, 0) if err != nil { return err } tenantName := c.String("tenant") client.Esxclient, err = client.GetClient(c) if err != nil { return err } tenant, err := verifyTenant(tenantName) if err != nil { return err } projects, err := client.Esxclient.Tenants.GetProjects(tenant.ID, nil) if err != nil { return err } if c.GlobalIsSet("non-interactive") { for _, t := range projects.Items { limits := quotaLineItemListToString(t.ResourceTicket.Limits) usage := quotaLineItemListToString(t.ResourceTicket.Usage) fmt.Printf("%s\t%s\t%s\t%s\n", t.ID, t.Name, limits, usage) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(projects.Items, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tLimit\tUsage\n") for _, t := range projects.Items { rt := t.ResourceTicket for i := 0; i < len(rt.Limits); i++ { if i == 0 { fmt.Fprintf(w, "%s\t%s\t%s %g %s\t%s %g %s\n", t.ID, t.Name, rt.Limits[i].Key, rt.Limits[i].Value, rt.Limits[i].Unit, rt.Usage[i].Key, rt.Usage[i].Value, rt.Usage[i].Unit) } else { fmt.Fprintf(w, "\t\t%s %g %s\t%s %g %s\n", rt.Limits[i].Key, rt.Limits[i].Value, rt.Limits[i].Unit, rt.Usage[i].Key, rt.Usage[i].Value, rt.Usage[i].Unit) } } for i := len(rt.Limits); i < len(rt.Usage); i++ { fmt.Fprintf(w, "\t\t\t%s %g %s\n", rt.Usage[i].Key, rt.Usage[i].Value, rt.Usage[i].Unit) } } err := w.Flush() if err != nil { return err } fmt.Printf("\nTotal projects: %d\n", len(projects.Items)) } return nil }
// Show project info with the specified project id, returns an error if one occurred func showProject(c *cli.Context, w io.Writer) error { err := checkArgCount(c, 1) if err != nil { return err } id := c.Args().First() client.Esxclient, err = client.GetClient(c) if err != nil { return err } project, err := client.Esxclient.Projects.Get(id) if err != nil { return err } if c.GlobalIsSet("non-interactive") { securityGroups := []string{} for _, s := range project.SecurityGroups { securityGroups = append(securityGroups, fmt.Sprintf("%s:%t", s.Name, s.Inherited)) } scriptSecurityGroups := strings.Join(securityGroups, ",") limits := quotaLineItemListToString(project.ResourceTicket.Limits) usages := quotaLineItemListToString(project.ResourceTicket.Usage) fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", project.ID, project.Name, project.ResourceTicket.TenantTicketID, project.ResourceTicket.TenantTicketName, limits, usages, scriptSecurityGroups) } else if utils.NeedsFormatting(c) { utils.FormatObject(project, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "Project ID: %s\n", project.ID) fmt.Fprintf(w, " Name: %s\n", project.Name) fmt.Fprintf(w, " TenantTicketID: %s\n", project.ResourceTicket.TenantTicketID) fmt.Fprintf(w, " TenantTicketName: %s\n", project.ResourceTicket.TenantTicketName) fmt.Fprintf(w, " Limits:\n") for _, l := range project.ResourceTicket.Limits { fmt.Fprintf(w, " %s\t%g\t%s\n", l.Key, l.Value, l.Unit) } fmt.Fprintf(w, " Usage:\n") for _, u := range project.ResourceTicket.Usage { fmt.Fprintf(w, " %s\t%g\t%s\n", u.Key, u.Value, u.Unit) } if len(project.SecurityGroups) != 0 { fmt.Fprintf(w, " SecurityGroups:\n") for _, s := range project.SecurityGroups { fmt.Fprintf(w, " %s\t%t\n", s.Name, s.Inherited) } } err = w.Flush() if err != nil { return err } } return nil }
func (c *CommandLine) writeColumns(response *client.Response, w io.Writer) { for _, result := range response.Results { // Create a tabbed writer for each result a they won't always line up w := new(tabwriter.Writer) w.Init(os.Stdout, 0, 8, 1, '\t', 0) csv := c.formatResults(result, "\t") for _, r := range csv { fmt.Fprintln(w, r) } w.Flush() } }
// Sends a show resource-ticket task to client based on the cli.Context // Returns an error if one occurred func showResourceTicket(c *cli.Context, w io.Writer) error { err := checkArgNum(c.Args(), 1, "resource-ticket show <name> [<options>]") if err != nil { return err } name := c.Args().First() tenantName := c.String("tenant") client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c)) if err != nil { return err } tenant, err := verifyTenant(tenantName) if err != nil { return err } rt, err := findResourceTicket(tenant.ID, name) if err != nil { return err } if c.GlobalIsSet("non-interactive") { usage := quotaLineItemListToString(rt.Usage) limits := quotaLineItemListToString(rt.Limits) fmt.Printf("%s\t%s\t%s\t%s\n", rt.Name, rt.ID, limits, usage) } else if utils.NeedsFormatting(c) { utils.FormatObject(rt, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tLimit\tUsage\n") for i := 0; i < len(rt.Limits); i++ { if i == 0 { fmt.Fprintf(w, "%s\t%s\t%s %g %s\t%s %g %s\n", rt.ID, rt.Name, rt.Limits[i].Key, rt.Limits[i].Value, rt.Limits[i].Unit, rt.Usage[i].Key, rt.Usage[i].Value, rt.Usage[i].Unit) } else { fmt.Fprintf(w, "\t\t%s %g %s\t%s %g %s\n", rt.Limits[i].Key, rt.Limits[i].Value, rt.Limits[i].Unit, rt.Usage[i].Key, rt.Usage[i].Value, rt.Usage[i].Unit) } } err := w.Flush() if err != nil { return err } } return nil }
func printClusterVMs(vms []photon.VM, w io.Writer, c *cli.Context) (err error) { clusterVMs := []ClusterVM{} for _, vm := range vms { ipAddr := "-" networks, err := getVMNetworks(vm.ID, c) if err != nil { continue } for _, nt := range networks { network := nt.(map[string]interface{}) if val, ok := network["network"]; !ok || val == nil { continue } if val, ok := network["ipAddress"]; ok && val != nil { ipAddr = val.(string) break } } clusterVM := ClusterVM{ vm, ipAddr, } clusterVMs = append(clusterVMs, clusterVM) } if c.GlobalIsSet("non-interactive") { fmt.Printf("%d\n", len(vms)) for _, clusterVM := range clusterVMs { fmt.Printf("%s\t%s\t%s\n", clusterVM.ClusterVM.ID, clusterVM.ClusterVM.Name, clusterVM.IPAddress) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(clusterVMs, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "VM ID\tVM Name\tVM IP\n") for _, clusterVM := range clusterVMs { fmt.Fprintf(w, "%s\t%s\t%s\n", clusterVM.ClusterVM.ID, clusterVM.ClusterVM.Name, clusterVM.IPAddress) } err := w.Flush() if err != nil { return err } } return nil }
func (c *CommandLine) writeColumns(response *client.Response, w io.Writer) { for _, result := range response.Results { // Print out all messages first for _, m := range result.Messages { fmt.Fprintf(w, "%s: %s.\n", m.Level, m.Text) } // Create a tabbed writer for each result as they won't always line up w := new(tabwriter.Writer) w.Init(os.Stdout, 0, 8, 1, '\t', 0) csv := c.formatResults(result, "\t") for _, r := range csv { fmt.Fprintln(w, r) } w.Flush() } }
// Retrieves a list of flavors func listFlavors(c *cli.Context, w io.Writer) error { err := checkArgNum(c.Args(), 0, "flavor list [<options>]") if err != nil { return err } client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c)) if err != nil { return err } name := c.String("name") kind := c.String("kind") options := &photon.FlavorGetOptions{ Name: name, Kind: kind, } flavors, err := client.Esxclient.Flavors.GetAll(options) if err != nil { return err } if c.GlobalIsSet("non-interactive") { for _, flavor := range flavors.Items { costs := quotaLineItemListToString(flavor.Cost) fmt.Printf("%s\t%s\t%s\t%s\n", flavor.ID, flavor.Name, flavor.Kind, costs) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(flavors.Items, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tKind\tCost\n") for _, flavor := range flavors.Items { printQuotaList(w, flavor.Cost, flavor.ID, flavor.Name, flavor.Kind) } err = w.Flush() if err != nil { return err } fmt.Printf("Total: %d\n", len(flavors.Items)) } return nil }
// Retrieves a list of resource tickets, returns an error if one occurred func listResourceTickets(c *cli.Context, w io.Writer) error { err := checkArgNum(c.Args(), 0, "resource-ticket list [<options>]") if err != nil { return err } tenantName := c.String("tenant") client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c)) if err != nil { return err } tenant, err := verifyTenant(tenantName) if err != nil { return err } tickets, err := client.Esxclient.Tenants.GetResourceTickets(tenant.ID, nil) if err != nil { return err } if c.GlobalIsSet("non-interactive") { for _, t := range tickets.Items { limits := quotaLineItemListToString(t.Limits) fmt.Printf("%s\t%s\t%s\n", t.ID, t.Name, limits) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(tickets.Items, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tLimit\n") for _, t := range tickets.Items { printQuotaList(w, t.Limits, t.ID, t.Name) } err := w.Flush() if err != nil { return err } fmt.Printf("\nTotal resource tickets: %d\n", len(tickets.Items)) } return nil }
// Lists all images func listImages(c *cli.Context, w io.Writer) error { err := checkArgCount(c, 0) if err != nil { return err } client.Esxclient, err = client.GetClient(c) if err != nil { return err } name := c.String("name") options := &photon.ImageGetOptions{ Name: name, } images, err := client.Esxclient.Images.GetAll(options) if err != nil { return err } if c.GlobalIsSet("non-interactive") { for _, image := range images.Items { fmt.Printf("%s\t%s\t%s\t%d\t%s\t%s\t%s\n", image.ID, image.Name, image.State, image.Size, image.ReplicationType, image.ReplicationProgress, image.SeedingProgress) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(images.Items, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tState\tSize(Byte)\tReplication_type\tReplicationProgress\tSeedingProgress\n") for _, image := range images.Items { fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%s\t%s\t%s\n", image.ID, image.Name, image.State, image.Size, image.ReplicationType, image.ReplicationProgress, image.SeedingProgress) } err = w.Flush() if err != nil { return err } fmt.Printf("\nTotal: %d\n", len(images.Items)) } return nil }
func listPhysicalNetworks(c *cli.Context, w io.Writer) error { err := checkArgCount(c, 0) if err != nil { return err } client.Esxclient, err = client.GetClient(c) if err != nil { return err } name := c.String("name") options := &photon.SubnetGetOptions{ Name: name, } networks, err := client.Esxclient.Subnets.GetAll(options) if err != nil { return err } if c.GlobalIsSet("non-interactive") { for _, network := range networks.Items { fmt.Printf("%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State, network.PortGroups, network.Description) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(networks.Items, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tState\tPortGroups\tDescription\tIsDefault\n") for _, network := range networks.Items { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%t\n", network.ID, network.Name, network.State, network.PortGroups, network.Description, network.IsDefault) } err = w.Flush() if err != nil { return err } fmt.Printf("Total: %d\n", len(networks.Items)) } return nil }
// Retrieves a list of deployments func listDeployments(c *cli.Context, w io.Writer) error { err := checkArgCount(c, 0) if err != nil { return err } client.Esxclient, err = client.GetClient(c) if err != nil { return err } deployments, err := client.Esxclient.Deployments.GetAll() if err != nil { return err } if utils.NeedsFormatting(c) { utils.FormatObjects(deployments, w, c) } else if c.GlobalIsSet("non-interactive") { for _, deployment := range deployments.Items { fmt.Printf("%s\n", deployment.ID) } } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\n") for _, deployment := range deployments.Items { fmt.Fprintf(w, "%s\n", deployment.ID) } err = w.Flush() if err != nil { return err } fmt.Printf("\nTotal: %d\n", len(deployments.Items)) } return nil }
// Retrieves a list of tenants, returns an error if one occurred func listTenants(c *cli.Context, w io.Writer) error { err := checkArgNum(c.Args(), 0, "tenant list") if err != nil { return err } client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c)) if err != nil { return err } tenants, err := client.Esxclient.Tenants.GetAll() if err != nil { return err } if c.GlobalIsSet("non-interactive") { for _, tenant := range tenants.Items { fmt.Printf("%s\t%s\n", tenant.ID, tenant.Name) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(tenants.Items, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\n") for _, tenant := range tenants.Items { fmt.Fprintf(w, "%s\t%s\n", tenant.ID, tenant.Name) } err = w.Flush() if err != nil { return err } fmt.Printf("\nTotal: %d\n", len(tenants.Items)) } return nil }
// Retrieves a list of availability zones, returns an error if one occurred func listAvailabilityZones(c *cli.Context, w io.Writer) error { err := checkArgCount(c, 0) if err != nil { return err } client.Esxclient, err = client.GetClient(c) if err != nil { return err } zones, err := client.Esxclient.AvailabilityZones.GetAll() if err != nil { return err } if c.GlobalIsSet("non-interactive") { for _, zone := range zones.Items { fmt.Printf("%s\t%s\n", zone.ID, zone.Name) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(zones.Items, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\n") for _, zone := range zones.Items { fmt.Fprintf(w, "%s\t%s\n", zone.ID, zone.Name) } err = w.Flush() if err != nil { return err } fmt.Printf("\nTotal: %d\n", len(zones.Items)) } return nil }
// StdCopy is a modified version of io.Copy. // // StdCopy will demultiplex `src`, assuming that it contains two streams, // previously multiplexed together using a StdWriter instance. // As it reads from `src`, StdCopy will write to `dstout` and `dsterr`. // // StdCopy will read until it hits EOF on `src`. It will then return a nil error. // In other words: if `err` is non nil, it indicates a real underlying error. // // `written` will hold the total number of bytes written to `dstout` and `dsterr`. func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) { var ( buf = make([]byte, startingBufLen) bufLen = len(buf) nr, nw int er, ew error out io.Writer frameSize int ) for { // Make sure we have at least a full header for nr < stdWriterPrefixLen { var nr2 int nr2, er = src.Read(buf[nr:]) nr += nr2 if er == io.EOF { if nr < stdWriterPrefixLen { logrus.Debugf("Corrupted prefix: %v", buf[:nr]) return written, nil } break } if er != nil { logrus.Debugf("Error reading header: %s", er) return 0, er } } // Check the first byte to know where to write switch StdType(buf[stdWriterFdIndex]) { case Stdin: fallthrough case Stdout: // Write on stdout out = dstout case Stderr: // Write on stderr out = dsterr default: logrus.Debugf("Error selecting output fd: (%d)", buf[stdWriterFdIndex]) return 0, fmt.Errorf("Unrecognized input header: %d", buf[stdWriterFdIndex]) } // Retrieve the size of the frame frameSize = int(binary.BigEndian.Uint32(buf[stdWriterSizeIndex : stdWriterSizeIndex+4])) logrus.Debugf("framesize: %d", frameSize) // Check if the buffer is big enough to read the frame. // Extend it if necessary. if frameSize+stdWriterPrefixLen > bufLen { logrus.Debugf("Extending buffer cap by %d (was %d)", frameSize+stdWriterPrefixLen-bufLen+1, len(buf)) buf = append(buf, make([]byte, frameSize+stdWriterPrefixLen-bufLen+1)...) bufLen = len(buf) } // While the amount of bytes read is less than the size of the frame + header, we keep reading for nr < frameSize+stdWriterPrefixLen { var nr2 int nr2, er = src.Read(buf[nr:]) nr += nr2 if er == io.EOF { if nr < frameSize+stdWriterPrefixLen { logrus.Debugf("Corrupted frame: %v", buf[stdWriterPrefixLen:nr]) return written, nil } break } if er != nil { logrus.Debugf("Error reading frame: %s", er) return 0, er } } // Write the retrieved frame (without header) nw, ew = out.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen]) if ew != nil { logrus.Debugf("Error writing frame: %s", ew) return 0, ew } // If the frame has not been fully written: error if nw != frameSize { logrus.Debugf("Error Short Write: (%d on %d)", nw, frameSize) return 0, io.ErrShortWrite } written += int64(nw) if out, ok := out.(*bufio.Writer); ok { logrus.Errorf("flushing a heretofore previously unflushed buffer") out.Flush() } // Move the rest of the buffer to the beginning copy(buf, buf[frameSize+stdWriterPrefixLen:]) // Move the index nr -= frameSize + stdWriterPrefixLen } }
// flushes data for writers that implement HTTP.Flusher. func flushWriter(w io.Writer) { if w, ok := w.(http.Flusher); ok { w.Flush() } }
func listVirtualNetworks(c *cli.Context, w io.Writer) error { err := checkArgCount(c, 0) if err != nil { return err } client.Esxclient, err = client.GetClient(c) if err != nil { return err } name := c.String("name") options := &photon.VirtualSubnetGetOptions{ Name: name, } projectId := c.String("projectId") if len(projectId) == 0 { tenant, err := verifyTenant("") if err != nil { return err } project, err := verifyProject(tenant.ID, "") if err != nil { return err } projectId = project.ID } networks, err := client.Esxclient.VirtualSubnets.GetAll(projectId, options) if err != nil { return err } if c.GlobalIsSet("non-interactive") { for _, network := range networks.Items { fmt.Printf("%s\t%s\t%s\t%s\t%s\t%t\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State, network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic, network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList) } } else if utils.NeedsFormatting(c) { utils.FormatObjects(networks.Items, w, c) } else { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tState\tDescriptions\tRoutingType\tIsDefault\tCIDR\tLowDynamicIP\tHighDynamicIP"+ "\tLowStaticIP\tHighStaticIP\tReservedIpList\n") for _, network := range networks.Items { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%t\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State, network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic, network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList) } err = w.Flush() if err != nil { return err } fmt.Printf("Total: %d\n", len(networks.Items)) } return nil }
// Retrieves a list of disk, returns an error if one occurred func listDisks(c *cli.Context, w io.Writer) error { err := checkArgCount(c, 0) if err != nil { return err } tenantName := c.String("tenant") projectName := c.String("project") summaryView := c.IsSet("summary") name := c.String("name") options := &photon.DiskGetOptions{ Name: name, } client.Esxclient, err = client.GetClient(c) if err != nil { return err } tenant, err := verifyTenant(tenantName) if err != nil { return err } project, err := verifyProject(tenant.ID, projectName) if err != nil { return err } diskList, err := client.Esxclient.Projects.GetDisks(project.ID, options) if err != nil { return err } if utils.NeedsFormatting(c) { utils.FormatObjects(diskList, w, c) return nil } stateCount := make(map[string]int) for _, disk := range diskList.Items { stateCount[disk.State]++ } if c.GlobalIsSet("non-interactive") { if !summaryView { for _, disk := range diskList.Items { fmt.Printf("%s\t%s\t%s\n", disk.ID, disk.Name, disk.State) } } } else if !utils.NeedsFormatting(c) { if !summaryView { w := new(tabwriter.Writer) w.Init(os.Stdout, 4, 4, 2, ' ', 0) fmt.Fprintf(w, "ID\tName\tState\n") for _, disk := range diskList.Items { fmt.Fprintf(w, "%s\t%s\t%s\n", disk.ID, disk.Name, disk.State) } err := w.Flush() if err != nil { return err } } fmt.Printf("\nTotal: %d\n", len(diskList.Items)) for key, value := range stateCount { fmt.Printf("%s: %d\n", key, value) } } return nil }