blob: ecf8d468348b2af3dce790c89360f1930772d6f9 [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 Hannaford2ca55d82015-02-12 14:21:55 +01009 users "github.com/rackspace/gophercloud/openstack/db/v1/databases"
Jamie Hannaford90684242015-02-10 12:46:07 +010010 "github.com/rackspace/gophercloud/pagination"
Jamie Hannaford9fdda582015-02-10 12:15:43 +010011)
12
13// CreateOptsBuilder is the top-level interface for create options.
14type CreateOptsBuilder interface {
15 ToInstanceCreateMap() (map[string]interface{}, error)
16}
17
Jamie Hannaford9fdda582015-02-10 12:15:43 +010018// CreateOpts is the struct responsible for configuring a new database instance.
19type CreateOpts struct {
20 // Either the integer UUID (in string form) of the flavor, or its URI
21 // reference as specified in the response from the List() call. Required.
22 FlavorRef string
23
24 // Specifies the volume size in gigabytes (GB). The value must be between 1
25 // and 300. Required.
26 Size int
27
28 // Name of the instance to create. The length of the name is limited to
29 // 255 characters and any characters are permitted. Optional.
30 Name string
31
32 // A slice of database information options.
Jamie Hannaford85f10332015-02-12 11:51:37 +010033 Databases db.BatchCreateOpts
Jamie Hannaford9fdda582015-02-10 12:15:43 +010034
35 // A slice of user information options.
Jamie Hannaford2ca55d82015-02-12 14:21:55 +010036 Users users.BatchCreateOpts
Jamie Hannaford9fdda582015-02-10 12:15:43 +010037}
38
39func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) {
40 if opts.Size > 300 || opts.Size < 1 {
41 return nil, fmt.Errorf("Size (GB) must be between 1-300")
42 }
43 if opts.FlavorRef == "" {
44 return nil, fmt.Errorf("FlavorRef is a required field")
45 }
46
47 instance := map[string]interface{}{
48 "volume": map[string]int{"size": opts.Size},
49 "flavorRef": opts.FlavorRef,
50 }
51
52 if opts.Name != "" {
53 instance["name"] = opts.Name
54 }
55 if len(opts.Databases) > 0 {
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +010056 dbs, err := opts.Databases.ToDBCreateMap()
Jamie Hannaford9fdda582015-02-10 12:15:43 +010057 if err != nil {
58 return nil, err
59 }
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +010060 instance["databases"] = dbs["databases"]
Jamie Hannaford9fdda582015-02-10 12:15:43 +010061 }
62 if len(opts.Users) > 0 {
Jamie Hannaford2ca55d82015-02-12 14:21:55 +010063 users, err := opts.Users.ToUserCreateMap()
Jamie Hannaford9fdda582015-02-10 12:15:43 +010064 if err != nil {
65 return nil, err
66 }
Jamie Hannaford2ca55d82015-02-12 14:21:55 +010067 instance["users"] = users["users"]
Jamie Hannaford9fdda582015-02-10 12:15:43 +010068 }
69
70 return map[string]interface{}{"instance": instance}, nil
71}
72
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +010073// Create asynchronously provisions a new database instance. It requires the
74// user to specify a flavor and a volume size. The API service then provisions
75// the instance with the requested flavor and sets up a volume of the specified
76// size, which is the storage for the database instance.
77//
78// Although this call only allows the creation of 1 instance per request, you
79// can create an instance with multiple databases and users. The default
80// binding for a MySQL instance is port 3306.
Jamie Hannaford9fdda582015-02-10 12:15:43 +010081func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
82 var res CreateResult
83
84 reqBody, err := opts.ToInstanceCreateMap()
85 if err != nil {
86 res.Err = err
87 return res
88 }
89
Jamie Hannaford90684242015-02-10 12:46:07 +010090 resp, err := perigee.Request("POST", baseURL(client), perigee.Options{
Jamie Hannaford9fdda582015-02-10 12:15:43 +010091 MoreHeaders: client.AuthenticatedHeaders(),
92 ReqBody: &reqBody,
93 Results: &res.Body,
94 OkCodes: []int{200},
95 })
96
97 res.Header = resp.HttpResponse.Header
98 res.Err = err
99
100 return res
101}
Jamie Hannaford90684242015-02-10 12:46:07 +0100102
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100103// List retrieves the status and information for all database instances.
Jamie Hannaford90684242015-02-10 12:46:07 +0100104func List(client *gophercloud.ServiceClient) pagination.Pager {
105 createPageFn := func(r pagination.PageResult) pagination.Page {
106 return InstancePage{pagination.LinkedPageBase{PageResult: r}}
107 }
108
109 return pagination.NewPager(client, baseURL(client), createPageFn)
110}
Jamie Hannaford821015f2015-02-10 12:58:36 +0100111
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100112// Get retrieves the status and information for a specified database instance.
Jamie Hannaford821015f2015-02-10 12:58:36 +0100113func Get(client *gophercloud.ServiceClient, id string) GetResult {
114 var res GetResult
115
116 resp, err := perigee.Request("GET", resourceURL(client, id), perigee.Options{
117 MoreHeaders: client.AuthenticatedHeaders(),
118 Results: &res.Body,
119 OkCodes: []int{200},
120 })
121
122 res.Header = resp.HttpResponse.Header
123 res.Err = err
124
125 return res
126}
Jamie Hannaford5b16b632015-02-10 13:36:23 +0100127
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100128// Delete permanently destroys the database instance.
Jamie Hannaford5b16b632015-02-10 13:36:23 +0100129func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
130 var res DeleteResult
131
132 resp, err := perigee.Request("DELETE", resourceURL(client, id), perigee.Options{
133 MoreHeaders: client.AuthenticatedHeaders(),
134 OkCodes: []int{202},
135 })
136
137 res.Header = resp.HttpResponse.Header
138 res.Err = err
139
140 return res
141}
Jamie Hannaford94164fa2015-02-10 13:58:45 +0100142
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100143// EnableRootUser enables the login from any host for the root user and
144// provides the user with a generated root password.
Jamie Hannaford94164fa2015-02-10 13:58:45 +0100145func EnableRootUser(client *gophercloud.ServiceClient, id string) UserRootResult {
146 var res UserRootResult
147
148 resp, err := perigee.Request("POST", userRootURL(client, id), perigee.Options{
149 MoreHeaders: client.AuthenticatedHeaders(),
150 Results: &res.Body,
151 OkCodes: []int{200},
152 })
153
154 res.Header = resp.HttpResponse.Header
155 res.Err = err
156
157 return res
158}
Jamie Hannaforda74d4252015-02-10 15:35:01 +0100159
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100160// IsRootEnabled checks an instance to see if root access is enabled. It returns
161// True if root user is enabled for the specified database instance or False
162// otherwise.
Jamie Hannaforda74d4252015-02-10 15:35:01 +0100163func IsRootEnabled(client *gophercloud.ServiceClient, id string) (bool, error) {
164 var res gophercloud.Result
165
166 _, err := perigee.Request("GET", userRootURL(client, id), perigee.Options{
167 MoreHeaders: client.AuthenticatedHeaders(),
168 Results: &res.Body,
169 OkCodes: []int{200},
170 })
171
172 return res.Body.(map[string]interface{})["rootEnabled"] == true, err
173}
Jamie Hannaford219ca592015-02-10 15:59:05 +0100174
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100175// RestartService will restart only the MySQL Instance. Restarting MySQL will
176// erase any dynamic configuration settings that you have made within MySQL.
177// The MySQL service will be unavailable until the instance restarts.
Jamie Hannaford219ca592015-02-10 15:59:05 +0100178func RestartService(client *gophercloud.ServiceClient, id string) ActionResult {
179 var res ActionResult
180
181 resp, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
182 MoreHeaders: client.AuthenticatedHeaders(),
183 ReqBody: map[string]bool{"restart": true},
184 OkCodes: []int{202},
185 })
186
187 res.Header = resp.HttpResponse.Header
188 res.Err = err
189
190 return res
191}
192
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100193// ResizeInstance changes the memory size of the instance, assuming a valid
194// flavorRef is provided. It will also restart the MySQL service.
Jamie Hannaford219ca592015-02-10 15:59:05 +0100195func ResizeInstance(client *gophercloud.ServiceClient, id, flavorRef string) ActionResult {
196 var res ActionResult
197
198 reqBody := map[string]map[string]string{
199 "resize": map[string]string{
200 "flavorRef": flavorRef,
201 },
202 }
203
204 resp, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
205 MoreHeaders: client.AuthenticatedHeaders(),
206 ReqBody: reqBody,
207 OkCodes: []int{202},
208 })
209
210 res.Header = resp.HttpResponse.Header
211 res.Err = err
212
213 return res
214}
215
Jamie Hannaford56d0c2e2015-02-12 11:50:18 +0100216// ResizeVolume will resize the attached volume for an instance. It supports
217// only increasing the volume size and does not support decreasing the size.
218// The volume size is in gigabytes (GB) and must be an integer.
Jamie Hannaford219ca592015-02-10 15:59:05 +0100219func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) ActionResult {
220 var res ActionResult
221
222 reqBody := map[string]map[string]map[string]int{
223 "resize": map[string]map[string]int{
224 "volume": map[string]int{"size": size},
225 },
226 }
227
228 resp, err := perigee.Request("POST", actionURL(client, id), perigee.Options{
229 MoreHeaders: client.AuthenticatedHeaders(),
230 ReqBody: reqBody,
231 OkCodes: []int{202},
232 })
233
234 res.Header = resp.HttpResponse.Header
235 res.Err = err
236
237 return res
238}