1package instances
2
3import (
4	"github.com/gophercloud/gophercloud"
5	db "github.com/gophercloud/gophercloud/openstack/db/v1/databases"
6	"github.com/gophercloud/gophercloud/openstack/db/v1/users"
7	"github.com/gophercloud/gophercloud/pagination"
8)
9
10// CreateOptsBuilder is the top-level interface for create options.
11type CreateOptsBuilder interface {
12	ToInstanceCreateMap() (map[string]interface{}, error)
13}
14
15// DatastoreOpts represents the configuration for how an instance stores data.
16type DatastoreOpts struct {
17	Version string `json:"version"`
18	Type    string `json:"type"`
19}
20
21// ToMap converts a DatastoreOpts to a map[string]string (for a request body)
22func (opts DatastoreOpts) ToMap() (map[string]interface{}, error) {
23	return gophercloud.BuildRequestBody(opts, "")
24}
25
26// NetworkOpts is used within CreateOpts to control a new server's network attachments.
27type NetworkOpts struct {
28	// UUID of a nova-network to attach to the newly provisioned server.
29	// Required unless Port is provided.
30	UUID string `json:"net-id,omitempty"`
31
32	// Port of a neutron network to attach to the newly provisioned server.
33	// Required unless UUID is provided.
34	Port string `json:"port-id,omitempty"`
35
36	// V4FixedIP [optional] specifies a fixed IPv4 address to be used on this network.
37	V4FixedIP string `json:"v4-fixed-ip,omitempty"`
38
39	// V6FixedIP [optional] specifies a fixed IPv6 address to be used on this network.
40	V6FixedIP string `json:"v6-fixed-ip,omitempty"`
41}
42
43// ToMap converts a NetworkOpts to a map[string]string (for a request body)
44func (opts NetworkOpts) ToMap() (map[string]interface{}, error) {
45	return gophercloud.BuildRequestBody(opts, "")
46}
47
48// CreateOpts is the struct responsible for configuring a new database instance.
49type CreateOpts struct {
50	// Either the integer UUID (in string form) of the flavor, or its URI
51	// reference as specified in the response from the List() call. Required.
52	FlavorRef string
53	// Specifies the volume size in gigabytes (GB). The value must be between 1
54	// and 300. Required.
55	Size int
56	// Name of the instance to create. The length of the name is limited to
57	// 255 characters and any characters are permitted. Optional.
58	Name string
59	// A slice of database information options.
60	Databases db.CreateOptsBuilder
61	// A slice of user information options.
62	Users users.CreateOptsBuilder
63	// Options to configure the type of datastore the instance will use. This is
64	// optional, and if excluded will default to MySQL.
65	Datastore *DatastoreOpts
66	// Networks dictates how this server will be attached to available networks.
67	Networks []NetworkOpts
68}
69
70// ToInstanceCreateMap will render a JSON map.
71func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) {
72	if opts.Size > 300 || opts.Size < 1 {
73		err := gophercloud.ErrInvalidInput{}
74		err.Argument = "instances.CreateOpts.Size"
75		err.Value = opts.Size
76		err.Info = "Size (GB) must be between 1-300"
77		return nil, err
78	}
79
80	if opts.FlavorRef == "" {
81		return nil, gophercloud.ErrMissingInput{Argument: "instances.CreateOpts.FlavorRef"}
82	}
83
84	instance := map[string]interface{}{
85		"volume":    map[string]int{"size": opts.Size},
86		"flavorRef": opts.FlavorRef,
87	}
88
89	if opts.Name != "" {
90		instance["name"] = opts.Name
91	}
92	if opts.Databases != nil {
93		dbs, err := opts.Databases.ToDBCreateMap()
94		if err != nil {
95			return nil, err
96		}
97		instance["databases"] = dbs["databases"]
98	}
99	if opts.Users != nil {
100		users, err := opts.Users.ToUserCreateMap()
101		if err != nil {
102			return nil, err
103		}
104		instance["users"] = users["users"]
105	}
106	if opts.Datastore != nil {
107		datastore, err := opts.Datastore.ToMap()
108		if err != nil {
109			return nil, err
110		}
111		instance["datastore"] = datastore
112	}
113
114	if len(opts.Networks) > 0 {
115		networks := make([]map[string]interface{}, len(opts.Networks))
116		for i, net := range opts.Networks {
117			var err error
118			networks[i], err = net.ToMap()
119			if err != nil {
120				return nil, err
121			}
122		}
123		instance["nics"] = networks
124	}
125
126	return map[string]interface{}{"instance": instance}, nil
127}
128
129// 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.
137func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
138	b, err := opts.ToInstanceCreateMap()
139	if err != nil {
140		r.Err = err
141		return
142	}
143	_, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
144	return
145}
146
147// List retrieves the status and information for all database instances.
148func List(client *gophercloud.ServiceClient) pagination.Pager {
149	return pagination.NewPager(client, baseURL(client), func(r pagination.PageResult) pagination.Page {
150		return InstancePage{pagination.LinkedPageBase{PageResult: r}}
151	})
152}
153
154// Get retrieves the status and information for a specified database instance.
155func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
156	_, r.Err = client.Get(resourceURL(client, id), &r.Body, nil)
157	return
158}
159
160// Delete permanently destroys the database instance.
161func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
162	_, r.Err = client.Delete(resourceURL(client, id), nil)
163	return
164}
165
166// EnableRootUser enables the login from any host for the root user and
167// provides the user with a generated root password.
168func EnableRootUser(client *gophercloud.ServiceClient, id string) (r EnableRootUserResult) {
169	_, r.Err = client.Post(userRootURL(client, id), nil, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
170	return
171}
172
173// IsRootEnabled checks an instance to see if root access is enabled. It returns
174// True if root user is enabled for the specified database instance or False
175// otherwise.
176func IsRootEnabled(client *gophercloud.ServiceClient, id string) (r IsRootEnabledResult) {
177	_, r.Err = client.Get(userRootURL(client, id), &r.Body, nil)
178	return
179}
180
181// Restart will restart only the MySQL Instance. Restarting MySQL will
182// erase any dynamic configuration settings that you have made within MySQL.
183// The MySQL service will be unavailable until the instance restarts.
184func Restart(client *gophercloud.ServiceClient, id string) (r ActionResult) {
185	b := map[string]interface{}{"restart": struct{}{}}
186	_, r.Err = client.Post(actionURL(client, id), &b, nil, nil)
187	return
188}
189
190// Resize changes the memory size of the instance, assuming a valid
191// flavorRef is provided. It will also restart the MySQL service.
192func Resize(client *gophercloud.ServiceClient, id, flavorRef string) (r ActionResult) {
193	b := map[string]interface{}{"resize": map[string]string{"flavorRef": flavorRef}}
194	_, r.Err = client.Post(actionURL(client, id), &b, nil, nil)
195	return
196}
197
198// ResizeVolume will resize the attached volume for an instance. It supports
199// only increasing the volume size and does not support decreasing the size.
200// The volume size is in gigabytes (GB) and must be an integer.
201func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) (r ActionResult) {
202	b := map[string]interface{}{"resize": map[string]interface{}{"volume": map[string]int{"size": size}}}
203	_, r.Err = client.Post(actionURL(client, id), &b, nil, nil)
204	return
205}
206
207// AttachConfigurationGroup will attach configuration group to the instance
208func AttachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string, configID string) (r ConfigurationResult) {
209	b := map[string]interface{}{"instance": map[string]interface{}{"configuration": configID}}
210	_, r.Err = client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}})
211	return
212}
213
214// DetachConfigurationGroup will dettach configuration group from the instance
215func DetachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string) (r ConfigurationResult) {
216	b := map[string]interface{}{"instance": map[string]interface{}{}}
217	_, r.Err = client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}})
218	return
219}
220