1// Copyright 2016-2020 The Libsacloud Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package sacloud
16
17import (
18	"encoding/json"
19	"math"
20	"strings"
21	"time"
22)
23
24// MonitorValue アクティビティモニター
25type MonitorValue struct {
26	CPUTime           *float64 `json:"CPU-TIME,omitempty"`          // CPU時間
27	Write             *float64 `json:",omitempty"`                  // ディスク書き込み
28	Read              *float64 `json:",omitempty"`                  // ディスク読み取り
29	Receive           *float64 `json:",omitempty"`                  // パケット受信
30	Send              *float64 `json:",omitempty"`                  // パケット送信
31	In                *float64 `json:",omitempty"`                  // パケット受信
32	Out               *float64 `json:",omitempty"`                  // パケット送信
33	TotalMemorySize   *float64 `json:"Total-Memory-Size,omitempty"` // 総メモリサイズ
34	UsedMemorySize    *float64 `json:"Used-Memory-Size,omitempty"`  // 使用済みメモリサイズ
35	TotalDisk1Size    *float64 `json:"Total-Disk1-Size,omitempty"`  // 総ディスクサイズ
36	UsedDisk1Size     *float64 `json:"Used-Disk1-Size,omitempty"`   // 使用済みディスクサイズ
37	TotalDisk2Size    *float64 `json:"Total-Disk2-Size,omitempty"`  // 総ディスクサイズ
38	UsedDisk2Size     *float64 `json:"Used-Disk2-Size,omitempty"`   // 使用済みディスクサイズ
39	BinlogUsedSizeKiB *float64 `json:"binlogUsedSizeKiB,omitempty"` // バイナリログのサイズ(レプリケーション有効時のみ、master/slave両方で利用可能)
40	DelayTimeSec      *float64 `json:"delayTimeSec,omitempty"`      // レプリケーション遅延時間(レプリケーション有効時のみ、slave側のみ)
41	FreeDiskSize      *float64 `json:"Free-Disk-Size,omitempty"`    // 空きディスクサイズ(NFS)
42	ResponseTimeSec   *float64 `json:"responsetimesec,omitempty"`   // レスポンスタイム(シンプル監視)
43	UplinkBPS         *float64 `json:"UplinkBps,omitempty"`         // 上り方向トラフィック
44	DownlinkBPS       *float64 `json:"DownlinkBps,omitempty"`       // 下り方向トラフィック
45	ActiveConnections *float64 `json:"activeConnections,omitempty"` // アクティブコネクション(プロキシLB)
46	ConnectionsPerSec *float64 `json:"connectionsPerSec,omitempty"` // 秒間コネクション数
47}
48
49// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応)
50func (m *MonitorValue) UnmarshalJSON(data []byte) error {
51	targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1)
52	if targetData == `[]` {
53		return nil
54	}
55
56	tmp := &struct {
57		CPUTime           *float64 `json:"CPU-TIME,omitempty"`
58		Write             *float64 `json:",omitempty"`
59		Read              *float64 `json:",omitempty"`
60		Receive           *float64 `json:",omitempty"`
61		Send              *float64 `json:",omitempty"`
62		In                *float64 `json:",omitempty"`
63		Out               *float64 `json:",omitempty"`
64		TotalMemorySize   *float64 `json:"Total-Memory-Size,omitempty"`
65		UsedMemorySize    *float64 `json:"Used-Memory-Size,omitempty"`
66		TotalDisk1Size    *float64 `json:"Total-Disk1-Size,omitempty"`
67		UsedDisk1Size     *float64 `json:"Used-Disk1-Size,omitempty"`
68		TotalDisk2Size    *float64 `json:"Total-Disk2-Size,omitempty"`
69		UsedDisk2Size     *float64 `json:"Used-Disk2-Size,omitempty"`
70		BinlogUsedSizeKiB *float64 `json:"binlogUsedSizeKiB,omitempty"`
71		DelayTimeSec      *float64 `json:"delayTimeSec,omitempty"`
72		FreeDiskSize      *float64 `json:"Free-Disk-Size,omitempty"`
73		ResponseTimeSec   *float64 `json:"responsetimesec,omitempty"`
74		UplinkBPS         *float64 `json:"UplinkBps,omitempty"`
75		DownlinkBPS       *float64 `json:"DownlinkBps,omitempty"`
76		ActiveConnections *float64 `json:"activeConnections,omitempty"`
77		ConnectionsPerSec *float64 `json:"connectionsPerSec,omitempty"`
78	}{}
79	if err := json.Unmarshal(data, &tmp); err != nil {
80		return err
81	}
82
83	m.CPUTime = tmp.CPUTime
84	m.Write = tmp.Write
85	m.Read = tmp.Read
86	m.Receive = tmp.Receive
87	m.Send = tmp.Send
88	m.In = tmp.In
89	m.Out = tmp.Out
90	m.TotalMemorySize = tmp.TotalMemorySize
91	m.UsedMemorySize = tmp.UsedMemorySize
92	m.TotalDisk1Size = tmp.TotalDisk1Size
93	m.UsedDisk1Size = tmp.UsedDisk1Size
94	m.TotalDisk2Size = tmp.TotalDisk2Size
95	m.UsedDisk2Size = tmp.UsedDisk2Size
96	m.BinlogUsedSizeKiB = tmp.BinlogUsedSizeKiB
97	m.DelayTimeSec = tmp.DelayTimeSec
98	m.FreeDiskSize = tmp.FreeDiskSize
99	m.ResponseTimeSec = tmp.ResponseTimeSec
100	m.UplinkBPS = tmp.UplinkBPS
101	m.DownlinkBPS = tmp.DownlinkBPS
102	m.ActiveConnections = tmp.ActiveConnections
103	m.ConnectionsPerSec = tmp.ConnectionsPerSec
104
105	return nil
106}
107
108// ResourceMonitorRequest アクティビティモニター取得リクエスト
109type ResourceMonitorRequest struct {
110	Start *time.Time `json:",omitempty"` // 取得開始時間
111	End   *time.Time `json:",omitempty"` // 取得終了時間
112}
113
114// NewResourceMonitorRequest アクティビティモニター取得リクエスト作成
115func NewResourceMonitorRequest(start *time.Time, end *time.Time) *ResourceMonitorRequest {
116	res := &ResourceMonitorRequest{}
117	if start != nil {
118		t := start.Truncate(time.Second)
119		res.Start = &t
120	}
121	if end != nil {
122		t := end.Truncate(time.Second)
123		res.End = &t
124	}
125	return res
126}
127
128// ResourceMonitorResponse アクティビティモニターレスポンス
129type ResourceMonitorResponse struct {
130	Data *MonitorValues `json:",omitempty"` // メトリクス
131}
132
133// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応)
134func (m *MonitorValues) UnmarshalJSON(data []byte) error {
135	targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1)
136	if targetData == `[]` {
137		return nil
138	}
139
140	tmp := map[string]*MonitorValue{}
141	if err := json.Unmarshal(data, &tmp); err != nil {
142		return err
143	}
144
145	value := MonitorValues(tmp)
146	*m = value
147	return nil
148}
149
150// MonitorSummaryData メトリクスサマリー
151type MonitorSummaryData struct {
152	Max   float64 // 最大値
153	Min   float64 // 最小値
154	Avg   float64 // 平均値
155	Count float64 // データ個数
156
157}
158
159// MonitorSummary アクティビティーモニター サマリー
160type MonitorSummary struct {
161	CPU  *MonitorSummaryData // CPU時間サマリー
162	Disk *struct {           // ディスク利用サマリー
163		Write *MonitorSummaryData // ディスク書き込みサマリー
164		Read  *MonitorSummaryData // ディスク読み取りサマリー
165	}
166	Interface *struct { // NIC送受信サマリー
167		Receive *MonitorSummaryData // 受信パケットサマリー
168		Send    *MonitorSummaryData // 送信パケットサマリー
169	}
170}
171
172// MonitorValues メトリクス リスト
173type MonitorValues map[string]*MonitorValue
174
175// FlatMonitorValue フラット化したメトリクス
176type FlatMonitorValue struct {
177	Time  time.Time // 対象時刻
178	Value float64   // 値
179}
180
181// Calc サマリー計算
182func (m *MonitorValues) Calc() *MonitorSummary {
183
184	res := &MonitorSummary{}
185	res.CPU = m.calcBy(func(v *MonitorValue) *float64 { return v.CPUTime })
186	res.Disk = &struct {
187		Write *MonitorSummaryData
188		Read  *MonitorSummaryData
189	}{
190		Write: m.calcBy(func(v *MonitorValue) *float64 { return v.Write }),
191		Read:  m.calcBy(func(v *MonitorValue) *float64 { return v.Read }),
192	}
193	res.Interface = &struct {
194		Receive *MonitorSummaryData
195		Send    *MonitorSummaryData
196	}{
197		Receive: m.calcBy(func(v *MonitorValue) *float64 { return v.Receive }),
198		Send:    m.calcBy(func(v *MonitorValue) *float64 { return v.Send }),
199	}
200
201	return res
202}
203
204func (m *MonitorValues) calcBy(f func(m *MonitorValue) *float64) *MonitorSummaryData {
205	res := &MonitorSummaryData{}
206	var sum float64
207	for _, data := range map[string]*MonitorValue(*m) {
208		value := f(data)
209		if value != nil {
210			res.Count++
211			res.Min = math.Min(res.Min, *value)
212			res.Max = math.Max(res.Max, *value)
213			sum += *value
214		}
215	}
216	if sum > 0 && res.Count > 0 {
217		res.Avg = sum / res.Count
218	}
219
220	return res
221}
222
223// FlattenCPUTimeValue フラット化 CPU時間
224func (m *MonitorValues) FlattenCPUTimeValue() ([]FlatMonitorValue, error) {
225	return m.flattenValue(func(v *MonitorValue) *float64 { return v.CPUTime })
226}
227
228// FlattenDiskWriteValue フラット化 ディスク書き込み
229func (m *MonitorValues) FlattenDiskWriteValue() ([]FlatMonitorValue, error) {
230	return m.flattenValue(func(v *MonitorValue) *float64 { return v.Write })
231}
232
233// FlattenDiskReadValue フラット化 ディスク読み取り
234func (m *MonitorValues) FlattenDiskReadValue() ([]FlatMonitorValue, error) {
235	return m.flattenValue(func(v *MonitorValue) *float64 { return v.Read })
236}
237
238// FlattenPacketSendValue フラット化 パケット送信
239func (m *MonitorValues) FlattenPacketSendValue() ([]FlatMonitorValue, error) {
240	return m.flattenValue(func(v *MonitorValue) *float64 { return v.Send })
241}
242
243// FlattenPacketReceiveValue フラット化 パケット受信
244func (m *MonitorValues) FlattenPacketReceiveValue() ([]FlatMonitorValue, error) {
245	return m.flattenValue(func(v *MonitorValue) *float64 { return v.Receive })
246}
247
248// FlattenInternetInValue フラット化 パケット受信
249func (m *MonitorValues) FlattenInternetInValue() ([]FlatMonitorValue, error) {
250	return m.flattenValue(func(v *MonitorValue) *float64 { return v.In })
251}
252
253// FlattenInternetOutValue フラット化 パケット送信
254func (m *MonitorValues) FlattenInternetOutValue() ([]FlatMonitorValue, error) {
255	return m.flattenValue(func(v *MonitorValue) *float64 { return v.Out })
256}
257
258// FlattenTotalMemorySizeValue フラット化 総メモリサイズ
259func (m *MonitorValues) FlattenTotalMemorySizeValue() ([]FlatMonitorValue, error) {
260	return m.flattenValue(func(v *MonitorValue) *float64 { return v.TotalMemorySize })
261}
262
263// FlattenUsedMemorySizeValue フラット化 使用済みメモリサイズ
264func (m *MonitorValues) FlattenUsedMemorySizeValue() ([]FlatMonitorValue, error) {
265	return m.flattenValue(func(v *MonitorValue) *float64 { return v.UsedMemorySize })
266}
267
268// FlattenTotalDisk1SizeValue フラット化 総ディスクサイズ
269func (m *MonitorValues) FlattenTotalDisk1SizeValue() ([]FlatMonitorValue, error) {
270	return m.flattenValue(func(v *MonitorValue) *float64 { return v.TotalDisk1Size })
271}
272
273// FlattenUsedDisk1SizeValue フラット化 使用済みディスクサイズ
274func (m *MonitorValues) FlattenUsedDisk1SizeValue() ([]FlatMonitorValue, error) {
275	return m.flattenValue(func(v *MonitorValue) *float64 { return v.UsedDisk1Size })
276}
277
278// FlattenTotalDisk2SizeValue フラット化 総ディスクサイズ
279func (m *MonitorValues) FlattenTotalDisk2SizeValue() ([]FlatMonitorValue, error) {
280	return m.flattenValue(func(v *MonitorValue) *float64 { return v.TotalDisk2Size })
281}
282
283// FlattenUsedDisk2SizeValue フラット化 使用済みディスクサイズ
284func (m *MonitorValues) FlattenUsedDisk2SizeValue() ([]FlatMonitorValue, error) {
285	return m.flattenValue(func(v *MonitorValue) *float64 { return v.UsedDisk2Size })
286}
287
288// FlattenBinlogUsedSizeKiBValue フラット化 バイナリログサイズ
289func (m *MonitorValues) FlattenBinlogUsedSizeKiBValue() ([]FlatMonitorValue, error) {
290	return m.flattenValue(func(v *MonitorValue) *float64 { return v.BinlogUsedSizeKiB })
291}
292
293// FlattenDelayTimeSecValue フラット化 レプリケーション遅延時間
294func (m *MonitorValues) FlattenDelayTimeSecValue() ([]FlatMonitorValue, error) {
295	return m.flattenValue(func(v *MonitorValue) *float64 { return v.DelayTimeSec })
296}
297
298// FlattenFreeDiskSizeValue フラット化 空きディスクサイズ(NFS)
299func (m *MonitorValues) FlattenFreeDiskSizeValue() ([]FlatMonitorValue, error) {
300	return m.flattenValue(func(v *MonitorValue) *float64 { return v.FreeDiskSize })
301}
302
303// FlattenResponseTimeSecValue フラット化 レスポンスタイム(シンプル監視)
304func (m *MonitorValues) FlattenResponseTimeSecValue() ([]FlatMonitorValue, error) {
305	return m.flattenValue(func(v *MonitorValue) *float64 { return v.ResponseTimeSec })
306}
307
308// FlattenUplinkBPSValue フラット化 上り方向トラフィック(セキュアモバイルSIM)
309func (m *MonitorValues) FlattenUplinkBPSValue() ([]FlatMonitorValue, error) {
310	return m.flattenValue(func(v *MonitorValue) *float64 { return v.UplinkBPS })
311}
312
313// FlattenDownlinkBPSValue フラット化 下り方向トライフィック(セキュアモバイルSIM)
314func (m *MonitorValues) FlattenDownlinkBPSValue() ([]FlatMonitorValue, error) {
315	return m.flattenValue(func(v *MonitorValue) *float64 { return v.DownlinkBPS })
316}
317
318// FlattenActiveConnections フラット化 アクティブコネクション
319func (m *MonitorValues) FlattenActiveConnections() ([]FlatMonitorValue, error) {
320	return m.flattenValue(func(v *MonitorValue) *float64 { return v.ActiveConnections })
321}
322
323// FlattenConnectionsPerSec フラット化 秒間接続数
324func (m *MonitorValues) FlattenConnectionsPerSec() ([]FlatMonitorValue, error) {
325	return m.flattenValue(func(v *MonitorValue) *float64 { return v.ConnectionsPerSec })
326}
327
328func (m *MonitorValues) flattenValue(f func(*MonitorValue) *float64) ([]FlatMonitorValue, error) {
329	var res []FlatMonitorValue
330
331	for k, v := range map[string]*MonitorValue(*m) {
332		if f(v) == nil {
333			continue
334		}
335		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
336		if err != nil {
337			return res, err
338		}
339		res = append(res, FlatMonitorValue{
340			// Time
341			Time: time,
342			// Value
343			Value: *f(v),
344		})
345	}
346	return res, nil
347}
348
349// HasValue 取得したアクティビティーモニターに有効値が含まれるか判定
350func (m *MonitorValue) HasValue() bool {
351	values := []*float64{
352		m.CPUTime,
353		m.Read, m.Receive,
354		m.Send, m.Write,
355		m.In, m.Out,
356		m.TotalMemorySize, m.UsedMemorySize,
357		m.TotalDisk1Size, m.UsedDisk1Size,
358		m.TotalDisk2Size, m.UsedDisk2Size,
359		m.BinlogUsedSizeKiB, m.DelayTimeSec,
360		m.FreeDiskSize, m.ResponseTimeSec,
361		m.UplinkBPS, m.DownlinkBPS,
362		m.ActiveConnections, m.ConnectionsPerSec,
363	}
364	for _, v := range values {
365		if v != nil {
366			return true
367		}
368	}
369	return false
370}
371