Skip to content

Commit 9eb412d

Browse files
authored
Merge pull request #2350 from sapcc/sharedfilesystems-schedulerstats-services
Add support for shared-file-system `/v2/services` and `/v2/scheduler-stats/pools`
2 parents 0ace4a5 + 43edb96 commit 9eb412d

File tree

15 files changed

+913
-1
lines changed

15 files changed

+913
-1
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//go:build acceptance
2+
// +build acceptance
3+
4+
package v2
5+
6+
import (
7+
"testing"
8+
9+
"github.com/gophercloud/gophercloud/acceptance/clients"
10+
"github.com/gophercloud/gophercloud/acceptance/tools"
11+
"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/schedulerstats"
12+
th "github.com/gophercloud/gophercloud/testhelper"
13+
)
14+
15+
func TestSchedulerStatsList(t *testing.T) {
16+
client, err := clients.NewSharedFileSystemV2Client()
17+
client.Microversion = "2.23"
18+
th.AssertNoErr(t, err)
19+
20+
allPages, err := schedulerstats.List(client, nil).AllPages()
21+
th.AssertNoErr(t, err)
22+
23+
allPools, err := schedulerstats.ExtractPools(allPages)
24+
th.AssertNoErr(t, err)
25+
26+
for _, recordset := range allPools {
27+
tools.PrintResource(t, &recordset)
28+
}
29+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//go:build acceptance
2+
// +build acceptance
3+
4+
package v2
5+
6+
import (
7+
"testing"
8+
9+
"github.com/gophercloud/gophercloud/acceptance/clients"
10+
"github.com/gophercloud/gophercloud/acceptance/tools"
11+
"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/services"
12+
th "github.com/gophercloud/gophercloud/testhelper"
13+
)
14+
15+
func TestServicesList(t *testing.T) {
16+
client, err := clients.NewSharedFileSystemV2Client()
17+
th.AssertNoErr(t, err)
18+
19+
client.Microversion = "2.7"
20+
allPages, err := services.List(client, nil).AllPages()
21+
th.AssertNoErr(t, err)
22+
23+
allServices, err := services.ExtractServices(allPages)
24+
th.AssertNoErr(t, err)
25+
26+
th.AssertIntGreaterOrEqual(t, len(allServices), 1)
27+
28+
for _, s := range allServices {
29+
tools.PrintResource(t, &s)
30+
th.AssertEquals(t, s.Status, "enabled")
31+
}
32+
}

acceptance/openstack/sharedfilesystems/v2/shares.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
// error will be returned if the share could not be created
1717
func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share, error) {
1818
if testing.Short() {
19-
t.Skip("Skipping test that requres share creation in short mode.")
19+
t.Skip("Skipping test that requires share creation in short mode.")
2020
}
2121

2222
iTrue := true
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
Package schedulerstats returns information about shared file systems capacity
3+
and utilisation. Example:
4+
5+
listOpts := schedulerstats.ListOpts{
6+
}
7+
8+
allPages, err := schedulerstats.List(client, listOpts).AllPages()
9+
if err != nil {
10+
panic(err)
11+
}
12+
13+
allStats, err := schedulerstats.ExtractPools(allPages)
14+
if err != nil {
15+
panic(err)
16+
}
17+
18+
for _, stat := range allStats {
19+
fmt.Printf("%+v\n", stat)
20+
}
21+
*/
22+
package schedulerstats
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package schedulerstats
2+
3+
import (
4+
"github.com/gophercloud/gophercloud"
5+
"github.com/gophercloud/gophercloud/pagination"
6+
)
7+
8+
// ListOptsBuilder allows extensions to add additional parameters to the
9+
// List request.
10+
type ListOptsBuilder interface {
11+
ToPoolsListQuery() (string, error)
12+
}
13+
14+
// ListOpts controls the view of data returned (e.g globally or per project).
15+
type ListOpts struct {
16+
// The pool name for the back end.
17+
ProjectID string `json:"project_id,omitempty"`
18+
// The pool name for the back end.
19+
PoolName string `json:"pool_name"`
20+
// The host name for the back end.
21+
HostName string `json:"host_name"`
22+
// The name of the back end.
23+
BackendName string `json:"backend_name"`
24+
// The capabilities for the storage back end.
25+
Capabilities string `json:"capabilities"`
26+
// The share type name or UUID. Allows filtering back end pools based on the extra-specs in the share type.
27+
ShareType string `json:"share_type,omitempty"`
28+
}
29+
30+
// ToPoolsListQuery formats a ListOpts into a query string.
31+
func (opts ListOpts) ToPoolsListQuery() (string, error) {
32+
q, err := gophercloud.BuildQueryString(opts)
33+
return q.String(), err
34+
}
35+
36+
// List makes a request against the API to list pool information.
37+
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
38+
url := poolsListURL(client)
39+
if opts != nil {
40+
query, err := opts.ToPoolsListQuery()
41+
if err != nil {
42+
return pagination.Pager{Err: err}
43+
}
44+
url += query
45+
}
46+
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
47+
return PoolPage{pagination.SinglePageBase(r)}
48+
})
49+
}
50+
51+
// ListDetailOptsBuilder allows extensions to add additional parameters to the
52+
// ListDetail request.
53+
type ListDetailOptsBuilder interface {
54+
ToPoolsListQuery() (string, error)
55+
}
56+
57+
// ListOpts controls the view of data returned (e.g globally or per project).
58+
type ListDetailOpts struct {
59+
// The pool name for the back end.
60+
ProjectID string `json:"project_id,omitempty"`
61+
// The pool name for the back end.
62+
PoolName string `json:"pool_name"`
63+
// The host name for the back end.
64+
HostName string `json:"host_name"`
65+
// The name of the back end.
66+
BackendName string `json:"backend_name"`
67+
// The capabilities for the storage back end.
68+
Capabilities string `json:"capabilities"`
69+
// The share type name or UUID. Allows filtering back end pools based on the extra-specs in the share type.
70+
ShareType string `json:"share_type,omitempty"`
71+
}
72+
73+
// ToPoolsListQuery formats a ListDetailOpts into a query string.
74+
func (opts ListDetailOpts) ToPoolsListQuery() (string, error) {
75+
q, err := gophercloud.BuildQueryString(opts)
76+
return q.String(), err
77+
}
78+
79+
// ListDetail makes a request against the API to list detailed pool information.
80+
func ListDetail(client *gophercloud.ServiceClient, opts ListDetailOptsBuilder) pagination.Pager {
81+
url := poolsListDetailURL(client)
82+
if opts != nil {
83+
query, err := opts.ToPoolsListQuery()
84+
if err != nil {
85+
return pagination.Pager{Err: err}
86+
}
87+
url += query
88+
}
89+
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
90+
return PoolPage{pagination.SinglePageBase(r)}
91+
})
92+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package schedulerstats
2+
3+
import (
4+
"encoding/json"
5+
"math"
6+
7+
"github.com/gophercloud/gophercloud/pagination"
8+
)
9+
10+
// Capabilities represents the information of an individual Pool.
11+
type Capabilities struct {
12+
// The following fields should be present in all storage drivers.
13+
14+
// The quality of service (QoS) support.
15+
Qos bool `json:"qos"`
16+
// The date and time stamp when the API request was issued.
17+
Timestamp string `json:"timestamp"`
18+
// The name of the share back end.
19+
ShareBackendName string `json:"share_backend_name"`
20+
// Share server is usually a storage virtual machine or a lightweight container that is used to export shared file systems.
21+
DriverHandlesShareServers bool `json:"driver_handles_share_servers"`
22+
// The driver version of the back end.
23+
DriverVersion string `json:"driver_version"`
24+
// The amount of free capacity for the back end, in GiBs. A valid value is a string, such as unknown, or an integer.
25+
FreeCapacityGB float64 `json:"-"`
26+
// The storage protocol for the back end. For example, NFS_CIFS, glusterfs, HDFS, etc.
27+
StorageProtocol string `json:"storage_protocol"`
28+
// The total capacity for the back end, in GiBs. A valid value is a string, such as unknown, or an integer.
29+
TotalCapacityGB float64 `json:"-"`
30+
// The specification that filters back ends by whether they do or do not support share snapshots.
31+
SnapshotSupport bool `json:"snapshot_support"`
32+
// The back end replication domain.
33+
ReplicationDomain string `json:"replication_domain"`
34+
// The name of the vendor for the back end.
35+
VendorName string `json:"vendor_name"`
36+
37+
// The following fields are optional and may have empty values depending
38+
39+
// on the storage driver in use.
40+
ReservedPercentage int64 `json:"reserved_percentage"`
41+
AllocatedCapacityGB float64 `json:"-"`
42+
}
43+
44+
// Pool represents an individual Pool retrieved from the
45+
// schedulerstats API.
46+
type Pool struct {
47+
// The name of the back end.
48+
Name string `json:"name"`
49+
// The name of the back end.
50+
Backend string `json:"backend"`
51+
// The pool name for the back end.
52+
Pool string `json:"pool"`
53+
// The host name for the back end.
54+
Host string `json:"host"`
55+
// The back end capabilities which include qos, total_capacity_gb, etc.
56+
Capabilities Capabilities `json:"capabilities,omitempty"`
57+
}
58+
59+
func (r *Capabilities) UnmarshalJSON(b []byte) error {
60+
type tmp Capabilities
61+
var s struct {
62+
tmp
63+
AllocatedCapacityGB interface{} `json:"allocated_capacity_gb"`
64+
FreeCapacityGB interface{} `json:"free_capacity_gb"`
65+
TotalCapacityGB interface{} `json:"total_capacity_gb"`
66+
}
67+
err := json.Unmarshal(b, &s)
68+
if err != nil {
69+
return err
70+
}
71+
*r = Capabilities(s.tmp)
72+
73+
// Generic function to parse a capacity value which may be a numeric
74+
// value, "unknown", or "infinite"
75+
parseCapacity := func(capacity interface{}) float64 {
76+
if capacity != nil {
77+
switch capacity.(type) {
78+
case float64:
79+
return capacity.(float64)
80+
case string:
81+
if capacity.(string) == "infinite" {
82+
return math.Inf(1)
83+
}
84+
}
85+
}
86+
return 0.0
87+
}
88+
89+
r.AllocatedCapacityGB = parseCapacity(s.AllocatedCapacityGB)
90+
r.FreeCapacityGB = parseCapacity(s.FreeCapacityGB)
91+
r.TotalCapacityGB = parseCapacity(s.TotalCapacityGB)
92+
93+
return nil
94+
}
95+
96+
// PoolPage is a single page of all List results.
97+
type PoolPage struct {
98+
pagination.SinglePageBase
99+
}
100+
101+
// IsEmpty satisfies the IsEmpty method of the Page interface. It returns true
102+
// if a List contains no results.
103+
func (page PoolPage) IsEmpty() (bool, error) {
104+
va, err := ExtractPools(page)
105+
return len(va) == 0, err
106+
}
107+
108+
// ExtractPools takes a List result and extracts the collection of
109+
// Pools returned by the API.
110+
func ExtractPools(p pagination.Page) ([]Pool, error) {
111+
var s struct {
112+
Pools []Pool `json:"pools"`
113+
}
114+
err := (p.(PoolPage)).ExtractInto(&s)
115+
return s.Pools, err
116+
}

0 commit comments

Comments
 (0)