blob: 86fe5d7c8cadf5ff0449ee08eb124cd539c9b8fc [file] [log] [blame]
Jamie Hannafordb6927c12014-11-03 10:31:26 +01001package nodes
Jamie Hannaford3cfa00a2014-11-03 11:16:35 +01002
3import (
Jamie Hannaford0a9a6be2014-11-03 12:55:38 +01004 "errors"
Jamie Hannaford3cfa00a2014-11-03 11:16:35 +01005 "fmt"
6
7 "github.com/rackspace/gophercloud"
8 "github.com/rackspace/gophercloud/pagination"
9)
10
Jamie Hannaford8cdaa802014-11-03 15:11:39 +010011// List is the operation responsible for returning a paginated collection of
12// load balancer nodes. It requires the node ID, its parent load balancer ID,
13// and optional limit integer (passed in either as a pointer or a nil poitner).
Jamie Hannaford3cfa00a2014-11-03 11:16:35 +010014func List(client *gophercloud.ServiceClient, loadBalancerID int, limit *int) pagination.Pager {
15 url := rootURL(client, loadBalancerID)
Jamie Hannaford3cfa00a2014-11-03 11:16:35 +010016 if limit != nil {
17 url += fmt.Sprintf("?limit=%d", limit)
18 }
19
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010020 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
Jamie Hannaford3cfa00a2014-11-03 11:16:35 +010021 return NodePage{pagination.SinglePageBase(r)}
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010022 })
23}
24
Jamie Hannaford8cdaa802014-11-03 15:11:39 +010025// CreateOptsBuilder is the interface responsible for generating the JSON
26// for a Create operation.
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010027type CreateOptsBuilder interface {
28 ToNodeCreateMap() (map[string]interface{}, error)
29}
30
Jamie Hannaford8cdaa802014-11-03 15:11:39 +010031// CreateOpts is a slice of CreateOpt structs, that allow the user to create
32// multiple nodes in a single operation (one node per CreateOpt).
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010033type CreateOpts []CreateOpt
34
Jamie Hannaford8cdaa802014-11-03 15:11:39 +010035// CreateOpt represents the options to create a single node.
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010036type CreateOpt struct {
Jamie Hannaford8cdaa802014-11-03 15:11:39 +010037 // Required - the IP address or CIDR for this back-end node. It can either be
38 // a private IP (ServiceNet) or a public IP.
39 Address string
40
41 // Optional - the port on which traffic is sent and received.
42 Port int
43
44 // Optional - the condition of the node. See the consts in Results.go.
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010045 Condition Condition
Jamie Hannaford8cdaa802014-11-03 15:11:39 +010046
47 // Optional - the type of the node. See the consts in Results.go.
48 Type Type
49
50 // Optional - a pointer to an integer between 0 and 100.
51 Weight *int
Jamie Hannaford00222d72014-11-03 13:58:52 +010052}
53
54func validateWeight(weight *int) error {
55 if weight != nil && (*weight > 100 || *weight < 0) {
56 return errors.New("Weight must be a valid int between 0 and 100")
57 }
58 return nil
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010059}
60
Jamie Hannaford8cdaa802014-11-03 15:11:39 +010061// ToNodeCreateMap converts a slice of options into a map that can be used for
62// the JSON.
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010063func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) {
64 type nodeMap map[string]interface{}
65 nodes := []nodeMap{}
66
67 for k, v := range opts {
68 if v.Address == "" {
69 return nodeMap{}, fmt.Errorf("ID is a required attribute, none provided for %d CreateOpt element", k)
70 }
Jamie Hannaford00222d72014-11-03 13:58:52 +010071 if weightErr := validateWeight(v.Weight); weightErr != nil {
72 return nodeMap{}, weightErr
73 }
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010074
75 node := make(map[string]interface{})
76 node["address"] = v.Address
77
78 if v.Port > 0 {
79 node["port"] = v.Port
80 }
81 if v.Condition != "" {
82 node["condition"] = v.Condition
83 }
84 if v.Type != "" {
85 node["type"] = v.Type
86 }
Jamie Hannaford00222d72014-11-03 13:58:52 +010087 if v.Weight != nil {
88 node["weight"] = &v.Weight
89 }
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010090
91 nodes = append(nodes, node)
Jamie Hannaford3cfa00a2014-11-03 11:16:35 +010092 }
Jamie Hannaforded8b89a2014-11-03 12:24:19 +010093
94 return nodeMap{"nodes": nodes}, nil
95}
96
Jamie Hannaford8cdaa802014-11-03 15:11:39 +010097// Create is the operation responsible for creating a new node on a load
98// balancer. Since every load balancer exists in both ServiceNet and the public
99// Internet, both private and public IP addresses can be used for nodes.
100//
101// If nodes need time to boot up services before they become operational, you
102// can temporarily prevent traffic from being sent to that node by setting the
103// Condition field to DRAINING. Health checks will still be performed; but once
104// your node is ready, you can update its condition to ENABLED and have it
105// handle traffic.
Jamie Hannaforded8b89a2014-11-03 12:24:19 +0100106func Create(client *gophercloud.ServiceClient, loadBalancerID int, opts CreateOptsBuilder) CreateResult {
107 var res CreateResult
108
109 reqBody, err := opts.ToNodeCreateMap()
110 if err != nil {
111 res.Err = err
112 return res
113 }
114
Ash Wilson59fb6c42015-02-12 16:21:13 -0500115 resp, err := client.Request("POST", rootURL(client, loadBalancerID), gophercloud.RequestOpts{
116 JSONBody: &reqBody,
117 JSONResponse: &res.Body,
118 OkCodes: []int{202},
Jamie Hannaforded8b89a2014-11-03 12:24:19 +0100119 })
120 if err != nil {
121 res.Err = err
122 return res
123 }
124
Ash Wilson59fb6c42015-02-12 16:21:13 -0500125 pr, err := pagination.PageResultFrom(resp)
Jamie Hannaforded8b89a2014-11-03 12:24:19 +0100126 if err != nil {
127 res.Err = err
128 return res
129 }
130
131 return CreateResult{pagination.SinglePageBase(pr)}
Jamie Hannaford3cfa00a2014-11-03 11:16:35 +0100132}
Jamie Hannaford16bebfc2014-11-03 12:52:30 +0100133
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100134// BulkDelete is the operation responsible for batch deleting multiple nodes in
135// a single operation. It accepts a slice of integer IDs and will remove them
136// from the load balancer. The maximum limit is 10 node removals at once.
Jamie Hannaford16bebfc2014-11-03 12:52:30 +0100137func BulkDelete(c *gophercloud.ServiceClient, loadBalancerID int, nodeIDs []int) DeleteResult {
138 var res DeleteResult
139
Jamie Hannaford0a9a6be2014-11-03 12:55:38 +0100140 if len(nodeIDs) > 10 || len(nodeIDs) == 0 {
141 res.Err = errors.New("You must provide a minimum of 1 and a maximum of 10 node IDs")
142 return res
143 }
144
Jamie Hannaford16bebfc2014-11-03 12:52:30 +0100145 url := rootURL(c, loadBalancerID)
Jamie Hannaford950561c2014-11-12 11:12:20 +0100146 url += gophercloud.IDSliceToQueryString("id", nodeIDs)
Jamie Hannaford16bebfc2014-11-03 12:52:30 +0100147
Ash Wilson2199f102015-02-12 16:16:09 -0500148 _, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{
149 OkCodes: []int{202},
Jamie Hannaford16bebfc2014-11-03 12:52:30 +0100150 })
151
152 return res
153}
Jamie Hannaford51175a02014-11-03 13:29:44 +0100154
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100155// Get is the operation responsible for showing details for a single node.
Jamie Hannaford51175a02014-11-03 13:29:44 +0100156func Get(c *gophercloud.ServiceClient, lbID, nodeID int) GetResult {
157 var res GetResult
158
Ash Wilson59fb6c42015-02-12 16:21:13 -0500159 _, res.Err = c.Request("GET", resourceURL(c, lbID, nodeID), gophercloud.RequestOpts{
160 JSONResponse: &res.Body,
161 OkCodes: []int{200},
Jamie Hannaford51175a02014-11-03 13:29:44 +0100162 })
163
164 return res
165}
Jamie Hannaford00222d72014-11-03 13:58:52 +0100166
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100167// UpdateOptsBuilder represents a type that can be converted into a JSON-like
168// map structure.
Jamie Hannaford00222d72014-11-03 13:58:52 +0100169type UpdateOptsBuilder interface {
170 ToNodeUpdateMap() (map[string]interface{}, error)
171}
172
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100173// UpdateOpts represent the options for updating an existing node.
Jamie Hannaford00222d72014-11-03 13:58:52 +0100174type UpdateOpts struct {
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100175 // Optional - the condition of the node. See the consts in Results.go.
Jamie Hannaford00222d72014-11-03 13:58:52 +0100176 Condition Condition
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100177
178 // Optional - the type of the node. See the consts in Results.go.
179 Type Type
180
181 // Optional - a pointer to an integer between 0 and 100.
182 Weight *int
Jamie Hannaford00222d72014-11-03 13:58:52 +0100183}
184
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100185// ToNodeUpdateMap converts an options struct into a JSON-like map.
Jamie Hannaford00222d72014-11-03 13:58:52 +0100186func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) {
187 node := make(map[string]interface{})
188
Jamie Hannaford00222d72014-11-03 13:58:52 +0100189 if opts.Condition != "" {
190 node["condition"] = opts.Condition
191 }
192 if opts.Weight != nil {
193 if weightErr := validateWeight(opts.Weight); weightErr != nil {
194 return node, weightErr
195 }
196 node["weight"] = &opts.Weight
197 }
198 if opts.Type != "" {
199 node["type"] = opts.Type
200 }
201
202 return map[string]interface{}{"node": node}, nil
203}
204
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100205// Update is the operation responsible for updating an existing node. A node's
206// IP, port, and status are immutable attributes and cannot be modified.
Jamie Hannaford00222d72014-11-03 13:58:52 +0100207func Update(c *gophercloud.ServiceClient, lbID, nodeID int, opts UpdateOptsBuilder) UpdateResult {
208 var res UpdateResult
209
210 reqBody, err := opts.ToNodeUpdateMap()
211 if err != nil {
212 res.Err = err
213 return res
214 }
215
Ash Wilson59fb6c42015-02-12 16:21:13 -0500216 _, res.Err = c.Request("PUT", resourceURL(c, lbID, nodeID), gophercloud.RequestOpts{
217 JSONBody: &reqBody,
218 OkCodes: []int{202},
Jamie Hannaford00222d72014-11-03 13:58:52 +0100219 })
220
221 return res
222}
Jamie Hannaford9f4870f2014-11-03 14:03:16 +0100223
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100224// Delete is the operation responsible for permanently deleting a node.
Jamie Hannaford9f4870f2014-11-03 14:03:16 +0100225func Delete(c *gophercloud.ServiceClient, lbID, nodeID int) DeleteResult {
226 var res DeleteResult
Ash Wilson59fb6c42015-02-12 16:21:13 -0500227 _, res.Err = c.Request("DELETE", resourceURL(c, lbID, nodeID), gophercloud.RequestOpts{
228 OkCodes: []int{202},
Jamie Hannaford9f4870f2014-11-03 14:03:16 +0100229 })
Jamie Hannaford9f4870f2014-11-03 14:03:16 +0100230 return res
231}
Jamie Hannaford1fac9dd2014-11-03 14:22:40 +0100232
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100233// ListEventsOptsBuilder allows extensions to add additional parameters to the
234// List request.
235type ListEventsOptsBuilder interface {
236 ToEventsListQuery() (string, error)
237}
238
239// ListEventsOpts allows the filtering and sorting of paginated collections through
240// the API.
241type ListEventsOpts struct {
242 Marker string `q:"marker"`
243 Limit int `q:"limit"`
244}
245
246// ToEventsListQuery formats a ListOpts into a query string.
247func (opts ListEventsOpts) ToEventsListQuery() (string, error) {
248 q, err := gophercloud.BuildQueryString(opts)
249 if err != nil {
250 return "", err
251 }
252 return q.String(), nil
253}
254
255// ListEvents is the operation responsible for listing all the events
256// associated with the activity between the node and the load balancer. The
257// events report errors found with the node. The detailedMessage provides the
258// detailed reason for the error.
Jamie Hannaford703527e2014-11-05 12:38:15 +0100259func ListEvents(client *gophercloud.ServiceClient, loadBalancerID int, opts ListEventsOptsBuilder) pagination.Pager {
260 url := eventsURL(client, loadBalancerID)
Jamie Hannaford8cdaa802014-11-03 15:11:39 +0100261
262 if opts != nil {
263 query, err := opts.ToEventsListQuery()
264 if err != nil {
265 return pagination.Pager{Err: err}
266 }
267 url += query
268 }
269
Jamie Hannaford1fac9dd2014-11-03 14:22:40 +0100270 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
271 return NodeEventPage{pagination.SinglePageBase(r)}
272 })
273}