Skip to content

Commit 9aa8e5b

Browse files
authored
Networking V2: add QoS policies List (#1591)
Add method to List QoS policies.
1 parent 8a1e87c commit 9aa8e5b

6 files changed

Lines changed: 298 additions & 0 deletions

File tree

openstack/networking/v2/extensions/qos/policies/doc.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,27 @@ Example to delete a QoS policy from the existing Network
177177
}
178178
179179
fmt.Printf("Network: %+v\n", networkWithQoS)
180+
181+
Example to list QoS policies
182+
183+
shared := true
184+
listOpts := policies.ListOpts{
185+
Name: "shared-policy",
186+
Shared: &shared,
187+
}
188+
189+
allPages, err := policies.List(networkClient, listOpts).AllPages()
190+
if err != nil {
191+
panic(err)
192+
}
193+
194+
allPolicies, err := policies.ExtractPolicies(allPages)
195+
if err != nil {
196+
panic(err)
197+
}
198+
199+
for _, policy := range allPolicies {
200+
fmt.Printf("%+v\n", policy)
201+
}
180202
*/
181203
package policies

openstack/networking/v2/extensions/qos/policies/requests.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package policies
22

33
import (
4+
"github.com/gophercloud/gophercloud"
45
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
56
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
7+
"github.com/gophercloud/gophercloud/pagination"
68
)
79

810
// PortCreateOptsExt adds QoS options to the base ports.CreateOpts.
@@ -112,3 +114,57 @@ func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, e
112114

113115
return base, nil
114116
}
117+
118+
// PolicyListOptsBuilder allows extensions to add additional parameters to the List request.
119+
type PolicyListOptsBuilder interface {
120+
ToPolicyListQuery() (string, error)
121+
}
122+
123+
// ListOpts allows the filtering and sorting of paginated collections through
124+
// the Neutron API. Filtering is achieved by passing in struct field values
125+
// that map to the Policy attributes you want to see returned.
126+
// SortKey allows you to sort by a particular BandwidthLimitRule attribute.
127+
// SortDir sets the direction, and is either `asc' or `desc'.
128+
// Marker and Limit are used for the pagination.
129+
type ListOpts struct {
130+
ID string `q:"id"`
131+
TenantID string `q:"tenant_id"`
132+
ProjectID string `q:"project_id"`
133+
Name string `q:"name"`
134+
Description string `q:"description"`
135+
RevisionNumber *int `q:"revision_number"`
136+
IsDefault *bool `q:"is_default"`
137+
Shared *bool `q:"shared"`
138+
Limit int `q:"limit"`
139+
Marker string `q:"marker"`
140+
SortKey string `q:"sort_key"`
141+
SortDir string `q:"sort_dir"`
142+
Tags string `q:"tags"`
143+
TagsAny string `q:"tags-any"`
144+
NotTags string `q:"not-tags"`
145+
NotTagsAny string `q:"not-tags-any"`
146+
}
147+
148+
// ToPolicyListQuery formats a ListOpts into a query string.
149+
func (opts ListOpts) ToPolicyListQuery() (string, error) {
150+
q, err := gophercloud.BuildQueryString(opts)
151+
return q.String(), err
152+
}
153+
154+
// List returns a Pager which allows you to iterate over a collection of
155+
// Policy. It accepts a ListOpts struct, which allows you to filter and sort
156+
// the returned collection for greater efficiency.
157+
func List(c *gophercloud.ServiceClient, opts PolicyListOptsBuilder) pagination.Pager {
158+
url := listURL(c)
159+
if opts != nil {
160+
query, err := opts.ToPolicyListQuery()
161+
if err != nil {
162+
return pagination.Pager{Err: err}
163+
}
164+
url += query
165+
}
166+
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
167+
return PolicyPage{pagination.LinkedPageBase{PageResult: r}}
168+
169+
})
170+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,90 @@
11
package policies
22

3+
import (
4+
"time"
5+
6+
"github.com/gophercloud/gophercloud"
7+
"github.com/gophercloud/gophercloud/pagination"
8+
)
9+
310
// QoSPolicyExt represents additional resource attributes available with the QoS extension.
411
type QoSPolicyExt struct {
512
// QoSPolicyID represents an associated QoS policy.
613
QoSPolicyID string `json:"qos_policy_id"`
714
}
15+
16+
// Policy represents a QoS policy.
17+
type Policy struct {
18+
// ID is the id of the policy.
19+
ID string `json:"id"`
20+
21+
// Name is the human-readable name of the policy.
22+
Name string `json:"name"`
23+
24+
// TenantID is the id of the Identity project.
25+
TenantID string `json:"tenant_id"`
26+
27+
// ProjectID is the id of the Identity project.
28+
ProjectID string `json:"project_id"`
29+
30+
// CreatedAt is the time at which the policy has been created.
31+
CreatedAt time.Time `json:"created_at"`
32+
33+
// UpdatedAt is the time at which the policy has been created.
34+
UpdatedAt time.Time `json:"updated_at"`
35+
36+
// IsDefault indicates if the policy is default policy or not.
37+
IsDefault bool `json:"is_default"`
38+
39+
// Description is thehuman-readable description for the resource.
40+
Description string `json:"description"`
41+
42+
// Shared indicates whether this policy is shared across all projects.
43+
Shared bool `json:"shared"`
44+
45+
// RevisionNumber represents revision number of the policy.
46+
RevisionNumber int `json:"revision_number"`
47+
48+
// Rules represents QoS rules of the policy.
49+
Rules []map[string]interface{} `json:"rules"`
50+
51+
// Tags optionally set via extensions/attributestags
52+
Tags []string `json:"tags"`
53+
}
54+
55+
// PolicyPage stores a single page of Policies from a List() API call.
56+
type PolicyPage struct {
57+
pagination.LinkedPageBase
58+
}
59+
60+
// NextPageURL is invoked when a paginated collection of policies has reached
61+
// the end of a page and the pager seeks to traverse over a new one.
62+
// In order to do this, it needs to construct the next page's URL.
63+
func (r PolicyPage) NextPageURL() (string, error) {
64+
var s struct {
65+
Links []gophercloud.Link `json:"policies_links"`
66+
}
67+
err := r.ExtractInto(&s)
68+
if err != nil {
69+
return "", err
70+
}
71+
return gophercloud.ExtractNextURL(s.Links)
72+
}
73+
74+
// IsEmpty checks whether a PolicyPage is empty.
75+
func (r PolicyPage) IsEmpty() (bool, error) {
76+
is, err := ExtractPolicies(r)
77+
return len(is) == 0, err
78+
}
79+
80+
// ExtractPolicies accepts a PolicyPage, and extracts the elements into a slice of Policies.
81+
func ExtractPolicies(r pagination.Page) ([]Policy, error) {
82+
var s []Policy
83+
err := ExtractPolicysInto(r, &s)
84+
return s, err
85+
}
86+
87+
// ExtractPoliciesInto extracts the elements into a slice of RBAC Policy structs.
88+
func ExtractPolicysInto(r pagination.Page, v interface{}) error {
89+
return r.(PolicyPage).Result.ExtractIntoSlicePtr(v, "policies")
90+
}

openstack/networking/v2/extensions/qos/policies/testing/fixtures.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package testing
22

3+
import (
4+
"time"
5+
6+
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies"
7+
)
8+
39
const GetPortResponse = `
410
{
511
"port": {
@@ -132,3 +138,80 @@ const UpdateNetworkWithoutPolicyResponse = `
132138
}
133139
}
134140
`
141+
142+
const ListPoliciesResponse = `
143+
{
144+
"policies": [
145+
{
146+
"name": "bw-limiter",
147+
"tags": [],
148+
"rules": [
149+
{
150+
"max_kbps": 3000,
151+
"direction": "egress",
152+
"qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae",
153+
"type": "bandwidth_limit",
154+
"id": "30a57f4a-336b-4382-8275-d708babd2241",
155+
"max_burst_kbps": 300
156+
}
157+
],
158+
"tenant_id": "a77cbe0998374aed9a6798ad6c61677e",
159+
"created_at": "2019-05-19T11:17:50Z",
160+
"updated_at": "2019-05-19T11:17:57Z",
161+
"is_default": false,
162+
"revision_number": 1,
163+
"shared": false,
164+
"project_id": "a77cbe0998374aed9a6798ad6c61677e",
165+
"id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae",
166+
"description": ""
167+
},
168+
{
169+
"name": "no-rules",
170+
"tags": [],
171+
"rules": [],
172+
"tenant_id": "a77cbe0998374aed9a6798ad6c61677e",
173+
"created_at": "2019-06-01T10:38:58Z",
174+
"updated_at": "2019-06-01T10:38:58Z",
175+
"is_default": false,
176+
"revision_number": 0,
177+
"shared": false,
178+
"project_id": "a77cbe0998374aed9a6798ad6c61677e",
179+
"id": "d6e7c2fe-24dc-43be-a088-24b47d4f8f88",
180+
"description": ""
181+
}
182+
]
183+
}
184+
`
185+
186+
var Policy1 = policies.Policy{
187+
Name: "bw-limiter",
188+
Rules: []map[string]interface{}{
189+
{
190+
"type": "bandwidth_limit",
191+
"max_kbps": float64(3000),
192+
"direction": "egress",
193+
"qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae",
194+
"max_burst_kbps": float64(300),
195+
"id": "30a57f4a-336b-4382-8275-d708babd2241",
196+
},
197+
},
198+
Tags: []string{},
199+
TenantID: "a77cbe0998374aed9a6798ad6c61677e",
200+
CreatedAt: time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC),
201+
UpdatedAt: time.Date(2019, 5, 19, 11, 17, 57, 0, time.UTC),
202+
RevisionNumber: 1,
203+
ProjectID: "a77cbe0998374aed9a6798ad6c61677e",
204+
ID: "d6ae28ce-fcb5-4180-aa62-d260a27e09ae",
205+
}
206+
207+
var Policy2 = policies.Policy{
208+
Name: "no-rules",
209+
Tags: []string{},
210+
Rules: []map[string]interface{}{},
211+
TenantID: "a77cbe0998374aed9a6798ad6c61677e",
212+
CreatedAt: time.Date(2019, 6, 1, 10, 38, 58, 0, time.UTC),
213+
UpdatedAt: time.Date(2019, 6, 1, 10, 38, 58, 0, time.UTC),
214+
RevisionNumber: 0,
215+
ProjectID: "a77cbe0998374aed9a6798ad6c61677e",
216+
ID: "d6e7c2fe-24dc-43be-a088-24b47d4f8f88",
217+
}

openstack/networking/v2/extensions/qos/policies/testing/requests_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies"
1010
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
1111
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
12+
"github.com/gophercloud/gophercloud/pagination"
1213
th "github.com/gophercloud/gophercloud/testhelper"
1314
)
1415

@@ -292,3 +293,43 @@ func TestUpdateNetworkWithoutPolicy(t *testing.T) {
292293
th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
293294
th.AssertEquals(t, n.QoSPolicyID, "")
294295
}
296+
297+
func TestListPolicies(t *testing.T) {
298+
th.SetupHTTP()
299+
defer th.TeardownHTTP()
300+
301+
th.Mux.HandleFunc("/v2.0/qos/policies", func(w http.ResponseWriter, r *http.Request) {
302+
th.TestMethod(t, r, "GET")
303+
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
304+
305+
w.Header().Add("Content-Type", "application/json")
306+
w.WriteHeader(http.StatusOK)
307+
308+
fmt.Fprintf(w, ListPoliciesResponse)
309+
})
310+
311+
count := 0
312+
313+
err := policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
314+
count++
315+
actual, err := policies.ExtractPolicies(page)
316+
if err != nil {
317+
t.Errorf("Failed to extract policies: %v", err)
318+
return false, nil
319+
}
320+
321+
expected := []policies.Policy{
322+
Policy1,
323+
Policy2,
324+
}
325+
326+
th.CheckDeepEquals(t, expected, actual)
327+
328+
return true, nil
329+
})
330+
th.AssertNoErr(t, err)
331+
332+
if count != 1 {
333+
t.Errorf("Expected 1 page, got %d", count)
334+
}
335+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package policies
2+
3+
import "github.com/gophercloud/gophercloud"
4+
5+
const resourcePath = "qos/policies"
6+
7+
func rootURL(c *gophercloud.ServiceClient) string {
8+
return c.ServiceURL(resourcePath)
9+
}
10+
11+
func listURL(c *gophercloud.ServiceClient) string {
12+
return rootURL(c)
13+
}

0 commit comments

Comments
 (0)