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 "strconv"
18
19// LoadBalancer ロードバランサー
20type LoadBalancer struct {
21	*Appliance // アプライアンス共通属性
22
23	Remark   *LoadBalancerRemark   `json:",omitempty"` // リマーク
24	Settings *LoadBalancerSettings `json:",omitempty"` // ロードバランサー設定
25}
26
27// IsHA 冗長化されている場合にtrueを返す
28func (l *LoadBalancer) IsHA() bool {
29	isHA := false
30	if len(l.Remark.Servers) > 1 {
31		if v, ok := l.Remark.Servers[1].(map[string]string); ok {
32			if _, ok := v["IPAddress"]; ok {
33				isHA = true
34			}
35		}
36	}
37	return isHA
38}
39
40// IPAddress1 ロードバランサ本体のIPアドレス(1番目)を返す
41func (l *LoadBalancer) IPAddress1() string {
42	if len(l.Remark.Servers) > 0 {
43		if v, ok := l.Remark.Servers[0].(map[string]string); ok {
44			if v, ok := v["IPAddress"]; ok {
45				return v
46			}
47		}
48	}
49	return ""
50}
51
52// IPAddress2 ロードバランサ本体のIPアドレス(2番目)を返す
53func (l *LoadBalancer) IPAddress2() string {
54	if len(l.Remark.Servers) > 1 {
55		if v, ok := l.Remark.Servers[1].(map[string]string); ok {
56			if v, ok := v["IPAddress"]; ok {
57				return v
58			}
59		}
60	}
61	return ""
62}
63
64// LoadBalancerRemark リマーク
65type LoadBalancerRemark struct {
66	*ApplianceRemarkBase
67	// TODO Zone
68	//Zone *Resource
69}
70
71// LoadBalancerSettings ロードバランサー設定リスト
72type LoadBalancerSettings struct {
73	LoadBalancer []*LoadBalancerSetting // ロードバランサー設定リスト
74}
75
76// LoadBalancerSetting ロードバランサー仮想IP設定
77type LoadBalancerSetting struct {
78	VirtualIPAddress string                `json:",omitempty"` // 仮想IPアドレス
79	Port             string                `json:",omitempty"` // ポート番号
80	DelayLoop        string                `json:",omitempty"` // 監視間隔
81	SorryServer      string                `json:",omitempty"` // ソーリーサーバー
82	Description      string                `json:",omitempty"` // 説明
83	Servers          []*LoadBalancerServer `json:",omitempty"` // 仮想IP配下の実サーバー
84}
85
86// LoadBalancerServer 仮想IP設定配下のサーバー
87type LoadBalancerServer struct {
88	IPAddress   string                   `json:",omitempty"` // IPアドレス
89	Port        string                   `json:",omitempty"` // ポート番号
90	HealthCheck *LoadBalancerHealthCheck `json:",omitempty"` // ヘルスチェック
91	Enabled     string                   `json:",omitempty"` // 有効/無効
92	Status      string                   `json:",omitempty"` // ステータス
93	ActiveConn  string                   `json:",omitempty"` // アクティブなコネクション
94}
95
96// LoadBalancerHealthCheck ヘルスチェック
97type LoadBalancerHealthCheck struct {
98	Protocol string `json:",omitempty"` // プロトコル
99	Path     string `json:",omitempty"` // HTTP/HTTPSの場合のリクエストパス
100	Status   string `json:",omitempty"` // HTTP/HTTPSの場合の期待するレスポンスコード
101}
102
103// LoadBalancerPlan ロードバランサープラン
104type LoadBalancerPlan int
105
106var (
107	// LoadBalancerPlanStandard スタンダードプラン
108	LoadBalancerPlanStandard = LoadBalancerPlan(1)
109	// LoadBalancerPlanPremium プレミアムプラン
110	LoadBalancerPlanPremium = LoadBalancerPlan(2)
111)
112
113// CreateLoadBalancerValue ロードバランサー作成用パラメーター
114type CreateLoadBalancerValue struct {
115	SwitchID     ID               // 接続先スイッチID
116	VRID         int              // VRID
117	Plan         LoadBalancerPlan // プラン
118	IPAddress1   string           // IPアドレス
119	MaskLen      int              // ネットワークマスク長
120	DefaultRoute string           // デフォルトルート
121	Name         string           // 名称
122	Description  string           // 説明
123	Tags         []string         // タグ
124	Icon         *Resource        // アイコン
125}
126
127// CreateDoubleLoadBalancerValue ロードバランサー(冗長化あり)作成用パラメーター
128type CreateDoubleLoadBalancerValue struct {
129	*CreateLoadBalancerValue
130	IPAddress2 string // IPアドレス2
131}
132
133// AllowLoadBalancerHealthCheckProtocol ロードバランサーでのヘルスチェック対応プロトコルリスト
134func AllowLoadBalancerHealthCheckProtocol() []string {
135	return []string{"http", "https", "ping", "tcp"}
136}
137
138// CreateNewLoadBalancerSingle ロードバランサー作成(冗長化なし)
139func CreateNewLoadBalancerSingle(values *CreateLoadBalancerValue, settings []*LoadBalancerSetting) (*LoadBalancer, error) {
140
141	lb := &LoadBalancer{
142		Appliance: &Appliance{
143			Class:           "loadbalancer",
144			propName:        propName{Name: values.Name},
145			propDescription: propDescription{Description: values.Description},
146			propTags:        propTags{Tags: values.Tags},
147			propPlanID:      propPlanID{Plan: &Resource{ID: ID(values.Plan)}},
148			propIcon: propIcon{
149				&Icon{
150					Resource: values.Icon,
151				},
152			},
153		},
154		Remark: &LoadBalancerRemark{
155			ApplianceRemarkBase: &ApplianceRemarkBase{
156				Switch: &ApplianceRemarkSwitch{
157					ID: values.SwitchID,
158				},
159				VRRP: &ApplianceRemarkVRRP{
160					VRID: values.VRID,
161				},
162				Network: &ApplianceRemarkNetwork{
163					NetworkMaskLen: values.MaskLen,
164					DefaultRoute:   values.DefaultRoute,
165				},
166				Servers: []interface{}{
167					map[string]string{"IPAddress": values.IPAddress1},
168				},
169			},
170		},
171	}
172
173	for _, s := range settings {
174		lb.AddLoadBalancerSetting(s)
175	}
176
177	return lb, nil
178}
179
180// CreateNewLoadBalancerDouble ロードバランサー(冗長化あり)作成
181func CreateNewLoadBalancerDouble(values *CreateDoubleLoadBalancerValue, settings []*LoadBalancerSetting) (*LoadBalancer, error) {
182	lb, err := CreateNewLoadBalancerSingle(values.CreateLoadBalancerValue, settings)
183	if err != nil {
184		return nil, err
185	}
186	lb.Remark.Servers = append(lb.Remark.Servers, map[string]string{"IPAddress": values.IPAddress2})
187	return lb, nil
188}
189
190// AddLoadBalancerSetting ロードバランサー仮想IP設定追加
191//
192// ロードバランサー設定は仮想IPアドレス単位で保持しています。
193// 仮想IPを増やす場合にこのメソッドを利用します。
194func (l *LoadBalancer) AddLoadBalancerSetting(setting *LoadBalancerSetting) {
195	if l.Settings == nil {
196		l.Settings = &LoadBalancerSettings{}
197	}
198	if l.Settings.LoadBalancer == nil {
199		l.Settings.LoadBalancer = []*LoadBalancerSetting{}
200	}
201	l.Settings.LoadBalancer = append(l.Settings.LoadBalancer, setting)
202}
203
204// DeleteLoadBalancerSetting ロードバランサー仮想IP設定の削除
205func (l *LoadBalancer) DeleteLoadBalancerSetting(vip string, port string) {
206	res := []*LoadBalancerSetting{}
207	for _, l := range l.Settings.LoadBalancer {
208		if l.VirtualIPAddress != vip || l.Port != port {
209			res = append(res, l)
210		}
211	}
212
213	l.Settings.LoadBalancer = res
214}
215
216// AddServer 仮想IP設定配下へ実サーバーを追加
217func (s *LoadBalancerSetting) AddServer(server *LoadBalancerServer) {
218	if s.Servers == nil {
219		s.Servers = []*LoadBalancerServer{}
220	}
221	s.Servers = append(s.Servers, server)
222}
223
224// DeleteServer 仮想IP設定配下の実サーバーを削除
225func (s *LoadBalancerSetting) DeleteServer(ip string, port string) {
226	res := []*LoadBalancerServer{}
227	for _, server := range s.Servers {
228		if server.IPAddress != ip || server.Port != port {
229			res = append(res, server)
230		}
231	}
232
233	s.Servers = res
234
235}
236
237// LoadBalancerStatusResult ロードバランサーのステータスAPI戻り値
238type LoadBalancerStatusResult []*LoadBalancerStatus
239
240// Get VIPに対応するステータスを取得
241func (l *LoadBalancerStatusResult) Get(vip string) *LoadBalancerStatus {
242	for _, v := range *l {
243		if v.VirtualIPAddress == vip {
244			return v
245		}
246	}
247	return nil
248}
249
250// LoadBalancerStatus ロードバランサーのステータス
251type LoadBalancerStatus struct {
252	VirtualIPAddress string
253	Port             string
254	Servers          []*LoadBalancerServerStatus `json:",omitempty"`
255	CPS              string
256}
257
258// Get IPアドレスに対応する実サーバのステータスを取得
259func (l *LoadBalancerStatus) Get(ip string) *LoadBalancerServerStatus {
260	for _, v := range l.Servers {
261		if v.IPAddress == ip {
262			return v
263		}
264	}
265	return nil
266}
267
268// NumCPS CPSを数値にして返す
269func (l *LoadBalancerStatus) NumCPS() int {
270	v, _ := strconv.Atoi(l.CPS) // nolint - ignore error
271	return v
272}
273
274// NumPort Portを数値にして返す
275func (l *LoadBalancerStatus) NumPort() int {
276	v, _ := strconv.Atoi(l.Port) // nolint - ignore error
277	return v
278}
279
280// LoadBalancerServerStatus ロードバランサーのVIP配下の実サーバのステータス
281type LoadBalancerServerStatus struct {
282	ActiveConn string
283	IPAddress  string
284	Status     string
285	Port       string
286	CPS        string
287}
288
289// NumActiveConn ActiveConnを数値にして返す
290func (l *LoadBalancerServerStatus) NumActiveConn() int {
291	v, _ := strconv.Atoi(l.ActiveConn) // nolint - ignore error
292	return v
293}
294
295// NumCPS CPSを数値にして返す
296func (l *LoadBalancerServerStatus) NumCPS() int {
297	v, _ := strconv.Atoi(l.CPS) // nolint - ignore error
298	return v
299}
300
301// NumPort Portを数値にして返す
302func (l *LoadBalancerServerStatus) NumPort() int {
303	v, _ := strconv.Atoi(l.Port) // nolint - ignore error
304	return v
305}
306