1// Copyright 2012-present Oliver Eilhard. All rights reserved.
2// Use of this source code is governed by a MIT-license.
3// See http://olivere.mit-license.org/license.txt for details.
4
5package elastic
6
7import (
8	"context"
9	"fmt"
10	"net/http"
11	"net/url"
12	"strings"
13	"time"
14
15	"github.com/olivere/elastic/v7/uritemplates"
16)
17
18// NodesInfoService allows to retrieve one or more or all of the
19// cluster nodes information.
20// It is documented at https://www.elastic.co/guide/en/elasticsearch/reference/7.0/cluster-nodes-info.html.
21type NodesInfoService struct {
22	client *Client
23
24	pretty     *bool       // pretty format the returned JSON response
25	human      *bool       // return human readable values for statistics
26	errorTrace *bool       // include the stack trace of returned errors
27	filterPath []string    // list of filters used to reduce the response
28	headers    http.Header // custom request-level HTTP headers
29
30	nodeId       []string
31	metric       []string
32	flatSettings *bool
33}
34
35// NewNodesInfoService creates a new NodesInfoService.
36func NewNodesInfoService(client *Client) *NodesInfoService {
37	return &NodesInfoService{
38		client: client,
39	}
40}
41
42// Pretty tells Elasticsearch whether to return a formatted JSON response.
43func (s *NodesInfoService) Pretty(pretty bool) *NodesInfoService {
44	s.pretty = &pretty
45	return s
46}
47
48// Human specifies whether human readable values should be returned in
49// the JSON response, e.g. "7.5mb".
50func (s *NodesInfoService) Human(human bool) *NodesInfoService {
51	s.human = &human
52	return s
53}
54
55// ErrorTrace specifies whether to include the stack trace of returned errors.
56func (s *NodesInfoService) ErrorTrace(errorTrace bool) *NodesInfoService {
57	s.errorTrace = &errorTrace
58	return s
59}
60
61// FilterPath specifies a list of filters used to reduce the response.
62func (s *NodesInfoService) FilterPath(filterPath ...string) *NodesInfoService {
63	s.filterPath = filterPath
64	return s
65}
66
67// Header adds a header to the request.
68func (s *NodesInfoService) Header(name string, value string) *NodesInfoService {
69	if s.headers == nil {
70		s.headers = http.Header{}
71	}
72	s.headers.Add(name, value)
73	return s
74}
75
76// Headers specifies the headers of the request.
77func (s *NodesInfoService) Headers(headers http.Header) *NodesInfoService {
78	s.headers = headers
79	return s
80}
81
82// NodeId is a list of node IDs or names to limit the returned information.
83// Use "_local" to return information from the node you're connecting to,
84// leave empty to get information from all nodes.
85func (s *NodesInfoService) NodeId(nodeId ...string) *NodesInfoService {
86	s.nodeId = append(s.nodeId, nodeId...)
87	return s
88}
89
90// Metric is a list of metrics you wish returned. Leave empty to return all.
91// Valid metrics are: settings, os, process, jvm, thread_pool, network,
92// transport, http, and plugins.
93func (s *NodesInfoService) Metric(metric ...string) *NodesInfoService {
94	s.metric = append(s.metric, metric...)
95	return s
96}
97
98// FlatSettings returns settings in flat format (default: false).
99func (s *NodesInfoService) FlatSettings(flatSettings bool) *NodesInfoService {
100	s.flatSettings = &flatSettings
101	return s
102}
103
104// buildURL builds the URL for the operation.
105func (s *NodesInfoService) buildURL() (string, url.Values, error) {
106	var nodeId, metric string
107
108	if len(s.nodeId) > 0 {
109		nodeId = strings.Join(s.nodeId, ",")
110	} else {
111		nodeId = "_all"
112	}
113
114	if len(s.metric) > 0 {
115		metric = strings.Join(s.metric, ",")
116	} else {
117		metric = "_all"
118	}
119
120	// Build URL
121	path, err := uritemplates.Expand("/_nodes/{node_id}/{metric}", map[string]string{
122		"node_id": nodeId,
123		"metric":  metric,
124	})
125	if err != nil {
126		return "", url.Values{}, err
127	}
128
129	// Add query string parameters
130	params := url.Values{}
131	if v := s.pretty; v != nil {
132		params.Set("pretty", fmt.Sprint(*v))
133	}
134	if v := s.human; v != nil {
135		params.Set("human", fmt.Sprint(*v))
136	}
137	if v := s.errorTrace; v != nil {
138		params.Set("error_trace", fmt.Sprint(*v))
139	}
140	if len(s.filterPath) > 0 {
141		params.Set("filter_path", strings.Join(s.filterPath, ","))
142	}
143	if s.flatSettings != nil {
144		params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
145	}
146	return path, params, nil
147}
148
149// Validate checks if the operation is valid.
150func (s *NodesInfoService) Validate() error {
151	return nil
152}
153
154// Do executes the operation.
155func (s *NodesInfoService) Do(ctx context.Context) (*NodesInfoResponse, error) {
156	// Check pre-conditions
157	if err := s.Validate(); err != nil {
158		return nil, err
159	}
160
161	// Get URL for request
162	path, params, err := s.buildURL()
163	if err != nil {
164		return nil, err
165	}
166
167	// Get HTTP response
168	res, err := s.client.PerformRequest(ctx, PerformRequestOptions{
169		Method:  "GET",
170		Path:    path,
171		Params:  params,
172		Headers: s.headers,
173	})
174	if err != nil {
175		return nil, err
176	}
177
178	// Return operation response
179	ret := new(NodesInfoResponse)
180	if err := s.client.decoder.Decode(res.Body, ret); err != nil {
181		return nil, err
182	}
183	return ret, nil
184}
185
186// NodesInfoResponse is the response of NodesInfoService.Do.
187type NodesInfoResponse struct {
188	ClusterName string                    `json:"cluster_name"`
189	Nodes       map[string]*NodesInfoNode `json:"nodes"`
190}
191
192// NodesInfoNode represents information about a node in the cluster.
193type NodesInfoNode struct {
194	// Name of the node, e.g. "Mister Fear"
195	Name string `json:"name"`
196	// TransportAddress, e.g. "127.0.0.1:9300"
197	TransportAddress string `json:"transport_address"`
198	// Host is the host name, e.g. "macbookair"
199	Host string `json:"host"`
200	// IP is the IP address, e.g. "192.168.1.2"
201	IP string `json:"ip"`
202	// Version is the Elasticsearch version running on the node, e.g. "1.4.3"
203	Version string `json:"version"`
204	// BuildHash is the Elasticsearch build bash, e.g. "36a29a7"
205	BuildHash string `json:"build_hash"`
206
207	// TotalIndexingBuffer represents the total heap allowed to be used to
208	// hold recently indexed documents before they must be written to disk.
209	TotalIndexingBuffer int64 `json:"total_indexing_buffer"` // e.g. 16gb
210	// TotalIndexingBufferInBytes is the same as TotalIndexingBuffer, but
211	// expressed in bytes.
212	TotalIndexingBufferInBytes string `json:"total_indexing_buffer_in_bytes"`
213
214	// Roles of the node, e.g. [master, ingest, data]
215	Roles []string `json:"roles"`
216
217	// Attributes of the node.
218	Attributes map[string]string `json:"attributes"`
219
220	// Settings of the node, e.g. paths and pidfile.
221	Settings map[string]interface{} `json:"settings"`
222
223	// OS information, e.g. CPU and memory.
224	OS *NodesInfoNodeOS `json:"os"`
225
226	// Process information, e.g. max file descriptors.
227	Process *NodesInfoNodeProcess `json:"process"`
228
229	// JVM information, e.g. VM version.
230	JVM *NodesInfoNodeJVM `json:"jvm"`
231
232	// ThreadPool information.
233	ThreadPool *NodesInfoNodeThreadPool `json:"thread_pool"`
234
235	// Network information.
236	Transport *NodesInfoNodeTransport `json:"transport"`
237
238	// HTTP information.
239	HTTP *NodesInfoNodeHTTP `json:"http"`
240
241	// Plugins information.
242	Plugins []*NodesInfoNodePlugin `json:"plugins"`
243
244	// Modules information.
245	Modules []*NodesInfoNodeModule `json:"modules"`
246
247	// Ingest information.
248	Ingest *NodesInfoNodeIngest `json:"ingest"`
249}
250
251// HasRole returns true if the node fulfills the given role.
252func (n *NodesInfoNode) HasRole(role string) bool {
253	for _, r := range n.Roles {
254		if r == role {
255			return true
256		}
257	}
258	return false
259}
260
261// IsMaster returns true if the node is a master node.
262func (n *NodesInfoNode) IsMaster() bool {
263	return n.HasRole("master")
264}
265
266// IsData returns true if the node is a data node.
267func (n *NodesInfoNode) IsData() bool {
268	return n.HasRole("data")
269}
270
271// IsIngest returns true if the node is an ingest node.
272func (n *NodesInfoNode) IsIngest() bool {
273	return n.HasRole("ingest")
274}
275
276// NodesInfoNodeOS represents OS-specific details about a node.
277type NodesInfoNodeOS struct {
278	RefreshInterval         string `json:"refresh_interval"`           // e.g. 1s
279	RefreshIntervalInMillis int    `json:"refresh_interval_in_millis"` // e.g. 1000
280	Name                    string `json:"name"`                       // e.g. Linux
281	Arch                    string `json:"arch"`                       // e.g. amd64
282	Version                 string `json:"version"`                    // e.g. 4.9.87-linuxkit-aufs
283	AvailableProcessors     int    `json:"available_processors"`       // e.g. 4
284	AllocatedProcessors     int    `json:"allocated_processors"`       // e.g. 4
285}
286
287// NodesInfoNodeProcess represents process-related information.
288type NodesInfoNodeProcess struct {
289	RefreshInterval         string `json:"refresh_interval"`           // e.g. 1s
290	RefreshIntervalInMillis int64  `json:"refresh_interval_in_millis"` // e.g. 1000
291	ID                      int    `json:"id"`                         // process id, e.g. 87079
292	Mlockall                bool   `json:"mlockall"`                   // e.g. false
293}
294
295// NodesInfoNodeJVM represents JVM-related information.
296type NodesInfoNodeJVM struct {
297	PID               int       `json:"pid"`                  // process id, e.g. 87079
298	Version           string    `json:"version"`              // e.g. "1.8.0_161"
299	VMName            string    `json:"vm_name"`              // e.g. "OpenJDK 64-Bit Server VM"
300	VMVersion         string    `json:"vm_version"`           // e.g. "25.161-b14"
301	VMVendor          string    `json:"vm_vendor"`            // e.g. "Oracle Corporation"
302	StartTime         time.Time `json:"start_time"`           // e.g. "2018-03-30T11:06:36.644Z"
303	StartTimeInMillis int64     `json:"start_time_in_millis"` // e.g. 1522407996644
304
305	// Mem information
306	Mem struct {
307		HeapInit           string `json:"heap_init"`              // e.g. "1gb"
308		HeapInitInBytes    int    `json:"heap_init_in_bytes"`     // e.g. 1073741824
309		HeapMax            string `json:"heap_max"`               // e.g. "1007.3mb"
310		HeapMaxInBytes     int    `json:"heap_max_in_bytes"`      // e.g. 1056309248
311		NonHeapInit        string `json:"non_heap_init"`          // e.g. "2.4mb"
312		NonHeapInitInBytes int    `json:"non_heap_init_in_bytes"` // e.g. 2555904
313		NonHeapMax         string `json:"non_heap_max"`           // e.g. "0b"
314		NonHeapMaxInBytes  int    `json:"non_heap_max_in_bytes"`  // e.g. 0
315		DirectMax          string `json:"direct_max"`             // e.g. "1007.3mb"
316		DirectMaxInBytes   int    `json:"direct_max_in_bytes"`    // e.g. 1056309248
317	} `json:"mem"`
318
319	GCCollectors []string `json:"gc_collectors"` // e.g. ["ParNew", "ConcurrentMarkSweep"]
320	MemoryPools  []string `json:"memory_pools"`  // e.g. ["Code Cache", "Metaspace", "Compressed Class Space", "Par Eden Space", "Par Survivor Space", "CMS Old Gen"]
321
322	// UsingCompressedOrdinaryObjectPointers should be a bool, but is a
323	// string in 6.2.3. We use an interface{} for now so that it won't break
324	// when this will be fixed in later versions of Elasticsearch.
325	UsingCompressedOrdinaryObjectPointers interface{} `json:"using_compressed_ordinary_object_pointers"`
326
327	InputArguments []string `json:"input_arguments"` // e.g. ["-Xms1g", "-Xmx1g" ...]
328}
329
330// NodesInfoNodeThreadPool represents information about the thread pool.
331type NodesInfoNodeThreadPool struct {
332	ForceMerge        *NodesInfoNodeThreadPoolSection `json:"force_merge"`
333	FetchShardStarted *NodesInfoNodeThreadPoolSection `json:"fetch_shard_started"`
334	Listener          *NodesInfoNodeThreadPoolSection `json:"listener"`
335	Index             *NodesInfoNodeThreadPoolSection `json:"index"`
336	Refresh           *NodesInfoNodeThreadPoolSection `json:"refresh"`
337	Generic           *NodesInfoNodeThreadPoolSection `json:"generic"`
338	Warmer            *NodesInfoNodeThreadPoolSection `json:"warmer"`
339	Search            *NodesInfoNodeThreadPoolSection `json:"search"`
340	Flush             *NodesInfoNodeThreadPoolSection `json:"flush"`
341	FetchShardStore   *NodesInfoNodeThreadPoolSection `json:"fetch_shard_store"`
342	Management        *NodesInfoNodeThreadPoolSection `json:"management"`
343	Get               *NodesInfoNodeThreadPoolSection `json:"get"`
344	Bulk              *NodesInfoNodeThreadPoolSection `json:"bulk"`
345	Snapshot          *NodesInfoNodeThreadPoolSection `json:"snapshot"`
346
347	Percolate *NodesInfoNodeThreadPoolSection `json:"percolate"` // check
348	Bench     *NodesInfoNodeThreadPoolSection `json:"bench"`     // check
349	Suggest   *NodesInfoNodeThreadPoolSection `json:"suggest"`   // deprecated
350	Optimize  *NodesInfoNodeThreadPoolSection `json:"optimize"`  // deprecated
351	Merge     *NodesInfoNodeThreadPoolSection `json:"merge"`     // deprecated
352}
353
354// NodesInfoNodeThreadPoolSection represents information about a certain
355// type of thread pool, e.g. for indexing or searching.
356type NodesInfoNodeThreadPoolSection struct {
357	Type      string      `json:"type"`       // e.g. fixed, scaling, or fixed_auto_queue_size
358	Min       int         `json:"min"`        // e.g. 4
359	Max       int         `json:"max"`        // e.g. 4
360	KeepAlive string      `json:"keep_alive"` // e.g. "5m"
361	QueueSize interface{} `json:"queue_size"` // e.g. "1k" or -1
362}
363
364// NodesInfoNodeTransport represents transport-related information.
365type NodesInfoNodeTransport struct {
366	BoundAddress   []string                                  `json:"bound_address"`
367	PublishAddress string                                    `json:"publish_address"`
368	Profiles       map[string]*NodesInfoNodeTransportProfile `json:"profiles"`
369}
370
371// NodesInfoNodeTransportProfile represents a transport profile.
372type NodesInfoNodeTransportProfile struct {
373	BoundAddress   []string `json:"bound_address"`
374	PublishAddress string   `json:"publish_address"`
375}
376
377// NodesInfoNodeHTTP represents HTTP-related information.
378type NodesInfoNodeHTTP struct {
379	BoundAddress            []string `json:"bound_address"`      // e.g. ["127.0.0.1:9200", "[fe80::1]:9200", "[::1]:9200"]
380	PublishAddress          string   `json:"publish_address"`    // e.g. "127.0.0.1:9300"
381	MaxContentLength        string   `json:"max_content_length"` // e.g. "100mb"
382	MaxContentLengthInBytes int64    `json:"max_content_length_in_bytes"`
383}
384
385// NodesInfoNodePlugin represents information about a plugin.
386type NodesInfoNodePlugin struct {
387	Name                 string   `json:"name"`    // e.g. "ingest-geoip"
388	Version              string   `json:"version"` // e.g. "6.2.3"
389	ElasticsearchVersion string   `json:"elasticsearch_version"`
390	JavaVersion          string   `json:"java_version"`
391	Description          string   `json:"description"` // e.g. "Ingest processor ..."
392	Classname            string   `json:"classname"`   // e.g. "org.elasticsearch.ingest.geoip.IngestGeoIpPlugin"
393	ExtendedPlugins      []string `json:"extended_plugins"`
394	HasNativeController  bool     `json:"has_native_controller"`
395	RequiresKeystore     bool     `json:"requires_keystore"`
396}
397
398// NodesInfoNodeModule represents information about a module.
399type NodesInfoNodeModule struct {
400	Name                 string   `json:"name"`    // e.g. "ingest-geoip"
401	Version              string   `json:"version"` // e.g. "6.2.3"
402	ElasticsearchVersion string   `json:"elasticsearch_version"`
403	JavaVersion          string   `json:"java_version"`
404	Description          string   `json:"description"` // e.g. "Ingest processor ..."
405	Classname            string   `json:"classname"`   // e.g. "org.elasticsearch.ingest.geoip.IngestGeoIpPlugin"
406	ExtendedPlugins      []string `json:"extended_plugins"`
407	HasNativeController  bool     `json:"has_native_controller"`
408	RequiresKeystore     bool     `json:"requires_keystore"`
409}
410
411// NodesInfoNodeIngest represents information about the ingester.
412type NodesInfoNodeIngest struct {
413	Processors []*NodesInfoNodeIngestProcessorInfo `json:"processors"`
414}
415
416// NodesInfoNodeIngestProcessorInfo represents ingest processor info.
417type NodesInfoNodeIngestProcessorInfo struct {
418	Type string `json:"type"` // e.g. append, convert, date etc.
419}
420