create flavor (#257)
* create flavor operation
* accept 200,201 status codes
* unit test
diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go
index ef133ff..d5d571c 100644
--- a/openstack/compute/v2/flavors/requests.go
+++ b/openstack/compute/v2/flavors/requests.go
@@ -54,6 +54,47 @@
})
}
+type CreateOptsBuilder interface {
+ ToFlavorCreateMap() (map[string]interface{}, error)
+}
+
+// CreateOpts is passed to Create to create a flavor
+// Source:
+// https://github.com/openstack/nova/blob/stable/newton/nova/api/openstack/compute/schemas/flavor_manage.py#L20
+type CreateOpts struct {
+ Name string `json:"name" required:"true"`
+ // memory size, in MBs
+ RAM int `json:"ram" required:"true"`
+ VCPUs int `json:"vcpus" required:"true"`
+ // disk size, in GBs
+ Disk *int `json:"disk" required:"true"`
+ ID string `json:"id,omitempty"`
+ // non-zero, positive
+ Swap *int `json:"swap,omitempty"`
+ RxTxFactor float64 `json:"rxtx_factor,omitempty"`
+ IsPublic *bool `json:"os-flavor-access:is_public,omitempty"`
+ // ephemeral disk size, in GBs, non-zero, positive
+ Ephemeral *int `json:"OS-FLV-EXT-DATA:ephemeral,omitempty"`
+}
+
+// ToFlavorCreateMap satisfies the CreateOptsBuilder interface
+func (opts *CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) {
+ return gophercloud.BuildRequestBody(opts, "flavor")
+}
+
+// Create a flavor
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
+ b, err := opts.ToFlavorCreateMap()
+ if err != nil {
+ r.Err = err
+ return
+ }
+ _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
+ OkCodes: []int{200, 201},
+ })
+ return
+}
+
// Get instructs OpenStack to provide details on a single flavor, identified by its ID.
// Use ExtractFlavor to convert its result into a Flavor.
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go
index a49de0d..18b8434 100644
--- a/openstack/compute/v2/flavors/results.go
+++ b/openstack/compute/v2/flavors/results.go
@@ -8,13 +8,21 @@
"github.com/gophercloud/gophercloud/pagination"
)
-// GetResult temporarily holds the response from a Get call.
-type GetResult struct {
+type commonResult struct {
gophercloud.Result
}
-// Extract provides access to the individual Flavor returned by the Get function.
-func (r GetResult) Extract() (*Flavor, error) {
+type CreateResult struct {
+ commonResult
+}
+
+// GetResult temporarily holds the response from a Get call.
+type GetResult struct {
+ commonResult
+}
+
+// Extract provides access to the individual Flavor returned by the Get and Create functions.
+func (r commonResult) Extract() (*Flavor, error) {
var s struct {
Flavor *Flavor `json:"flavor"`
}
@@ -40,41 +48,32 @@
VCPUs int `json:"vcpus"`
}
-func (f *Flavor) UnmarshalJSON(b []byte) error {
- var flavor struct {
- ID string `json:"id"`
- Disk int `json:"disk"`
- RAM int `json:"ram"`
- Name string `json:"name"`
- RxTxFactor float64 `json:"rxtx_factor"`
- Swap interface{} `json:"swap"`
- VCPUs int `json:"vcpus"`
+func (r *Flavor) UnmarshalJSON(b []byte) error {
+ type tmp Flavor
+ var s struct {
+ tmp
+ Swap interface{} `json:"swap"`
}
- err := json.Unmarshal(b, &flavor)
+ err := json.Unmarshal(b, &s)
if err != nil {
return err
}
- f.ID = flavor.ID
- f.Disk = flavor.Disk
- f.RAM = flavor.RAM
- f.Name = flavor.Name
- f.RxTxFactor = flavor.RxTxFactor
- f.VCPUs = flavor.VCPUs
+ *r = Flavor(s.tmp)
- switch t := flavor.Swap.(type) {
+ switch t := s.Swap.(type) {
case float64:
- f.Swap = int(t)
+ r.Swap = int(t)
case string:
switch t {
case "":
- f.Swap = 0
+ r.Swap = 0
default:
swap, err := strconv.ParseFloat(t, 64)
if err != nil {
return err
}
- f.Swap = int(swap)
+ r.Swap = int(swap)
}
}
diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go
index 1b96933..f4f1c8d 100644
--- a/openstack/compute/v2/flavors/testing/requests_test.go
+++ b/openstack/compute/v2/flavors/testing/requests_test.go
@@ -132,3 +132,55 @@
t.Errorf("Expected %#v, but was %#v", expected, actual)
}
}
+
+func TestCreateFlavor(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "POST")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+ w.Header().Add("Content-Type", "application/json")
+ fmt.Fprintf(w, `
+ {
+ "flavor": {
+ "id": "1",
+ "name": "m1.tiny",
+ "disk": 1,
+ "ram": 512,
+ "vcpus": 1,
+ "rxtx_factor": 1,
+ "swap": ""
+ }
+ }
+ `)
+ })
+
+ disk := 1
+ opts := &flavors.CreateOpts{
+ ID: "1",
+ Name: "m1.tiny",
+ Disk: &disk,
+ RAM: 512,
+ VCPUs: 1,
+ RxTxFactor: 1.0,
+ }
+ actual, err := flavors.Create(fake.ServiceClient(), opts).Extract()
+ if err != nil {
+ t.Fatalf("Unable to create flavor: %v", err)
+ }
+
+ expected := &flavors.Flavor{
+ ID: "1",
+ Name: "m1.tiny",
+ Disk: 1,
+ RAM: 512,
+ VCPUs: 1,
+ RxTxFactor: 1,
+ Swap: 0,
+ }
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Expected %#v, but was %#v", expected, actual)
+ }
+}
diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go
index ee0dfdb..2fc2179 100644
--- a/openstack/compute/v2/flavors/urls.go
+++ b/openstack/compute/v2/flavors/urls.go
@@ -11,3 +11,7 @@
func listURL(client *gophercloud.ServiceClient) string {
return client.ServiceURL("flavors", "detail")
}
+
+func createURL(client *gophercloud.ServiceClient) string {
+ return client.ServiceURL("flavors")
+}