blob: 08fa9b5752093459854c7c93f1de7812a26c4881 [file] [log] [blame]
Jamie Hannaford9fdda582015-02-10 12:15:43 +01001package instances
2
3import (
4 "fmt"
5
6 "github.com/racker/perigee"
7 "github.com/rackspace/gophercloud"
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +01008 db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
Jamie Hannaford90684242015-02-10 12:46:07 +01009 "github.com/rackspace/gophercloud/pagination"
Jamie Hannaford9fdda582015-02-10 12:15:43 +010010)
11
12// CreateOptsBuilder is the top-level interface for create options.
13type CreateOptsBuilder interface {
14 ToInstanceCreateMap() (map[string]interface{}, error)
15}
16
Jamie Hannaford9fdda582015-02-10 12:15:43 +010017// UserOpts is the struct responsible for configuring a user; often in the
18// context of an instance.
19type UserOpts struct {
20 // Specifies a name for the user.
21 Name string
22
23 // Specifies a password for the user.
24 Password string
25
26 // An array of databases that this user will connect to. The `name` field is
27 // the only requirement for each option.
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +010028 Databases []db.CreateOpts
Jamie Hannaford9fdda582015-02-10 12:15:43 +010029
30 // Specifies the host from which a user is allowed to connect to the database.
31 // Possible values are a string containing an IPv4 address or "%" to allow
32 // connecting from any host. Optional; the default is "%".
33 Host string
34}
35
36func (opts UserOpts) ToMap() (map[string]interface{}, error) {
37 user := map[string]interface{}{}
38
39 if opts.Name != "" {
40 user["name"] = opts.Name
41 }
42 if opts.Password != "" {
43 user["password"] = opts.Password
44 }
45 if opts.Host != "" {
46 user["host"] = opts.Host
47 }
48
49 var dbs []map[string]string
50 for _, db := range opts.Databases {
51 dbs = append(dbs, map[string]string{"name": db.Name})
52 }
53 if len(dbs) > 0 {
54 user["databases"] = dbs
55 }
56
57 return user, nil
58}
59
60type UsersOpts []UserOpts
61
62func (opts UsersOpts) ToMap() ([]map[string]interface{}, error) {
63 var users []map[string]interface{}
64 for _, opt := range opts {
65 user, err := opt.ToMap()
66 if err != nil {
67 return users, err
68 }
69 users = append(users, user)
70 }
71 return users, nil
72}
73
74// CreateOpts is the struct responsible for configuring a new database instance.
75type CreateOpts struct {
76 // Either the integer UUID (in string form) of the flavor, or its URI
77 // reference as specified in the response from the List() call. Required.
78 FlavorRef string
79
80 // Specifies the volume size in gigabytes (GB). The value must be between 1
81 // and 300. Required.
82 Size int
83
84 // Name of the instance to create. The length of the name is limited to
85 // 255 characters and any characters are permitted. Optional.
86 Name string
87
88 // A slice of database information options.
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +010089 Databases []db.CreateOpts
Jamie Hannaford9fdda582015-02-10 12:15:43 +010090
91 // A slice of user information options.
92 Users UsersOpts
93}
94
95func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) {
96 if opts.Size > 300 || opts.Size < 1 {
97 return nil, fmt.Errorf("Size (GB) must be between 1-300")
98 }
99 if opts.FlavorRef == "" {
100 return nil, fmt.Errorf("FlavorRef is a required field")
101 }
102
103 instance := map[string]interface{}{
104 "volume": map[string]int{"size": opts.Size},
105 "flavorRef": opts.FlavorRef,
106 }
107
108 if opts.Name != "" {
109 instance["name"] = opts.Name
110 }
111 if len(opts.Databases) > 0 {
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100112 dbs, err := opts.Databases.ToDBCreateMap()
Jamie Hannaford9fdda582015-02-10 12:15:43 +0100113 if err != nil {
114 return nil, err
115 }
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100116 instance["databases"] = dbs["databases"]
Jamie Hannaford9fdda582015-02-10 12:15:43 +0100117 }
118 if len(opts.Users) > 0 {
119 users, err := opts.Users.ToMap()
120 if err != nil {
121 return nil, err
122 }
123 instance["users"] = users
124 }
125
126 return map[string]interface{}{"instance": instance}, nil
127}
128
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100129// Create asynchronously provisions a new database instance. It requires the
130// user to specify a flavor and a volume size. The API service then provisions
131// the instance with the requested flavor and sets up a volume of the specified
132// size, which is the storage for the database instance.
133//
134// Although this call only allows the creation of 1 instance per request, you
135// can create an instance with multiple databases and users. The default
136// binding for a MySQL instance is port 3306.
Jamie Hannaford9fdda582015-02-10 12:15:43 +0100137func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
138 var res CreateResult
139
140 reqBody, err := opts.ToInstanceCreateMap()
141 if err != nil {
142 res.Err = err
143 return res
144 }
145
Jamie Hannaford90684242015-02-10 12:46:07 +0100146 resp, err := perigee.Request("POST", baseURL(client), perigee.Options{
Jamie Hannaford9fdda582015-02-10 12:15:43 +0100147 MoreHeaders: client.AuthenticatedHeaders(),
148 ReqBody: &reqBody,
149 Results: &res.Body,
150 OkCodes: []int{200},
151 })
152
153 res.Header = resp.HttpResponse.Header
154 res.Err = err
155
156 return res
157}
Jamie Hannaford90684242015-02-10 12:46:07 +0100158
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100159// List retrieves the status and information for all database instances.
Jamie Hannaford90684242015-02-10 12:46:07 +0100160func List(client *gophercloud.ServiceClient) pagination.Pager {
161 createPageFn := func(r pagination.PageResult) pagination.Page {
162 return InstancePage{pagination.LinkedPageBase{PageResult: r}}
163 }
164
165 return pagination.NewPager(client, baseURL(client), createPageFn)
166}
Jamie Hannaford821015f2015-02-10 12:58:36 +0100167
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100168// Get retrieves the status and information for a specified database instance.
Jamie Hannaford821015f2015-02-10 12:58:36 +0100169func Get(client *gophercloud.ServiceClient, id string) GetResult {
170 var res GetResult
171
172 resp, err := perigee.Request("GET", resourceURL(client, id), perigee.Options{
173 MoreHeaders: client.AuthenticatedHeaders(),
174 Results: &res.Body,
175 OkCodes: []int{200},
176 })
177
178 res.Header = resp.HttpResponse.Header
179 res.Err = err
180
181 return res
182}
Jamie Hannaford5b16b632015-02-10 13:36:23 +0100183
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100184// Delete permanently destroys the database instance.
Jamie Hannaford5b16b632015-02-10 13:36:23 +0100185func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
186 var res DeleteResult
187
188 resp, err := perigee.Request("DELETE", resourceURL(client, id), perigee.Options{
189 MoreHeaders: client.AuthenticatedHeaders(),
190 OkCodes: []int{202},
191 })
192
193 res.Header = resp.HttpResponse.Header
194 res.Err = err
195
196 return res
197}
Jamie Hannaford94164fa2015-02-10 13:58:45 +0100198
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100199// EnableRootUser enables the login from any host for the root user and
200// provides the user with a generated root password.
Jamie Hannaford94164fa2015-02-10 13:58:45 +0100201func EnableRootUser(client *gophercloud.ServiceClient, id string) UserRootResult {
202 var res UserRootResult
203
204 resp, err := perigee.Request("POST", userRootURL(client, id), perigee.Options{
205 MoreHeaders: client.AuthenticatedHeaders(),
206 Results: &res.Body,
207 OkCodes: []int{200},
208 })
209
210 res.Header = resp.HttpResponse.Header
211 res.Err = err
212
213 return res
214}
Jamie Hannaforda74d4252015-02-10 15:35:01 +0100215
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100216// IsRootEnabled checks an instance to see if root access is enabled. It returns
217// True if root user is enabled for the specified database instance or False
218// otherwise.
Jamie Hannaforda74d4252015-02-10 15:35:01 +0100219func IsRootEnabled(client *gophercloud.ServiceClient, id string) (bool, error) {
220 var res gophercloud.Result
221
222 _, err := perigee.Request("GET", userRootURL(client, id), perigee.Options{
223 MoreHeaders: client.AuthenticatedHeaders(),
224 Results: &res.Body,
225 OkCodes: []int{200},
226 })
227
228 return res.Body.(map[string]interface{})["rootEnabled"] == true, err
229}
Jamie Hannaford219ca592015-02-10 15:59:05 +0100230
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100231// RestartService will restart only the MySQL Instance. Restarting MySQL will
232// erase any dynamic configuration settings that you have made within MySQL.
233// The MySQL service will be unavailable until the instance restarts.
Jamie Hannaford219ca592015-02-10 15:59:05 +0100234func RestartService(client *gophercloud.ServiceClient, id string) ActionResult {
235 var res ActionResult
236
237 resp, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
238 MoreHeaders: client.AuthenticatedHeaders(),
239 ReqBody: map[string]bool{"restart": true},
240 OkCodes: []int{202},
241 })
242
243 res.Header = resp.HttpResponse.Header
244 res.Err = err
245
246 return res
247}
248
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100249// ResizeInstance changes the memory size of the instance, assuming a valid
250// flavorRef is provided. It will also restart the MySQL service.
Jamie Hannaford219ca592015-02-10 15:59:05 +0100251func ResizeInstance(client *gophercloud.ServiceClient, id, flavorRef string) ActionResult {
252 var res ActionResult
253
254 reqBody := map[string]map[string]string{
255 "resize": map[string]string{
256 "flavorRef": flavorRef,
257 },
258 }
259
260 resp, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
261 MoreHeaders: client.AuthenticatedHeaders(),
262 ReqBody: reqBody,
263 OkCodes: []int{202},
264 })
265
266 res.Header = resp.HttpResponse.Header
267 res.Err = err
268
269 return res
270}
271
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100272// ResizeVolume will resize the attached volume for an instance. It supports
273// only increasing the volume size and does not support decreasing the size.
274// The volume size is in gigabytes (GB) and must be an integer.
Jamie Hannaford219ca592015-02-10 15:59:05 +0100275func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) ActionResult {
276 var res ActionResult
277
278 reqBody := map[string]map[string]map[string]int{
279 "resize": map[string]map[string]int{
280 "volume": map[string]int{"size": size},
281 },
282 }
283
284 resp, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
285 MoreHeaders: client.AuthenticatedHeaders(),
286 ReqBody: reqBody,
287 OkCodes: []int{202},
288 })
289
290 res.Header = resp.HttpResponse.Header
291 res.Err = err
292
293 return res
294}