Skip to content

Commit 84fc916

Browse files
authored
Merge pull request #3439 from gophercloud/bp-v2-c28c4b8
[v2] neutron: add segments extension package
2 parents 6a3f95a + 5477f7f commit 84fc916

7 files changed

Lines changed: 565 additions & 0 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package segments
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/gophercloud/gophercloud/v2"
8+
"github.com/gophercloud/gophercloud/v2/internal/acceptance/tools"
9+
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/segments"
10+
th "github.com/gophercloud/gophercloud/v2/testhelper"
11+
)
12+
13+
func CreateSegment(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*segments.Segment, error) {
14+
name := tools.RandomString("TESTACC-SEGMENT-", 8)
15+
desc := "test segment description"
16+
17+
opts := segments.CreateOpts{
18+
NetworkID: networkID,
19+
NetworkType: "geneve",
20+
Name: name,
21+
Description: desc,
22+
}
23+
24+
segment, err := segments.Create(context.TODO(), client, opts).Extract()
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
tools.PrintResource(t, segment)
30+
31+
th.AssertEquals(t, segment.Name, name)
32+
th.AssertEquals(t, segment.Description, desc)
33+
th.AssertEquals(t, segment.NetworkType, "geneve")
34+
th.AssertEquals(t, segment.NetworkID, networkID)
35+
36+
return segment, nil
37+
}
38+
39+
func DeleteSegment(t *testing.T, client *gophercloud.ServiceClient, segmentID string) {
40+
t.Logf("Attempting to delete segment %s", segmentID)
41+
42+
err := segments.Delete(context.TODO(), client, segmentID).ExtractErr()
43+
if err != nil {
44+
t.Fatalf("Failed to delete segment %s: %v", segmentID, err)
45+
}
46+
47+
t.Logf("Deleted segment %s", segmentID)
48+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//go:build acceptance || networking || segments
2+
3+
package segments
4+
5+
import (
6+
"context"
7+
"testing"
8+
9+
"github.com/gophercloud/gophercloud/v2/internal/acceptance/clients"
10+
networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2"
11+
"github.com/gophercloud/gophercloud/v2/internal/acceptance/tools"
12+
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/segments"
13+
th "github.com/gophercloud/gophercloud/v2/testhelper"
14+
)
15+
16+
func TestSegmentCRUD(t *testing.T) {
17+
clients.RequireAdmin(t)
18+
19+
client, err := clients.NewNetworkV2Client()
20+
th.AssertNoErr(t, err)
21+
22+
// Skip these tests if we don't have the required extension
23+
networking.RequireNeutronExtension(t, client, "segment")
24+
25+
network, err := networking.CreateNetwork(t, client)
26+
th.AssertNoErr(t, err)
27+
defer networking.DeleteNetwork(t, client, network.ID)
28+
29+
segment, err := CreateSegment(t, client, network.ID)
30+
th.AssertNoErr(t, err)
31+
defer DeleteSegment(t, client, segment.ID)
32+
33+
// Get
34+
segGet, err := segments.Get(context.TODO(), client, segment.ID).Extract()
35+
th.AssertNoErr(t, err)
36+
th.AssertEquals(t, segment.ID, segGet.ID)
37+
38+
// Update
39+
newName := tools.RandomString("UPDATED-SEGMENT-", 8)
40+
newDesc := "updated description"
41+
updateOpts := segments.UpdateOpts{
42+
Name: &newName,
43+
Description: &newDesc,
44+
}
45+
segUpdated, err := segments.Update(context.TODO(), client, segment.ID, updateOpts).Extract()
46+
th.AssertNoErr(t, err)
47+
th.AssertEquals(t, newName, segUpdated.Name)
48+
th.AssertEquals(t, newDesc, segUpdated.Description)
49+
50+
// List
51+
allPages, err := segments.List(client, nil).AllPages(context.TODO())
52+
th.AssertNoErr(t, err)
53+
allSegments, err := segments.ExtractSegments(allPages)
54+
th.AssertNoErr(t, err)
55+
th.AssertIntGreaterOrEqual(t, len(allSegments), 1)
56+
t.Logf("Found %d segments", len(allSegments))
57+
tools.PrintResource(t, allSegments)
58+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package segments
2+
3+
import (
4+
"context"
5+
6+
"github.com/gophercloud/gophercloud/v2"
7+
"github.com/gophercloud/gophercloud/v2/pagination"
8+
)
9+
10+
// ListOpts allows filtering when listing segments.
11+
type ListOpts struct {
12+
Name string `q:"name"`
13+
Description string `q:"description"`
14+
NetworkID string `q:"network_id"`
15+
PhysicalNetwork string `q:"physical_network"`
16+
NetworkType string `q:"network_type"`
17+
SegmentationID int `q:"segmentation_id"`
18+
RevisionNumber int `q:"revision_number"`
19+
SortDir string `q:"sort_dir"`
20+
SortKey string `q:"sort_key"`
21+
Fields string `q:"fields"`
22+
}
23+
24+
// ListOptsBuilder interface for listing.
25+
type ListOptsBuilder interface {
26+
ToSegmentListQuery() (string, error)
27+
}
28+
29+
func (opts ListOpts) ToSegmentListQuery() (string, error) {
30+
q, err := gophercloud.BuildQueryString(opts)
31+
if err != nil {
32+
return "", err
33+
}
34+
return q.String(), nil
35+
}
36+
37+
// List all segments.
38+
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
39+
url := rootURL(c)
40+
if opts != nil {
41+
query, err := opts.ToSegmentListQuery()
42+
if err != nil {
43+
return pagination.Pager{Err: err}
44+
}
45+
url += query
46+
}
47+
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
48+
return SegmentPage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}}
49+
})
50+
}
51+
52+
// CreateOptsBuilder allows extensions to add additional parameters.
53+
type CreateOptsBuilder interface {
54+
ToSegmentCreateMap() (map[string]any, error)
55+
}
56+
57+
// CreateOpts contains the fields needed for creating a segment.
58+
type CreateOpts struct {
59+
Name string `json:"name,omitempty"`
60+
Description string `json:"description,omitempty"`
61+
NetworkID string `json:"network_id" required:"true"`
62+
NetworkType string `json:"network_type" required:"true"`
63+
PhysicalNetwork string `json:"physical_network,omitempty"`
64+
SegmentationID int `json:"segmentation_id,omitempty"`
65+
}
66+
67+
func (opts CreateOpts) ToSegmentCreateMap() (map[string]any, error) {
68+
return gophercloud.BuildRequestBody(opts, "segment")
69+
}
70+
71+
// Create a new segment.
72+
func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
73+
b, err := opts.ToSegmentCreateMap()
74+
if err != nil {
75+
r.Err = err
76+
return
77+
}
78+
resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil)
79+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
80+
return
81+
}
82+
83+
// Get retrieves a segment by ID.
84+
func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) {
85+
resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil)
86+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
87+
return
88+
}
89+
90+
// Delete removes a segment by ID.
91+
func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) {
92+
resp, err := c.Delete(ctx, resourceURL(c, id), nil)
93+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
94+
return
95+
}
96+
97+
// UpdateOpts contains fields to update a segment.
98+
type UpdateOpts struct {
99+
Name *string `json:"name,omitempty"`
100+
Description *string `json:"description,omitempty"`
101+
SegmentationID *int `json:"segmentation_id,omitempty"`
102+
}
103+
104+
// UpdateOptsBuilder is the interface for update options.
105+
type UpdateOptsBuilder interface {
106+
ToSegmentUpdateMap() (map[string]any, error)
107+
}
108+
109+
func (opts UpdateOpts) ToSegmentUpdateMap() (map[string]any, error) {
110+
return gophercloud.BuildRequestBody(opts, "segment")
111+
}
112+
113+
// Update a segment.
114+
func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
115+
b, err := opts.ToSegmentUpdateMap()
116+
if err != nil {
117+
r.Err = err
118+
return
119+
}
120+
resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
121+
OkCodes: []int{200},
122+
})
123+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
124+
return
125+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package segments
2+
3+
import (
4+
"time"
5+
6+
"github.com/gophercloud/gophercloud/v2"
7+
"github.com/gophercloud/gophercloud/v2/pagination"
8+
)
9+
10+
// Segment model
11+
type Segment struct {
12+
ID string `json:"id"`
13+
Name string `json:"name"`
14+
Description string `json:"description"`
15+
NetworkID string `json:"network_id"`
16+
NetworkType string `json:"network_type"`
17+
PhysicalNetwork string `json:"physical_network"`
18+
SegmentationID int `json:"segmentation_id"`
19+
RevisionNumber int `json:"revision_number"`
20+
CreatedAt time.Time `json:"created_at"`
21+
UpdatedAt time.Time `json:"updated_at"`
22+
}
23+
24+
// SegmentPage wraps a page of segments.
25+
type SegmentPage struct {
26+
pagination.LinkedPageBase
27+
}
28+
29+
func (r SegmentPage) IsEmpty() (bool, error) {
30+
if r.StatusCode == 204 {
31+
return true, nil
32+
}
33+
34+
is, err := ExtractSegments(r)
35+
return len(is) == 0, err
36+
}
37+
38+
func ExtractSegments(r pagination.Page) ([]Segment, error) {
39+
var s []Segment
40+
err := ExtractSegmentsInto(r, &s)
41+
return s, err
42+
}
43+
44+
// ExtractSegmentsInto extracts the elements into a slice of Segment structs.
45+
func ExtractSegmentsInto(r pagination.Page, v any) error {
46+
return r.(SegmentPage).ExtractIntoSlicePtr(v, "segments")
47+
}
48+
49+
// Segment results
50+
type commonResult struct {
51+
gophercloud.Result
52+
}
53+
54+
func (r commonResult) Extract() (*Segment, error) {
55+
var s Segment
56+
err := r.ExtractInto(&s)
57+
return &s, err
58+
}
59+
60+
func (r commonResult) ExtractInto(v any) error {
61+
return r.ExtractIntoStructPtr(v, "segment")
62+
}
63+
64+
type GetResult struct {
65+
commonResult
66+
}
67+
68+
type CreateResult struct {
69+
commonResult
70+
}
71+
72+
type UpdateResult struct {
73+
commonResult
74+
}
75+
76+
type DeleteResult struct {
77+
gophercloud.ErrResult
78+
}

0 commit comments

Comments
 (0)