1package server
2
3import (
4	"strconv"
5	"strings"
6
7	"github.com/hetznercloud/cli/internal/cmd/output"
8	"github.com/hetznercloud/cli/internal/cmd/util"
9	"github.com/hetznercloud/cli/internal/state"
10	"github.com/hetznercloud/hcloud-go/hcloud/schema"
11
12	"github.com/hetznercloud/hcloud-go/hcloud"
13	"github.com/spf13/cobra"
14)
15
16var listTableOutput *output.Table
17
18func init() {
19	listTableOutput = describeListTableOutput(nil)
20}
21
22func newListCommand(cli *state.State) *cobra.Command {
23	cmd := &cobra.Command{
24		Use:   "list [FLAGS]",
25		Short: "List servers",
26		Long: util.ListLongDescription(
27			"Displays a list of servers.",
28			listTableOutput.Columns(),
29		),
30		TraverseChildren:      true,
31		DisableFlagsInUseLine: true,
32		PreRunE:               cli.EnsureToken,
33		RunE:                  cli.Wrap(runList),
34	}
35	output.AddFlag(cmd, output.OptionNoHeader(), output.OptionColumns(listTableOutput.Columns()), output.OptionJSON())
36	cmd.Flags().StringP("selector", "l", "", "Selector to filter by labels")
37	return cmd
38}
39
40func runList(cli *state.State, cmd *cobra.Command, args []string) error {
41	outOpts := output.FlagsForCommand(cmd)
42
43	labelSelector, _ := cmd.Flags().GetString("selector")
44	opts := hcloud.ServerListOpts{
45		ListOpts: hcloud.ListOpts{
46			LabelSelector: labelSelector,
47			PerPage:       50,
48		},
49	}
50	servers, err := cli.Client().Server.AllWithOpts(cli.Context, opts)
51	if err != nil {
52		return err
53	}
54
55	if outOpts.IsSet("json") {
56		var serversSchema []schema.Server
57		for _, server := range servers {
58			serverSchema := schema.Server{
59				ID:         server.ID,
60				Name:       server.Name,
61				Status:     string(server.Status),
62				Created:    server.Created,
63				Datacenter: util.DatacenterToSchema(*server.Datacenter),
64				ServerType: util.ServerTypeToSchema(*server.ServerType),
65				PublicNet: schema.ServerPublicNet{
66					IPv4: schema.ServerPublicNetIPv4{
67						IP:      server.PublicNet.IPv4.IP.String(),
68						Blocked: server.PublicNet.IPv4.Blocked,
69						DNSPtr:  server.PublicNet.IPv4.DNSPtr,
70					},
71					IPv6: schema.ServerPublicNetIPv6{
72						IP:      server.PublicNet.IPv6.IP.String(),
73						Blocked: server.PublicNet.IPv6.Blocked,
74					},
75				},
76				RescueEnabled:   server.RescueEnabled,
77				BackupWindow:    hcloud.String(server.BackupWindow),
78				OutgoingTraffic: &server.OutgoingTraffic,
79				IngoingTraffic:  &server.IngoingTraffic,
80				IncludedTraffic: server.IncludedTraffic,
81				Protection: schema.ServerProtection{
82					Delete:  server.Protection.Delete,
83					Rebuild: server.Protection.Rebuild,
84				},
85				Labels:          server.Labels,
86				PrimaryDiskSize: server.PrimaryDiskSize,
87			}
88			if server.Image != nil {
89				serverImage := util.ImageToSchema(*server.Image)
90				serverSchema.Image = &serverImage
91			}
92			if server.ISO != nil {
93				serverISO := util.ISOToSchema(*server.ISO)
94				serverSchema.ISO = &serverISO
95			}
96			for ip, dnsPTR := range server.PublicNet.IPv6.DNSPtr {
97				serverSchema.PublicNet.IPv6.DNSPtr = append(serverSchema.PublicNet.IPv6.DNSPtr, schema.ServerPublicNetIPv6DNSPtr{
98					IP:     ip,
99					DNSPtr: dnsPTR,
100				})
101			}
102			for _, floatingIP := range server.PublicNet.FloatingIPs {
103				serverSchema.PublicNet.FloatingIPs = append(serverSchema.PublicNet.FloatingIPs, floatingIP.ID)
104			}
105			for _, volume := range server.Volumes {
106				serverSchema.Volumes = append(serverSchema.Volumes, volume.ID)
107			}
108			for _, privateNet := range server.PrivateNet {
109				privateNetSchema := schema.ServerPrivateNet{
110					Network:    privateNet.Network.ID,
111					IP:         privateNet.IP.String(),
112					MACAddress: privateNet.MACAddress,
113				}
114				for _, aliasIP := range privateNet.Aliases {
115					privateNetSchema.AliasIPs = append(privateNetSchema.AliasIPs, aliasIP.String())
116				}
117				serverSchema.PrivateNet = append(serverSchema.PrivateNet, privateNetSchema)
118			}
119			serversSchema = append(serversSchema, serverSchema)
120		}
121		return util.DescribeJSON(serversSchema)
122	}
123
124	cols := []string{"id", "name", "status", "ipv4", "ipv6", "datacenter"}
125	if outOpts.IsSet("columns") {
126		cols = outOpts["columns"]
127	}
128
129	tw := describeListTableOutput(cli)
130	if err = tw.ValidateColumns(cols); err != nil {
131		return err
132	}
133
134	if !outOpts.IsSet("noheader") {
135		tw.WriteHeader(cols)
136	}
137	for _, server := range servers {
138		tw.Write(cols, server)
139	}
140	tw.Flush()
141	return nil
142}
143
144func describeListTableOutput(cli *state.State) *output.Table {
145	return output.NewTable().
146		AddAllowedFields(hcloud.Server{}).
147		AddFieldFn("ipv4", output.FieldFn(func(obj interface{}) string {
148			server := obj.(*hcloud.Server)
149			return server.PublicNet.IPv4.IP.String()
150		})).
151		AddFieldFn("ipv6", output.FieldFn(func(obj interface{}) string {
152			server := obj.(*hcloud.Server)
153			return server.PublicNet.IPv6.Network.String()
154		})).
155		AddFieldFn("datacenter", output.FieldFn(func(obj interface{}) string {
156			server := obj.(*hcloud.Server)
157			return server.Datacenter.Name
158		})).
159		AddFieldFn("location", output.FieldFn(func(obj interface{}) string {
160			server := obj.(*hcloud.Server)
161			return server.Datacenter.Location.Name
162		})).
163		AddFieldFn("labels", output.FieldFn(func(obj interface{}) string {
164			server := obj.(*hcloud.Server)
165			return util.LabelsToString(server.Labels)
166		})).
167		AddFieldFn("type", output.FieldFn(func(obj interface{}) string {
168			server := obj.(*hcloud.Server)
169			return server.ServerType.Name
170		})).
171		AddFieldFn("volumes", output.FieldFn(func(obj interface{}) string {
172			server := obj.(*hcloud.Server)
173			var volumes []string
174			for _, volume := range server.Volumes {
175				volumeID := strconv.Itoa(volume.ID)
176				volumes = append(volumes, volumeID)
177			}
178			return strings.Join(volumes, ", ")
179		})).
180		AddFieldFn("private_net", output.FieldFn(func(obj interface{}) string {
181			server := obj.(*hcloud.Server)
182			var networks []string
183			if cli != nil {
184				for _, network := range server.PrivateNet {
185					networks = append(networks, cli.NetworkName(network.Network.ID))
186				}
187			}
188			return util.NA(strings.Join(networks, ", "))
189		})).
190		AddFieldFn("protection", output.FieldFn(func(obj interface{}) string {
191			server := obj.(*hcloud.Server)
192			var protection []string
193			if server.Protection.Delete {
194				protection = append(protection, "delete")
195			}
196			if server.Protection.Rebuild {
197				protection = append(protection, "rebuild")
198			}
199			return strings.Join(protection, ", ")
200		})).
201		AddFieldFn("created", output.FieldFn(func(obj interface{}) string {
202			server := obj.(*hcloud.Server)
203			return util.Datetime(server.Created)
204		}))
205}
206