From 2e831e9b459599a3b0bbf23491b5e0e0583d220c Mon Sep 17 00:00:00 2001 From: disha-wani Date: Fri, 20 Jul 2018 13:31:02 +0530 Subject: [PATCH 1/4] add BMS service --- openstack/bms/v2/common/common_tests.go | 17 ++ openstack/bms/v2/flavors/doc.go | 17 ++ openstack/bms/v2/flavors/requests.go | 124 +++++++++++++ openstack/bms/v2/flavors/results.go | 123 +++++++++++++ .../bms/v2/flavors/testing/requests_test.go | 128 +++++++++++++ openstack/bms/v2/flavors/urls.go | 9 + openstack/bms/v2/keypairs/doc.go | 15 ++ openstack/bms/v2/keypairs/requests.go | 68 +++++++ openstack/bms/v2/keypairs/results.go | 65 +++++++ openstack/bms/v2/keypairs/testing/fixtures.go | 23 +++ .../bms/v2/keypairs/testing/requests_test.go | 44 +++++ openstack/bms/v2/keypairs/urls.go | 11 ++ openstack/bms/v2/nics/doc.go | 17 ++ openstack/bms/v2/nics/requests.go | 81 +++++++++ openstack/bms/v2/nics/results.go | 86 +++++++++ .../bms/v2/nics/testing/requests_test.go | 120 +++++++++++++ openstack/bms/v2/nics/urls.go | 13 ++ openstack/bms/v2/servers/doc.go | 17 ++ openstack/bms/v2/servers/requests.go | 168 ++++++++++++++++++ openstack/bms/v2/servers/results.go | 163 +++++++++++++++++ .../bms/v2/servers/testing/requests_test.go | 131 ++++++++++++++ openstack/bms/v2/servers/urls.go | 13 ++ openstack/bms/v2/tags/doc.go | 25 +++ openstack/bms/v2/tags/requests.go | 48 +++++ openstack/bms/v2/tags/results.go | 39 ++++ .../bms/v2/tags/testing/requests_test.go | 87 +++++++++ openstack/bms/v2/tags/urls.go | 9 + 27 files changed, 1661 insertions(+) create mode 100644 openstack/bms/v2/common/common_tests.go create mode 100644 openstack/bms/v2/flavors/doc.go create mode 100644 openstack/bms/v2/flavors/requests.go create mode 100644 openstack/bms/v2/flavors/results.go create mode 100644 openstack/bms/v2/flavors/testing/requests_test.go create mode 100644 openstack/bms/v2/flavors/urls.go create mode 100644 openstack/bms/v2/keypairs/doc.go create mode 100644 openstack/bms/v2/keypairs/requests.go create mode 100644 openstack/bms/v2/keypairs/results.go create mode 100644 openstack/bms/v2/keypairs/testing/fixtures.go create mode 100644 openstack/bms/v2/keypairs/testing/requests_test.go create mode 100644 openstack/bms/v2/keypairs/urls.go create mode 100644 openstack/bms/v2/nics/doc.go create mode 100644 openstack/bms/v2/nics/requests.go create mode 100644 openstack/bms/v2/nics/results.go create mode 100644 openstack/bms/v2/nics/testing/requests_test.go create mode 100644 openstack/bms/v2/nics/urls.go create mode 100644 openstack/bms/v2/servers/doc.go create mode 100644 openstack/bms/v2/servers/requests.go create mode 100644 openstack/bms/v2/servers/results.go create mode 100644 openstack/bms/v2/servers/testing/requests_test.go create mode 100644 openstack/bms/v2/servers/urls.go create mode 100644 openstack/bms/v2/tags/doc.go create mode 100644 openstack/bms/v2/tags/requests.go create mode 100644 openstack/bms/v2/tags/results.go create mode 100644 openstack/bms/v2/tags/testing/requests_test.go create mode 100644 openstack/bms/v2/tags/urls.go diff --git a/openstack/bms/v2/common/common_tests.go b/openstack/bms/v2/common/common_tests.go new file mode 100644 index 000000000..287397023 --- /dev/null +++ b/openstack/bms/v2/common/common_tests.go @@ -0,0 +1,17 @@ +package common + +import ( + "strings" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/testhelper/client" +) + +const TokenID = client.TokenID + +func ServiceClient() *golangsdk.ServiceClient { + sc := client.ServiceClient() + e := strings.Replace(sc.Endpoint, "v2", "v2.1", 1) + sc.ResourceBase = e + return sc +} diff --git a/openstack/bms/v2/flavors/doc.go b/openstack/bms/v2/flavors/doc.go new file mode 100644 index 000000000..2dfe37ded --- /dev/null +++ b/openstack/bms/v2/flavors/doc.go @@ -0,0 +1,17 @@ +/* +Package flavors enables management and retrieval of Flavors +BMS service. + +Example to List Flavors + + listOpts := flavors.ListOpts{} + allFlavors, err := flavors.List(bmsClient, listOpts) + if err != nil { + panic(err) + } + + for _, flavor := range allFlavors { + fmt.Printf("%+v\n", flavor) + } +*/ +package flavors diff --git a/openstack/bms/v2/flavors/requests.go b/openstack/bms/v2/flavors/requests.go new file mode 100644 index 000000000..0b7c3e119 --- /dev/null +++ b/openstack/bms/v2/flavors/requests.go @@ -0,0 +1,124 @@ +package flavors + +import ( + "reflect" + "strings" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToFlavorListQuery() (string, error) +} + +//The AccessType arguement is optional, and if it is not supplied, OpenStack +//returns the PublicAccess flavors. +type AccessType string + +const ( + // PublicAccess returns public flavors and private flavors associated with + // that project. + PublicAccess AccessType = "true" + + // PrivateAccess (admin only) returns private flavors, across all projects. + PrivateAccess AccessType = "false" + + // AllAccess (admin only) returns public and private flavors across all + // projects. + AllAccess AccessType = "None" +) + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the flavor attributes you want to see returned. +type ListOpts struct { + //Specifies the name of the BMS flavor + Name string + + //Specifies the ID of the BMS flavor + ID string + + // MinDisk and MinRAM, if provided, elides flavors which do not meet your + // criteria. + MinDisk int `q:"minDisk"` + + MinRAM int `q:"minRam"` + + // AccessType, if provided, instructs List which set of flavors to return. + // If IsPublic not provided, flavors for the current project are returned. + AccessType AccessType `q:"is_public"` + + //SortKey allows you to sort by a particular attribute + SortKey string `q:"sort_key"` + + //SortDir sets the direction, and is either `asc' or `desc' + SortDir string `q:"sort_dir"` +} + +func List(c *golangsdk.ServiceClient, opts ListOpts) ([]Flavor, error) { + q, err := golangsdk.BuildQueryString(&opts) + if err != nil { + return nil, err + } + u := listURL(c) + q.String() + pages, err := pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { + return FlavorPage{pagination.LinkedPageBase{PageResult: r}} + }).AllPages() + + allFlavors, err := ExtractFlavors(pages) + if err != nil { + return nil, err + } + + return FilterFlavors(allFlavors, opts) +} + +//FilterFlavors used to filter flavors using Id and Name +func FilterFlavors(flavors []Flavor, opts ListOpts) ([]Flavor, error) { + + var refinedFlavors []Flavor + var matched bool + m := map[string]interface{}{} + + if opts.ID != "" { + m["ID"] = opts.ID + } + if opts.Name != "" { + m["Name"] = opts.Name + } + if len(m) > 0 && len(flavors) > 0 { + for _, flavor := range flavors { + matched = true + + for key, value := range m { + if sVal := getStructField(&flavor, key); !(sVal == value) { + matched = false + } + } + if matched { + refinedFlavors = append(refinedFlavors, flavor) + } + } + } else { + refinedFlavors = flavors + } + var flavorList []Flavor + + for i := 0; i < len(refinedFlavors); i++ { + if strings.Contains(refinedFlavors[i].Name, "physical") { + flavorList = append(flavorList, refinedFlavors[i]) + } + + } + + return flavorList, nil +} + +func getStructField(v *Flavor, field string) string { + r := reflect.ValueOf(v) + f := reflect.Indirect(r).FieldByName(field) + return string(f.String()) +} diff --git a/openstack/bms/v2/flavors/results.go b/openstack/bms/v2/flavors/results.go new file mode 100644 index 000000000..e1b83e90f --- /dev/null +++ b/openstack/bms/v2/flavors/results.go @@ -0,0 +1,123 @@ +package flavors + +import ( + "encoding/json" + "strconv" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +// Flavor represent (virtual) hardware configurations for server resources +// in a region. +type Flavor struct { + // ID is the flavor's unique ID. + ID string `json:"id"` + + // Disk is the amount of root disk, measured in GB. + Disk int `json:"disk"` + + // MinDisk and MinRAM, if provided, elides flavors which do not meet your + // criteria. + MinDisk int `json:"minDisk"` + + MinRAM int `json:"minRam"` + + // RAM is the amount of memory, measured in MB. + RAM int `json:"ram"` + + // Name is the name of the flavor. + Name string `json:"name"` + + // RxTxFactor describes bandwidth alterations of the flavor. + RxTxFactor float64 `json:"rxtx_factor"` + + // Swap is the amount of swap space, measured in MB. + Swap int `json:"swap"` + + // VCPUs indicates how many (virtual) CPUs are available for this flavor. + VCPUs int `json:"vcpus"` + + // IsPublic indicates whether the flavor is public. + IsPublic bool `json:"os-flavor-access:is_public"` + + // Ephemeral is the amount of ephemeral disk space, measured in GB. + Ephemeral int `json:"OS-FLV-EXT-DATA:ephemeral"` + + // Whether or not the flavor has been administratively disabled + Disabled bool `json:"OS-FLV-DISABLED:disabled"` + + // Specifies the shortcut link of the BMS flavor. + Links []golangsdk.Link `json:"links"` + + SortKey string `json:"sort_key"` + + //SortDir sets the direction, and is either `asc' or `desc' + SortDir string `json:"sort_dir"` +} + +func (r *Flavor) UnmarshalJSON(b []byte) error { + type tmp Flavor + var s struct { + tmp + Swap interface{} `json:"swap"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = Flavor(s.tmp) + + switch t := s.Swap.(type) { + case float64: + r.Swap = int(t) + case string: + switch t { + case "": + r.Swap = 0 + default: + swap, err := strconv.ParseFloat(t, 64) + if err != nil { + return err + } + r.Swap = int(swap) + } + } + + return nil +} + +// FlavorPage contains a single page of all flavors from a List call. +type FlavorPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a FlavorPage contains any results. +func (page FlavorPage) IsEmpty() (bool, error) { + flavors, err := ExtractFlavors(page) + return len(flavors) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (page FlavorPage) NextPageURL() (string, error) { + var s struct { + Links []golangsdk.Link `json:"flavors_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return golangsdk.ExtractNextURL(s.Links) +} + +// ExtractFlavors provides access to the list of flavors in a page acquired +// from the List operation. +func ExtractFlavors(r pagination.Page) ([]Flavor, error) { + var s struct { + Flavors []Flavor `json:"flavors"` + } + err := (r.(FlavorPage)).ExtractInto(&s) + return s.Flavors, err +} diff --git a/openstack/bms/v2/flavors/testing/requests_test.go b/openstack/bms/v2/flavors/testing/requests_test.go new file mode 100644 index 000000000..d85e52b48 --- /dev/null +++ b/openstack/bms/v2/flavors/testing/requests_test.go @@ -0,0 +1,128 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/openstack/bms/v2/flavors" + th "github.com/huaweicloud/golangsdk/testhelper" + fake "github.com/huaweicloud/golangsdk/testhelper/client" +) + +func TestListFlavor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/flavors/detail", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "flavors": [ + { + "name": "physical.h2.large", + "links": [ + { + "href": "https://compute.region.eu-de.otc-tsi.de/v2/91d687759aed45d28b5f6084bc2fa8ad/flavors/physical.h2.large", + "rel": "self" + }, + { + "href": "https://compute.region.eu-de.otc-tsi.de/91d687759aed45d28b5f6084bc2fa8ad/flavors/physical.h2.large", + "rel": "bookmark" + } + ], + "ram": 196608, + "OS-FLV-DISABLED:disabled": false, + "vcpus": 36, + "os-flavor-access:is_public": true, + "rxtx_factor": 1, + "OS-FLV-EXT-DATA:ephemeral": 0, + "disk": 0, + "id": "physical.h2.large" + }, + { + "name": "physical.m2.medium", + "links": [ + { + "href": "https://compute.region.eu-de.otc-tsi.de/v2/91d687759aed45d28b5f6084bc2fa8ad/flavors/physical.m2.medium", + "rel": "self" + }, + { + "href": "https://compute.region.eu-de.otc-tsi.de/91d687759aed45d28b5f6084bc2fa8ad/flavors/physical.m2.medium", + "rel": "bookmark" + } + ], + "ram": 2097152, + "OS-FLV-DISABLED:disabled": false, + "vcpus": 192, + "os-flavor-access:is_public": true, + "rxtx_factor": 1, + "OS-FLV-EXT-DATA:ephemeral": 0, + "disk": 17000, + "id": "physical.m2.medium" + } + ] +} + `) + }) + + //count := 0 + + actual, err := flavors.List(fake.ServiceClient(), flavors.ListOpts{}) + if err != nil { + t.Errorf("Failed to extract flavors: %v", err) + } + + expected := []flavors.Flavor{ + { + ID: "physical.h2.large", + RAM: 196608, + Disabled: false, + Name: "physical.h2.large", + VCPUs: 36, + IsPublic: true, + RxTxFactor: 1, + Ephemeral: 0, + Disk: 0, + Links: []golangsdk.Link{ + { + Href: "https://compute.region.eu-de.otc-tsi.de/v2/91d687759aed45d28b5f6084bc2fa8ad/flavors/physical.h2.large", + Rel: "self", + }, + { + Href: "https://compute.region.eu-de.otc-tsi.de/91d687759aed45d28b5f6084bc2fa8ad/flavors/physical.h2.large", + Rel: "bookmark", + }, + }, + }, + { + ID: "physical.m2.medium", + RAM: 2097152, + Disabled: false, + Name: "physical.m2.medium", + VCPUs: 192, + IsPublic: true, + RxTxFactor: 1, + Ephemeral: 0, + Disk: 17000, + Links: []golangsdk.Link{ + { + Href: "https://compute.region.eu-de.otc-tsi.de/v2/91d687759aed45d28b5f6084bc2fa8ad/flavors/physical.m2.medium", + Rel: "self", + }, + { + Href: "https://compute.region.eu-de.otc-tsi.de/91d687759aed45d28b5f6084bc2fa8ad/flavors/physical.m2.medium", + Rel: "bookmark", + }, + }, + }, + } + + th.AssertDeepEquals(t, expected, actual) +} diff --git a/openstack/bms/v2/flavors/urls.go b/openstack/bms/v2/flavors/urls.go new file mode 100644 index 000000000..78ad17743 --- /dev/null +++ b/openstack/bms/v2/flavors/urls.go @@ -0,0 +1,9 @@ +package flavors + +import ( + "github.com/huaweicloud/golangsdk" +) + +func listURL(client *golangsdk.ServiceClient) string { + return client.ServiceURL("flavors", "detail") +} diff --git a/openstack/bms/v2/keypairs/doc.go b/openstack/bms/v2/keypairs/doc.go new file mode 100644 index 000000000..6cc0cfded --- /dev/null +++ b/openstack/bms/v2/keypairs/doc.go @@ -0,0 +1,15 @@ +/* +Package keypairs provides the ability to manage key pairs as well as create +bms with a specified key pair. + +Example to List Key Pairs + + listkeypair := keypairs.ListOpts{Name: "c2c-keypair1"} + allkeypair, err := keypairs.List(client,listkeypair) + if err != nil { + panic(err) + } + + fmt.Println(allkeypair) +*/ +package keypairs diff --git a/openstack/bms/v2/keypairs/requests.go b/openstack/bms/v2/keypairs/requests.go new file mode 100644 index 000000000..f1416146f --- /dev/null +++ b/openstack/bms/v2/keypairs/requests.go @@ -0,0 +1,68 @@ +package keypairs + +import ( + "reflect" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +type ListOpts struct { + // Name is used to refer to this keypair from other services within this + // region. + Name string `json:"name"` +} + +/// List returns a Pager that allows you to iterate over a collection of KeyPairs. +func List(c *golangsdk.ServiceClient, opts ListOpts) ([]KeyPair, error) { + u := listURL(c) + pages, err := pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { + return KeyPairPage{pagination.LinkedPageBase{PageResult: r}} + }).AllPages() + + allkeypairs, err := ExtractKeyPairs(pages) + if err != nil { + return nil, err + } + + return FilterKeyPairs(allkeypairs, opts) +} + +//FilterKeyPairs used to filter keypairs using name +func FilterKeyPairs(keypairs []KeyPair, opts ListOpts) ([]KeyPair, error) { + + var refinedKeypairs []KeyPair + var matched bool + m := map[string]interface{}{} + + if opts.Name != "" { + m["Name"] = opts.Name + } + + if len(m) > 0 && len(keypairs) > 0 { + for _, keypair := range keypairs { + matched = true + + for key, value := range m { + if sVal := getStructKeyPairField(&keypair, key); !(sVal == value) { + matched = false + } + } + + if matched { + refinedKeypairs = append(refinedKeypairs, keypair) + } + } + + } else { + refinedKeypairs = keypairs + } + + return refinedKeypairs, nil +} + +func getStructKeyPairField(v *KeyPair, field string) string { + r := reflect.ValueOf(v) + f := reflect.Indirect(r).FieldByName(field) + return string(f.String()) +} diff --git a/openstack/bms/v2/keypairs/results.go b/openstack/bms/v2/keypairs/results.go new file mode 100644 index 000000000..9ec6c3236 --- /dev/null +++ b/openstack/bms/v2/keypairs/results.go @@ -0,0 +1,65 @@ +package keypairs + +import ( + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +// KeyPair is an SSH key known to the OpenStack Cloud that is available to be +// injected into bms servers. +type KeyPair struct { + // Name is used to refer to this keypair from other services within this + // region. + Name string `json:"name"` + + // Fingerprint is a short sequence of bytes that can be used to authenticate + // or validate a longer public key. + Fingerprint string `json:"fingerprint"` + + // PublicKey is the public key from this pair, in OpenSSH format. + // "ssh-rsa AAAAB3Nz..." + PublicKey string `json:"public_key"` +} + +// KeyPairPage stores a single page of all KeyPair results from a List call. +// Use the ExtractKeyPairs function to convert the results to a slice of +// KeyPairs. +type KeyPairPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a KeyPairPage is empty. +func (page KeyPairPage) IsEmpty() (bool, error) { + ks, err := ExtractKeyPairs(page) + return len(ks) == 0, err +} + +// ExtractKeyPairs interprets a page of results as a slice of KeyPairs. +func ExtractKeyPairs(r pagination.Page) ([]KeyPair, error) { + type pair struct { + KeyPair KeyPair `json:"keypair"` + } + var s struct { + KeyPairs []pair `json:"keypairs"` + } + err := (r.(KeyPairPage)).ExtractInto(&s) + results := make([]KeyPair, len(s.KeyPairs)) + for i, pair := range s.KeyPairs { + results[i] = pair.KeyPair + } + return results, err +} + +type keyPairResult struct { + golangsdk.Result +} + +// Extract is a method that attempts to interpret any KeyPair resource response +// as a KeyPair struct. +func (r keyPairResult) Extract() (*KeyPair, error) { + var s struct { + KeyPair *KeyPair `json:"keypair"` + } + err := r.ExtractInto(&s) + return s.KeyPair, err +} diff --git a/openstack/bms/v2/keypairs/testing/fixtures.go b/openstack/bms/v2/keypairs/testing/fixtures.go new file mode 100644 index 000000000..e2aec3dd4 --- /dev/null +++ b/openstack/bms/v2/keypairs/testing/fixtures.go @@ -0,0 +1,23 @@ +package testing + +// ListOutput is a sample response to a List call. +const ListOutput = ` +{ + "keypairs": [ + { + "keypair": { + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyUHvhG56/beGw/23/zYGQJ9YUmsMiRhigdbuhpS7QgPMG1LMlW32Homqph166GXpfRvo66vwO+vTrQu9xLR0Z4oYntKDGtc9pF5SRE7nsSjxmtrs2GjJB+dBsk0WgSxVUZP0jV59ecJJhWz5IvtjDJ7UkuwmDv27GLDVnuADS4uAeXXhUKKHnCgkYXLgOsSbp52e9oq2ulMNCZ3RWtFLHE/phShPYjDvZ/8grG2WKkhsf65cR71CIOaOfDbf6AfOyUr0xFLeGg+elSE/g4IHe4yCZodAjGlvE78jkBdEIXb6wmr0nWY033KiunMyWX2ERey5rcQ1XI4YuUgP2ApCd Generated-by-Nova\n", + "name": "c2c-firstkey", + "fingerprint": "68:17:18:4c:4f:a7:05:68:71:01:3c:f1:db:4c:38:4f" + } + }, + { + "keypair": { + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD2iZGC1Gr6YWjjobcqA5SP150HA4FzCbDk1r91KKr7GHpeVSsnaXM+e/Eh6VB04ahnF4bllz3fgXfeFgTbVfMbIOcomqU1KmtZpcXOjCVrHo4I6dpAisZ8yO6mBjbbl440Xgocs2UqnWVTlW2vf0O0IFmPODJLN7P1r1r2Vfd0gnZpGN5/J8HvzsQLtdbmttl/ylxkbrq/20bIlY1VF3FXNO7KeREJTwDgdo3xRTFBXMkbSAj9b7dBlN4nhB0lXl8lnAGAA+nkJ69Av3UE0yaG2jYJcW9yjqLuIH3GMFGXSdqLMitdGqfR0o8RhHW40xZM7wMOXUbShy2r4u7uGUXv Generated-by-Nova\n", + "name": "c2c-secondkey", + "fingerprint": "a7:c0:d6:6b:60:32:15:fe:f7:74:37:18:85:7a:fa:31" + } + } + ] +} +` diff --git a/openstack/bms/v2/keypairs/testing/requests_test.go b/openstack/bms/v2/keypairs/testing/requests_test.go new file mode 100644 index 000000000..9e8dfc0e5 --- /dev/null +++ b/openstack/bms/v2/keypairs/testing/requests_test.go @@ -0,0 +1,44 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/huaweicloud/golangsdk/openstack/bms/v2/keypairs" + th "github.com/huaweicloud/golangsdk/testhelper" + "github.com/huaweicloud/golangsdk/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ListOutput) + }) + + actual, err := keypairs.List(client.ServiceClient(), keypairs.ListOpts{}) + th.AssertNoErr(t, err) + expected := []keypairs.KeyPair{ + { + + Name: "c2c-firstkey", + PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyUHvhG56/beGw/23/zYGQJ9YUmsMiRhigdbuhpS7QgPMG1LMlW32Homqph166GXpfRvo66vwO+vTrQu9xLR0Z4oYntKDGtc9pF5SRE7nsSjxmtrs2GjJB+dBsk0WgSxVUZP0jV59ecJJhWz5IvtjDJ7UkuwmDv27GLDVnuADS4uAeXXhUKKHnCgkYXLgOsSbp52e9oq2ulMNCZ3RWtFLHE/phShPYjDvZ/8grG2WKkhsf65cR71CIOaOfDbf6AfOyUr0xFLeGg+elSE/g4IHe4yCZodAjGlvE78jkBdEIXb6wmr0nWY033KiunMyWX2ERey5rcQ1XI4YuUgP2ApCd Generated-by-Nova\n", + Fingerprint: "68:17:18:4c:4f:a7:05:68:71:01:3c:f1:db:4c:38:4f", + }, + { + + Name: "c2c-secondkey", + PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD2iZGC1Gr6YWjjobcqA5SP150HA4FzCbDk1r91KKr7GHpeVSsnaXM+e/Eh6VB04ahnF4bllz3fgXfeFgTbVfMbIOcomqU1KmtZpcXOjCVrHo4I6dpAisZ8yO6mBjbbl440Xgocs2UqnWVTlW2vf0O0IFmPODJLN7P1r1r2Vfd0gnZpGN5/J8HvzsQLtdbmttl/ylxkbrq/20bIlY1VF3FXNO7KeREJTwDgdo3xRTFBXMkbSAj9b7dBlN4nhB0lXl8lnAGAA+nkJ69Av3UE0yaG2jYJcW9yjqLuIH3GMFGXSdqLMitdGqfR0o8RhHW40xZM7wMOXUbShy2r4u7uGUXv Generated-by-Nova\n", + Fingerprint: "a7:c0:d6:6b:60:32:15:fe:f7:74:37:18:85:7a:fa:31", + }, + } + th.AssertDeepEquals(t, expected, actual) +} diff --git a/openstack/bms/v2/keypairs/urls.go b/openstack/bms/v2/keypairs/urls.go new file mode 100644 index 000000000..f21c6866f --- /dev/null +++ b/openstack/bms/v2/keypairs/urls.go @@ -0,0 +1,11 @@ +package keypairs + +import ( + "github.com/huaweicloud/golangsdk" +) + +const resourcePath = "os-keypairs" + +func listURL(c *golangsdk.ServiceClient) string { + return c.ServiceURL(resourcePath) +} diff --git a/openstack/bms/v2/nics/doc.go b/openstack/bms/v2/nics/doc.go new file mode 100644 index 000000000..fb331f0b1 --- /dev/null +++ b/openstack/bms/v2/nics/doc.go @@ -0,0 +1,17 @@ +/* +Package nics enables management and retrieval of NICs +BMS service. + +Example to List Flavors + + listOpts := nics.ListOpts{} + allNics, err := nics.List(bmsClient, listOpts) + if err != nil { + panic(err) + } + + for _, nic := range allNics { + fmt.Printf("%+v\n", nic) + } +*/ +package nics diff --git a/openstack/bms/v2/nics/requests.go b/openstack/bms/v2/nics/requests.go new file mode 100644 index 000000000..490afd722 --- /dev/null +++ b/openstack/bms/v2/nics/requests.go @@ -0,0 +1,81 @@ +package nics + +import ( + "reflect" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. +type ListOpts struct { + // ID is the unique identifier for the nic. + ID string `json:"port_id"` + + // Status indicates whether or not a nic is currently operational. + Status string `json:"port_state"` +} + +// List returns collection of nics. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +func List(c *golangsdk.ServiceClient, serverId string, opts ListOpts) ([]Nic, error) { + u := listURL(c, serverId) + pages, err := pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { + return NicPage{pagination.LinkedPageBase{PageResult: r}} + }).AllPages() + + allNICs, err := ExtractNics(pages) + if err != nil { + return nil, err + } + + return FilterNICs(allNICs, opts) +} + +//FilterNICs used to filter nics using id and status. +func FilterNICs(nics []Nic, opts ListOpts) ([]Nic, error) { + + var refinedNICs []Nic + var matched bool + m := map[string]interface{}{} + + if opts.ID != "" { + m["ID"] = opts.ID + } + if opts.Status != "" { + m["Status"] = opts.Status + } + if len(m) > 0 && len(nics) > 0 { + for _, nic := range nics { + matched = true + + for key, value := range m { + if sVal := getStructField(&nic, key); !(sVal == value) { + matched = false + } + } + + if matched { + refinedNICs = append(refinedNICs, nic) + } + } + + } else { + refinedNICs = nics + } + + return refinedNICs, nil +} + +func getStructField(v *Nic, field string) string { + r := reflect.ValueOf(v) + f := reflect.Indirect(r).FieldByName(field) + return string(f.String()) +} + +// Get retrieves a particular nic based on its unique ID. +func Get(c *golangsdk.ServiceClient, serverId string, id string) (r GetResult) { + _, r.Err = c.Get(getURL(c, serverId, id), &r.Body, nil) + return +} diff --git a/openstack/bms/v2/nics/results.go b/openstack/bms/v2/nics/results.go new file mode 100644 index 000000000..cf1b6e4fd --- /dev/null +++ b/openstack/bms/v2/nics/results.go @@ -0,0 +1,86 @@ +package nics + +import ( + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +type FixedIP struct { + SubnetID string `json:"subnet_id"` + IPAddress string `json:"ip_address"` +} + +//Nic Manage and perform other operations on Nic, including querying Nics as well as +//querying Nic. +type Nic struct { + // ID is the unique identifier for the nic. + ID string `json:"port_id"` + + //Specifies the ID of the network to which the NIC port belongs. + NetworkID string `json:"net_id"` + + // Status indicates whether or not a nic is currently operational. + Status string `json:"port_state"` + + //Specifies the NIC private IP address. + FixedIP []FixedIP `json:"fixed_ips"` + + //Specifies the MAC address of the NIC. + MACAddress string `json:"mac_addr"` +} + +// NicPage is the page returned by a pager when traversing over a +// collection of nics. +type NicPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of nics has reached +// the end of a page and the pager seeks to traverse over a new one. In order +// to do this, it needs to construct the next page's URL. +func (r NicPage) NextPageURL() (string, error) { + var s struct { + Links []golangsdk.Link `json:"interfaceAttachments_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return golangsdk.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a NicPage struct is empty. +func (r NicPage) IsEmpty() (bool, error) { + is, err := ExtractNics(r) + return len(is) == 0, err +} + +// ExtractNics accepts a Page struct, specifically a NicPage struct, +// and extracts the elements into a slice of Nic structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractNics(r pagination.Page) ([]Nic, error) { + var s struct { + Nics []Nic `json:"interfaceAttachments"` + } + err := (r.(NicPage)).ExtractInto(&s) + return s.Nics, err +} + +type commonResult struct { + golangsdk.Result +} + +// Extract is a function that accepts a result and extracts a nic. +func (r commonResult) Extract() (*Nic, error) { + var s struct { + Nic *Nic `json:"interfaceAttachment"` + } + err := r.ExtractInto(&s) + return s.Nic, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Nic. +type GetResult struct { + commonResult +} diff --git a/openstack/bms/v2/nics/testing/requests_test.go b/openstack/bms/v2/nics/testing/requests_test.go new file mode 100644 index 000000000..4d192b32f --- /dev/null +++ b/openstack/bms/v2/nics/testing/requests_test.go @@ -0,0 +1,120 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/huaweicloud/golangsdk/openstack/bms/v2/nics" + th "github.com/huaweicloud/golangsdk/testhelper" + fake "github.com/huaweicloud/golangsdk/testhelper/client" +) + +func TestListNIC(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/2bff7a8a-3934-4f79-b1d6-53dc5540f00e/os-interface", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "interfaceAttachments": [ + { + "port_state": "ACTIVE", + "fixed_ips": [ + { + "subnet_id": "518e34f2-16d4-4242-9378-b7eea505ab9c", + "ip_address": "192.168.0.80" + } + ], + "port_id": "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", + "net_id": "31e04eb7-7ccc-4116-9f43-a51eb29fa348", + "mac_addr": "fa:16:3e:00:1a:9a" + }, + { + "port_state": "ACTIVE", + "fixed_ips": [ + { + "subnet_id": "f3ef8cb3-9954-4434-a558-70b623b3c69b", + "ip_address": "192.168.1.206" + } + ], + "port_id": "a5fff9e7-65e1-4e46-95da-263d66ff4a7a", + "net_id": "68cf7e29-b770-4951-be66-9b3f16297732", + "mac_addr": "fa:16:3e:83:dc:08" + } + ] +} + `) + }) + + //count := 0 + + actual, err := nics.List(fake.ServiceClient(), "2bff7a8a-3934-4f79-b1d6-53dc5540f00e", nics.ListOpts{}) + if err != nil { + t.Errorf("Failed to extract nics: %v", err) + } + + expected := []nics.Nic{ + { + ID: "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", + Status: "ACTIVE", + NetworkID: "31e04eb7-7ccc-4116-9f43-a51eb29fa348", + MACAddress: "fa:16:3e:00:1a:9a", + FixedIP: []nics.FixedIP{{SubnetID: "518e34f2-16d4-4242-9378-b7eea505ab9c", IPAddress: "192.168.0.80"}}, + }, + { + ID: "a5fff9e7-65e1-4e46-95da-263d66ff4a7a", + Status: "ACTIVE", + NetworkID: "68cf7e29-b770-4951-be66-9b3f16297732", + MACAddress: "fa:16:3e:83:dc:08", + FixedIP: []nics.FixedIP{{SubnetID: "f3ef8cb3-9954-4434-a558-70b623b3c69b", IPAddress: "192.168.1.206"}}, + }, + } + + th.AssertDeepEquals(t, expected, actual) +} + +func TestGetNIC(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/2bff7a8a-3934-4f79-b1d6-53dc5540f00e/os-interface/1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "interfaceAttachment": { + "port_state": "ACTIVE", + "fixed_ips": [ + { + "subnet_id": "518e34f2-16d4-4242-9378-b7eea505ab9c", + "ip_address": "192.168.0.80" + } + ], + "port_id": "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", + "net_id": "31e04eb7-7ccc-4116-9f43-a51eb29fa348", + "mac_addr": "fa:16:3e:00:1a:9a" + } +} + `) + }) + + n, err := nics.Get(fake.ServiceClient(), "2bff7a8a-3934-4f79-b1d6-53dc5540f00e", "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", n.ID) + th.AssertEquals(t, "ACTIVE", n.Status) + th.AssertEquals(t, "fa:16:3e:00:1a:9a", n.MACAddress) + th.AssertEquals(t, "31e04eb7-7ccc-4116-9f43-a51eb29fa348", n.NetworkID) + th.AssertDeepEquals(t, []nics.FixedIP{{SubnetID: "518e34f2-16d4-4242-9378-b7eea505ab9c", IPAddress: "192.168.0.80"}}, n.FixedIP) + +} diff --git a/openstack/bms/v2/nics/urls.go b/openstack/bms/v2/nics/urls.go new file mode 100644 index 000000000..5365f8306 --- /dev/null +++ b/openstack/bms/v2/nics/urls.go @@ -0,0 +1,13 @@ +package nics + +import ( + "github.com/huaweicloud/golangsdk" +) + +func listURL(client *golangsdk.ServiceClient, serverId string) string { + return client.ServiceURL("servers", serverId, "os-interface") +} + +func getURL(client *golangsdk.ServiceClient, serverId string, Id string) string { + return client.ServiceURL("servers", serverId, "os-interface", Id) +} diff --git a/openstack/bms/v2/servers/doc.go b/openstack/bms/v2/servers/doc.go new file mode 100644 index 000000000..dd88571f8 --- /dev/null +++ b/openstack/bms/v2/servers/doc.go @@ -0,0 +1,17 @@ +/* +Package servers enables management and retrieval of Servers +BMS service. + +Example to List Servers + + listOpts := servers.ListOpts{} + allServers, err := servers.List(bmsClient, listOpts) + if err != nil { + panic(err) + } + + for _, server := range allServers { + fmt.Printf("%+v\n", server) + } +*/ +package servers diff --git a/openstack/bms/v2/servers/requests.go b/openstack/bms/v2/servers/requests.go new file mode 100644 index 000000000..c43254be6 --- /dev/null +++ b/openstack/bms/v2/servers/requests.go @@ -0,0 +1,168 @@ +package servers + +import ( + "reflect" + "strings" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +// SortDir is a type for specifying in which direction to sort a list of servers. +type SortDir string + +// SortKey is a type for specifying by which key to sort a list of servers. +type SortKey string + +var ( + // SortAsc is used to sort a list of servers in ascending order. + SortAsc SortDir = "asc" + // SortDesc is used to sort a list of servers in descending order. + SortDesc SortDir = "desc" + // SortUUID is used to sort a list of servers by uuid. + SortUUID SortKey = "uuid" + // SortVMState is used to sort a list of servers by vm_state. + SortVMState SortKey = "vm_state" + // SortDisplayName is used to sort a list of servers by display_name. + SortDisplayName SortKey = "display_name" + // SortTaskState is used to sort a list of servers by task_state. + SortTaskState SortKey = "task_state" + // SortPowerState is used to sort a list of servers by power_state. + SortPowerState SortKey = "power_state" + // SortAvailabilityZone is used to sort a list of servers by availability_zone. + SortAvailabilityZone SortKey = "availability_zone" +) + +// ListServerOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the server attributes you want to see returned. +type ListOpts struct { + // ID uniquely identifies this server amongst all other servers, + // including those not accessible to the current tenant. + ID string + //ID of the user to which the BMS belongs. + UserID string + //Contains the nova-compute status + HostStatus string + //Contains the host ID of the BMS. + HostID string + // KeyName indicates which public key was injected into the server on launch. + KeyName string + // Specifies the BMS name, not added in query since returns like results. + Name string + // Specifies the BMS image ID. + ImageID string `q:"image"` + // Specifies flavor ID. + FlavorID string `q:"flavor"` + // Specifies the BMS status. + Status string `q:"status"` + //Filters out the BMSs that have been updated since the changes-since time. + // The parameter is in ISO 8601 time format, for example, 2013-06-09T06:42:18Z. + ChangesSince string `q:"changes-since"` + //Specifies whether to query the BMSs of all tenants. This parameter is available only to administrators. + // The value can be 0 (do not query the BMSs of all tenants) or 1 (query the BMSs of all tenants). + AllTenants int `q:"all_tenants"` + //Specifies the IP address. This parameter supports fuzzy matching. + IP string `q:"ip"` + //Specifies the tag list. Returns BMSs that match all tags. Use commas (,) to separate multiple tags + Tags string `q:"tags"` + //Specifies the tag list. Returns BMSs that match any tag + TagsAny string `q:"tags-any"` + //Specifies the tag list. Returns BMSs that do not match all tags. + NotTags string `q:"not-tags"` + //Specifies the tag list. Returns BMSs that do not match any of the tags. + NotTagsAny string `q:"not-tags-any"` + //Specifies the BMS sorting attribute, which can be the BMS UUID (uuid), BMS status (vm_state), + // BMS name (display_name), BMS task status (task_state), power status (power_state), + // creation time (created_at), last time when the BMS is updated (updated_at), and availability zone + // (availability_zone). You can specify multiple sort_key and sort_dir pairs. + SortKey SortKey `q:"sort_key"` + //Specifies the sorting direction, i.e. asc or desc. + SortDir SortDir `q:"sort_dir"` +} + +// ListServer returns a Pager which allows you to iterate over a collection of +// BMS Server resources. It accepts a ListServerOpts struct, which allows you to +// filter the returned collection for greater efficiency. +func List(c *golangsdk.ServiceClient, opts ListOpts) ([]Server, error) { + c.Microversion = "2.26" + q, err := golangsdk.BuildQueryString(&opts) + if err != nil { + return nil, err + } + u := listURL(c) + q.String() + pages, err := pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { + return ServerPage{pagination.LinkedPageBase{PageResult: r}} + }).AllPages() + + allservers, err := ExtractServers(pages) + if err != nil { + return nil, err + } + return FilterServers(allservers, opts) +} + +func FilterServers(servers []Server, opts ListOpts) ([]Server, error) { + var refinedServers []Server + var matched bool + m := map[string]interface{}{} + + if opts.ID != "" { + m["ID"] = opts.ID + } + if opts.Name != "" { + m["Name"] = opts.Name + } + if opts.UserID != "" { + m["UserID"] = opts.UserID + } + if opts.HostStatus != "" { + m["HostStatus"] = opts.HostStatus + } + if opts.HostID != "" { + m["HostID"] = opts.HostID + } + if opts.KeyName != "" { + m["KeyName"] = opts.KeyName + } + if len(m) > 0 && len(servers) > 0 { + for _, server := range servers { + matched = true + + for key, value := range m { + if sVal := getStructServerField(&server, key); !(sVal == value) { + matched = false + } + } + if matched { + refinedServers = append(refinedServers, server) + } + } + } else { + refinedServers = servers + } + var serverList []Server + + for i := 0; i < len(refinedServers); i++ { + if strings.Contains(refinedServers[i].Flavor.ID, "physical") { + serverList = append(serverList, refinedServers[i]) + } + + } + return serverList, nil +} + +func getStructServerField(v *Server, field string) string { + r := reflect.ValueOf(v) + f := reflect.Indirect(r).FieldByName(field) + return string(f.String()) +} + +// Get requests details on a single server, by ID. +func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + MoreHeaders: map[string]string{"X-OpenStack-Nova-API-Version": "2.26"}, + }) + return +} diff --git a/openstack/bms/v2/servers/results.go b/openstack/bms/v2/servers/results.go new file mode 100644 index 000000000..a1be32666 --- /dev/null +++ b/openstack/bms/v2/servers/results.go @@ -0,0 +1,163 @@ +package servers + +import ( + "time" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/pagination" +) + +type commonResult struct { + golangsdk.Result +} + +type ServerPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a page contains no Server results. +func (r ServerPage) IsEmpty() (bool, error) { + s, err := ExtractServers(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r ServerPage) NextPageURL() (string, error) { + var s struct { + Links []golangsdk.Link `json:"servers_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return golangsdk.ExtractNextURL(s.Links) +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a server. +type GetResult struct { + commonResult +} + +// Extract is a function that accepts a result and extracts a server. +func (r commonResult) Extract() (*Server, error) { + var s struct { + Server *Server `json:"server"` + } + err := r.ExtractInto(&s) + return s.Server, err +} + +// ExtractServers accepts a Page struct, specifically a ServerPage struct, +// and extracts the elements into a slice of Server structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractServers(r pagination.Page) ([]Server, error) { + var s struct { + Servers []Server `json:"servers"` + } + err := (r.(ServerPage)).ExtractInto(&s) + return s.Servers, err +} + +// Server exposes fields corresponding to a given server on the user's account. +type Server struct { + // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant. + ID string `json:"id"` + // TenantID identifies the tenant owning this server resource. + TenantID string `json:"tenant_id"` + // UserID uniquely identifies the user account owning the tenant. + UserID string `json:"user_id"` + // Name contains the human-readable name for the server. + Name string `json:"name"` + // Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE. + Status string `json:"status"` + // Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created. + Updated time.Time `json:"updated"` + Created time.Time `json:"created"` + //Specifies the nova-compute status. + HostStatus string `json:"host_status"` + //Specifies the host ID of the BMS. + HostID string `json:"hostid"` + // Progress ranges from 0..100. + // A request made against the server completes only once Progress reaches 100. + Progress int `json:"progress"` + // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration. + AccessIPv4 string `json:"accessIPv4"` + AccessIPv6 string `json:"accessIPv6"` + // Image refers to a JSON object, which itself indicates the OS image used to deploy the server. + Image Images `json:"image"` + // Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server. + Flavor Flavor `json:"flavor"` + // Addresses includes a list of all IP addresses assigned to the server, keyed by pool. + Addresses map[string]interface{} `json:"addresses"` + // Metadata includes a list of all user-specified key-value pairs attached to the server. + Metadata map[string]string `json:"metadata"` + // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference. + Links []Links `json:"links"` + // KeyName indicates which public key was injected into the server on launch. + KeyName string `json:"key_name"` + // AdminPass will generally be empty. However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place. + // Note that this is the ONLY time this field will be valid. + AdminPass string `json:"adminPass"` + // SecurityGroups includes the security groups that this instance has applied to it + SecurityGroups []SecurityGroups `json:"security_groups"` + //Specifies the BMS tag. + //Added in micro version 2.26. + Tags []string `json:"tags"` + //Specifies whether a BMS is locked + Locked bool `json:"locked"` + ConfigDrive string `json:"config_drive"` + //Specifies the AZ ID. This is an extended attribute. + AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` + //Specifies the disk configuration mode. This is an extended attribute. + DiskConfig string `json:"OS-DCF:diskConfig"` + //Specifies the name of a host on the hypervisor. + // It is an extended attribute provided by the Nova driver + HostName string `json:"OS-EXT-SRV-ATTR:hostname"` + //Specifies the server description. + Description string `json:"description"` + //Specifies the job status of the BMS. This is an extended attribute. + TaskState string `json:"OS-EXT-STS:task_state"` + //Specifies the power status of the BMS. This is an extended attribute + PowerState int `json:"OS-EXT-STS:power_state"` + //Specifies the UUID of the kernel image when the AMI image is used + KernelId string `json:"OS-EXT-SRV-ATTR:kernel_id"` + //Specifies the host name of the BMS. This is an extended attribute + Host string `json:"OS-EXT-SRV-ATTR:host"` + //Specifies the UUID of the Ramdisk image when the AMI image is used. + RamdiskId string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` + //Specifies the BMS startup sequence in the batch BMS creation scenario. + Launch_index int `json:"OS-EXT-SRV-ATTR:launch_index"` + //Specifies the user data specified during BMS creation. + UserData string `json:"OS-EXT-SRV-ATTR:user_data"` + //Specifies the reserved BMS IDs in the batch BMS creation scenario. + ReservationID string `json:"OS-EXT-SRV-ATTR:reservation_id"` + //Specifies the device name of the BMS system disk + RootDevicName string `json:"OS-EXT-SRV-ATTR:root_device_name"` + //Specifies the name of a host on the hypervisor. + HypervisorHostName string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` + //Specifies the BMS status. This is an extended attribute. + VMState string `json:"OS-EXT-STS:vm_state"` + //Specifies the BMS ID. This is an extended attribute. + InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` +} + +type SecurityGroups struct { + Name string `json:"name"` +} + +type Flavor struct { + ID string `json:"id"` + Links []Links `json:"links"` +} + +type Links struct { + Rel string `json:"rel"` + Href string `json:"href"` +} + +type Images struct { + ID string `json:"id"` + Links []Links `json:"links"` +} diff --git a/openstack/bms/v2/servers/testing/requests_test.go b/openstack/bms/v2/servers/testing/requests_test.go new file mode 100644 index 000000000..0cfc431a4 --- /dev/null +++ b/openstack/bms/v2/servers/testing/requests_test.go @@ -0,0 +1,131 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/huaweicloud/golangsdk/openstack/bms/v2/servers" + th "github.com/huaweicloud/golangsdk/testhelper" + fake "github.com/huaweicloud/golangsdk/testhelper/client" +) + +func TestListServers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "servers": [ +{ + "tenant_id": "17fbda95add24720a4038ba4b1c705ed", + "id": "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", + "user_id": "aec83183a5b54cf2bc28812d1dc5509e", + "name": "BMS-1", +"key_name": "KeyPair-click2cloud", + "flavor": { + "links": [ + { + "rel": "bookmark", + "href": "https://ecs.eu-de.otc.t-systems.com:443/17fbda95add24720a4038ba4b1c705ed/flavors/physical.o2.medium" + } + ], + "id": "physical.o2.medium" + }, + "status": "ACTIVE" + + }, +{ + "tenant_id": "17fbda95add24720a4038ba4b1c705ed", + "id": "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764r", + "user_id": "aec83183a5b54cf2bc28812d1dc5509e", + "name": "BMS-2", +"key_name": "KeyPair-click2cloud", + "flavor": { + "links": [ + { + "rel": "bookmark", + "href": "https://ecs.eu-de.otc.t-systems.com:443/17fbda95add24720a4038ba4b1c705ed/flavors/physical.o2.medium" + } + ], + "id": "physical.o2.medium" + }, + "status": "ACTIVE" + } +] +} + `) + }) + + serverOpts := servers.ListOpts{} + actual, err := servers.List(fake.ServiceClient(), serverOpts) + if err != nil { + t.Errorf("Failed to extract server list: %v", err) + } + + expected := []servers.Server{ + { + ID: "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", + Status: "ACTIVE", + UserID: "aec83183a5b54cf2bc28812d1dc5509e", + Name: "BMS-1", + TenantID: "17fbda95add24720a4038ba4b1c705ed", + KeyName: "KeyPair-click2cloud", + Flavor: servers.Flavor{ID: "physical.o2.medium", Links: []servers.Links{{Rel: "bookmark", + Href: "https://ecs.eu-de.otc.t-systems.com:443/17fbda95add24720a4038ba4b1c705ed/flavors/physical.o2.medium"}}}, + }, + { + ID: "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764r", + Status: "ACTIVE", + UserID: "aec83183a5b54cf2bc28812d1dc5509e", + Name: "BMS-2", + TenantID: "17fbda95add24720a4038ba4b1c705ed", + KeyName: "KeyPair-click2cloud", + Flavor: servers.Flavor{ID: "physical.o2.medium", Links: []servers.Links{{Rel: "bookmark", + Href: "https://ecs.eu-de.otc.t-systems.com:443/17fbda95add24720a4038ba4b1c705ed/flavors/physical.o2.medium"}}}, + }, + } + + th.AssertDeepEquals(t, expected, actual) +} + +func TestGetServer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/2bff7a8a-3934-4f79-b1d6-53dc5540f00e", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "server": { + "tenant_id": "17fbda95add24720a4038ba4b1c705ed", + "id": "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", + "user_id": "aec83183a5b54cf2bc28812d1dc5509e", + "name": "BMS-1", + "status": "ACTIVE" + } +} + `) + }) + + n, err := servers.Get(fake.ServiceClient(), "2bff7a8a-3934-4f79-b1d6-53dc5540f00e").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "1d3bf3ae-bc4a-4890-86f8-8c31a6eb764f", n.ID) + th.AssertEquals(t, "ACTIVE", n.Status) + th.AssertEquals(t, "BMS-1", n.Name) + th.AssertEquals(t, "aec83183a5b54cf2bc28812d1dc5509e", n.UserID) + th.AssertEquals(t, "17fbda95add24720a4038ba4b1c705ed", n.TenantID) + +} diff --git a/openstack/bms/v2/servers/urls.go b/openstack/bms/v2/servers/urls.go new file mode 100644 index 000000000..5326d5422 --- /dev/null +++ b/openstack/bms/v2/servers/urls.go @@ -0,0 +1,13 @@ +package servers + +import ( + "github.com/huaweicloud/golangsdk" +) + +func getURL(client *golangsdk.ServiceClient, server_id string) string { + return client.ServiceURL("servers", server_id) +} + +func listURL(client *golangsdk.ServiceClient) string { + return client.ServiceURL("servers", "detail") +} diff --git a/openstack/bms/v2/tags/doc.go b/openstack/bms/v2/tags/doc.go new file mode 100644 index 000000000..49c34bca2 --- /dev/null +++ b/openstack/bms/v2/tags/doc.go @@ -0,0 +1,25 @@ +/* +Package tags enables management and retrieval of Tags +BMS service. + +Example to Create a Tag + + createOpts := tags.CreateOpts{ + Tag: []string{"__type_baremetal"}, + + } + + tag, err := tags.Create(bmsClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Tag + + serverID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + err := tags.Delete(bmsClient, serverID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package tags diff --git a/openstack/bms/v2/tags/requests.go b/openstack/bms/v2/tags/requests.go new file mode 100644 index 000000000..4931008c7 --- /dev/null +++ b/openstack/bms/v2/tags/requests.go @@ -0,0 +1,48 @@ +package tags + +import ( + "github.com/huaweicloud/golangsdk" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// create request. +type CreateOptsBuilder interface { + ToTagsCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new tag. +type CreateOpts struct { + Tag []string `json:"tags" required:"true"` +} + +// ToTagsCreateMap builds a create request body from CreateOpts. +func (opts CreateOpts) ToTagsCreateMap() (map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "") +} + +// Create will create a new Tag based on the values in CreateOpts. To extract +// the Tag object from the response, call the Extract method on the +// CreateResult. +func Create(c *golangsdk.ServiceClient, serverId string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTagsCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, serverId), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Get retrieves a particular tag based on its unique ID. +func Get(c *golangsdk.ServiceClient, serverId string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, serverId), &r.Body, nil) + return +} + +// Delete will permanently delete a particular tag based on its unique ID. +func Delete(c *golangsdk.ServiceClient, serverId string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, serverId), nil) + return +} diff --git a/openstack/bms/v2/tags/results.go b/openstack/bms/v2/tags/results.go new file mode 100644 index 000000000..0ea511331 --- /dev/null +++ b/openstack/bms/v2/tags/results.go @@ -0,0 +1,39 @@ +package tags + +import ( + "github.com/huaweicloud/golangsdk" +) + +type Tags struct { + //Specifies the tags of a BMS + Tags []string `json:"tags"` +} + +type commonResult struct { + golangsdk.Result +} + +// Extract interprets any commonResult as a Tags. +func (r commonResult) Extract() (*Tags, error) { + var s *Tags + err := r.ExtractInto(&s) + return s, err +} + +// CreateResult represents the result of an create operation. Call its Extract +// method to interpret it as a Tag. +type CreateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type DeleteResult struct { + golangsdk.ErrResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Tag. +type GetResult struct { + commonResult +} diff --git a/openstack/bms/v2/tags/testing/requests_test.go b/openstack/bms/v2/tags/testing/requests_test.go new file mode 100644 index 000000000..0e0ef80a4 --- /dev/null +++ b/openstack/bms/v2/tags/testing/requests_test.go @@ -0,0 +1,87 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/huaweicloud/golangsdk/openstack/bms/v2/common" + "github.com/huaweicloud/golangsdk/openstack/bms/v2/tags" + th "github.com/huaweicloud/golangsdk/testhelper" +) + +func TestCreateTag(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/2bff7a8a-3934-4f79-b1d6-53dc5540f00e/tags", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` +{ + "tags": [ + "__type_baremetal" + ] +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "tags": [ + "__type_baremetal" + ] +} `) + }) + + options := tags.CreateOpts{ + Tag: []string{"__type_baremetal"}, + } + n, err := tags.Create(fake.ServiceClient(), "2bff7a8a-3934-4f79-b1d6-53dc5540f00e", options).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "__type_baremetal", n.Tags[0]) +} + +func TestDeleteTag(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/2bff7a8a-3934-4f79-b1d6-53dc5540f00e/tags", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + res := tags.Delete(fake.ServiceClient(), "2bff7a8a-3934-4f79-b1d6-53dc5540f00e") + th.AssertNoErr(t, res.Err) +} + +func TestGetTags(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/2bff7a8a-3934-4f79-b1d6-53dc5540f00e/tags", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "tags": [ + "__type_baremetal" + ] +} + `) + }) + + n, err := tags.Get(fake.ServiceClient(), "2bff7a8a-3934-4f79-b1d6-53dc5540f00e").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "__type_baremetal", n.Tags[0]) + +} diff --git a/openstack/bms/v2/tags/urls.go b/openstack/bms/v2/tags/urls.go new file mode 100644 index 000000000..864970410 --- /dev/null +++ b/openstack/bms/v2/tags/urls.go @@ -0,0 +1,9 @@ +package tags + +import ( + "github.com/huaweicloud/golangsdk" +) + +func resourceURL(client *golangsdk.ServiceClient, serverId string) string { + return client.ServiceURL("servers", serverId, "tags") +} From 73b129152d7762e63ffe60a6e5354b18487ba2c0 Mon Sep 17 00:00:00 2001 From: disha-wani Date: Fri, 20 Jul 2018 13:32:58 +0530 Subject: [PATCH 2/4] add BMS client method --- openstack/client.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openstack/client.go b/openstack/client.go index a509f508f..9efdedda8 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -541,6 +541,14 @@ func NewHwSFSV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*g return sc, err } +func NewBMSV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "compute") + e := strings.Replace(sc.Endpoint, "v2", "v2.1", 1) + sc.Endpoint = e + sc.ResourceBase = e + return sc, err +} + // NewDeHServiceV1 creates a ServiceClient that may be used to access the v1 Dedicated Hosts service. func NewDeHServiceV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) { sc, err := initClientOpts(client, eo, "deh") From dc04604bdb03d638d517b16136aee27efe216a43 Mon Sep 17 00:00:00 2001 From: Sapan Date: Mon, 23 Jul 2018 16:39:43 +0530 Subject: [PATCH 3/4] remove ok code from bms server get method --- openstack/bms/v2/servers/requests.go | 1 - 1 file changed, 1 deletion(-) diff --git a/openstack/bms/v2/servers/requests.go b/openstack/bms/v2/servers/requests.go index c43254be6..e59cd87c7 100644 --- a/openstack/bms/v2/servers/requests.go +++ b/openstack/bms/v2/servers/requests.go @@ -161,7 +161,6 @@ func getStructServerField(v *Server, field string) string { // Get requests details on a single server, by ID. func Get(client *golangsdk.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, &golangsdk.RequestOpts{ - OkCodes: []int{200}, MoreHeaders: map[string]string{"X-OpenStack-Nova-API-Version": "2.26"}, }) return From 3027769f13504fc5d50b1385b75229bd208ea543 Mon Sep 17 00:00:00 2001 From: disha-wani Date: Tue, 24 Jul 2018 11:46:53 +0530 Subject: [PATCH 4/4] removed common_test.go file to use testhelper client --- openstack/bms/v2/common/common_tests.go | 17 ----------------- openstack/bms/v2/tags/testing/requests_test.go | 2 +- 2 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 openstack/bms/v2/common/common_tests.go diff --git a/openstack/bms/v2/common/common_tests.go b/openstack/bms/v2/common/common_tests.go deleted file mode 100644 index 287397023..000000000 --- a/openstack/bms/v2/common/common_tests.go +++ /dev/null @@ -1,17 +0,0 @@ -package common - -import ( - "strings" - - "github.com/huaweicloud/golangsdk" - "github.com/huaweicloud/golangsdk/testhelper/client" -) - -const TokenID = client.TokenID - -func ServiceClient() *golangsdk.ServiceClient { - sc := client.ServiceClient() - e := strings.Replace(sc.Endpoint, "v2", "v2.1", 1) - sc.ResourceBase = e - return sc -} diff --git a/openstack/bms/v2/tags/testing/requests_test.go b/openstack/bms/v2/tags/testing/requests_test.go index 0e0ef80a4..14e5be6c6 100644 --- a/openstack/bms/v2/tags/testing/requests_test.go +++ b/openstack/bms/v2/tags/testing/requests_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - fake "github.com/huaweicloud/golangsdk/openstack/bms/v2/common" "github.com/huaweicloud/golangsdk/openstack/bms/v2/tags" th "github.com/huaweicloud/golangsdk/testhelper" + fake "github.com/huaweicloud/golangsdk/testhelper/client" ) func TestCreateTag(t *testing.T) {