Skip to content

Commit a75271e

Browse files
authored
Merge pull request #2417 from shhgs/2022-05-30
Neutron v2: ScheduleBGPSpeakerOpts, RemoveBGPSpeaker, Lis…
2 parents 0ab2b5d + 3721112 commit a75271e

7 files changed

Lines changed: 385 additions & 0 deletions

File tree

acceptance/openstack/networking/v2/extensions/agents/agents_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ package agents
55

66
import (
77
"testing"
8+
"time"
89

910
"github.com/gophercloud/gophercloud/acceptance/clients"
1011
networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
12+
spk "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/bgp/speakers"
1113
"github.com/gophercloud/gophercloud/acceptance/tools"
1214
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents"
15+
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers"
1316
th "github.com/gophercloud/gophercloud/testhelper"
1417
)
1518

@@ -92,3 +95,106 @@ func TestAgentsRUD(t *testing.T) {
9295
err = agents.Delete(client, allAgents[0].ID).ExtractErr()
9396
th.AssertNoErr(t, err)
9497
}
98+
99+
func TestBGPAgentRUD(t *testing.T) {
100+
timeout := 120 * time.Second
101+
clients.RequireAdmin(t)
102+
103+
client, err := clients.NewNetworkV2Client()
104+
th.AssertNoErr(t, err)
105+
106+
// List BGP Agents
107+
listOpts := &agents.ListOpts{
108+
AgentType: "BGP Dynamic Routing Agent",
109+
}
110+
allPages, err := agents.List(client, listOpts).AllPages()
111+
th.AssertNoErr(t, err)
112+
113+
allAgents, err := agents.ExtractAgents(allPages)
114+
th.AssertNoErr(t, err)
115+
116+
t.Logf("Retrieved BGP agents")
117+
tools.PrintResource(t, allAgents)
118+
119+
// Create a BGP Speaker
120+
bgpSpeaker, err := spk.CreateBGPSpeaker(t, client)
121+
th.AssertNoErr(t, err)
122+
pages, err := agents.ListDRAgentHostingBGPSpeakers(client, bgpSpeaker.ID).AllPages()
123+
th.AssertNoErr(t, err)
124+
bgpAgents, err := agents.ExtractAgents(pages)
125+
th.AssertNoErr(t, err)
126+
th.AssertIntGreaterOrEqual(t, len(bgpAgents), 1)
127+
128+
// List the BGP Agents that accommodate the BGP Speaker
129+
err = tools.WaitForTimeout(
130+
func() (bool, error) {
131+
flag := true
132+
for _, agt := range bgpAgents {
133+
t.Logf("BGP Speaker %s has been scheduled to agent %s", bgpSpeaker.ID, agt.ID)
134+
bgpAgent, err := agents.Get(client, agt.ID).Extract()
135+
th.AssertNoErr(t, err)
136+
numOfSpeakers := int(bgpAgent.Configurations["bgp_speakers"].(float64))
137+
flag = flag && (numOfSpeakers == 1)
138+
}
139+
return flag, nil
140+
}, timeout)
141+
th.AssertNoErr(t, err)
142+
143+
// Remove the BGP Speaker from the first agent
144+
err = agents.RemoveBGPSpeaker(client, bgpAgents[0].ID, bgpSpeaker.ID).ExtractErr()
145+
th.AssertNoErr(t, err)
146+
t.Logf("BGP Speaker %s has been removed from agent %s", bgpSpeaker.ID, bgpAgents[0].ID)
147+
err = tools.WaitForTimeout(
148+
func() (bool, error) {
149+
bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract()
150+
th.AssertNoErr(t, err)
151+
agentConf := bgpAgent.Configurations
152+
numOfSpeakers := int(agentConf["bgp_speakers"].(float64))
153+
t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers)
154+
return numOfSpeakers == 0, nil
155+
}, timeout)
156+
th.AssertNoErr(t, err)
157+
158+
// Remove all BGP Speakers from the agent
159+
pages, err = agents.ListBGPSpeakers(client, bgpAgents[0].ID).AllPages()
160+
th.AssertNoErr(t, err)
161+
allSpeakers, err := agents.ExtractBGPSpeakers(pages)
162+
th.AssertNoErr(t, err)
163+
for _, speaker := range allSpeakers {
164+
th.AssertNoErr(t, agents.RemoveBGPSpeaker(client, bgpAgents[0].ID, speaker.ID).ExtractErr())
165+
}
166+
167+
// Schedule a BGP Speaker to an agent
168+
opts := agents.ScheduleBGPSpeakerOpts{
169+
SpeakerID: bgpSpeaker.ID,
170+
}
171+
err = agents.ScheduleBGPSpeaker(client, bgpAgents[0].ID, opts).ExtractErr()
172+
th.AssertNoErr(t, err)
173+
t.Logf("Successfully scheduled speaker %s to agent %s", bgpSpeaker.ID, bgpAgents[0].ID)
174+
175+
err = tools.WaitForTimeout(
176+
func() (bool, error) {
177+
bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract()
178+
th.AssertNoErr(t, err)
179+
agentConf := bgpAgent.Configurations
180+
numOfSpeakers := int(agentConf["bgp_speakers"].(float64))
181+
t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers)
182+
return 1 == numOfSpeakers, nil
183+
}, timeout)
184+
th.AssertNoErr(t, err)
185+
186+
// Delete the BGP Speaker
187+
speakers.Delete(client, bgpSpeaker.ID).ExtractErr()
188+
th.AssertNoErr(t, err)
189+
t.Logf("Successfully deleted the BGP Speaker, %s", bgpSpeaker.ID)
190+
err = tools.WaitForTimeout(
191+
func() (bool, error) {
192+
bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract()
193+
th.AssertNoErr(t, err)
194+
agentConf := bgpAgent.Configurations
195+
numOfSpeakers := int(agentConf["bgp_speakers"].(float64))
196+
t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers)
197+
return 0 == numOfSpeakers, nil
198+
}, timeout)
199+
th.AssertNoErr(t, err)
200+
}

openstack/networking/v2/extensions/agents/doc.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,35 @@ Example to List BGP speakers by dragent
9797
log.Printf("%v", s)
9898
}
9999
100+
Example to Schedule bgp speaker to dragent
101+
102+
var opts agents.ScheduleBGPSpeakerOpts
103+
opts.SpeakerID = speakerID
104+
err := agents.ScheduleBGPSpeaker(c, agentID, opts).ExtractErr()
105+
if err != nil {
106+
log.Panic(err)
107+
}
108+
109+
Example to Remove bgp speaker from dragent
110+
111+
err := agents.RemoveBGPSpeaker(c, agentID, speakerID).ExtractErr()
112+
if err != nil {
113+
log.Panic(err)
114+
}
115+
116+
Example to list dragents hosting specific bgp speaker
117+
118+
pages, err := agents.ListDRAgentHostingBGPSpeakers(client, speakerID).AllPages()
119+
if err != nil {
120+
log.Panic(err)
121+
}
122+
allAgents, err := agents.ExtractAgents(pages)
123+
if err != nil {
124+
log.Panic(err)
125+
}
126+
for _, a := range allAgents {
127+
log.Printf("%+v", a)
128+
}
100129
*/
101130

102131
package agents

openstack/networking/v2/extensions/agents/requests.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,50 @@ func ListBGPSpeakers(c *gophercloud.ServiceClient, agentID string) pagination.Pa
158158
return ListBGPSpeakersResult{pagination.SinglePageBase(r)}
159159
})
160160
}
161+
162+
// ScheduleBGPSpeakerOptsBuilder declare a function that build ScheduleBGPSpeakerOpts into a request body
163+
type ScheduleBGPSpeakerOptsBuilder interface {
164+
ToAgentScheduleBGPSpeakerMap() (map[string]interface{}, error)
165+
}
166+
167+
// ScheduleBGPSpeakerOpts represents the data that would be POST to the endpoint
168+
type ScheduleBGPSpeakerOpts struct {
169+
SpeakerID string `json:"bgp_speaker_id" required:"true"`
170+
}
171+
172+
// ToAgentScheduleBGPSpeakerMap builds a request body from ScheduleBGPSpeakerOpts
173+
func (opts ScheduleBGPSpeakerOpts) ToAgentScheduleBGPSpeakerMap() (map[string]interface{}, error) {
174+
return gophercloud.BuildRequestBody(opts, "")
175+
}
176+
177+
// ScheduleBGPSpeaker schedule a BGP speaker to a BGP agent
178+
// POST /v2.0/agents/{agent-id}/bgp-drinstances
179+
func ScheduleBGPSpeaker(c *gophercloud.ServiceClient, agentID string, opts ScheduleBGPSpeakerOptsBuilder) (r ScheduleBGPSpeakerResult) {
180+
b, err := opts.ToAgentScheduleBGPSpeakerMap()
181+
if err != nil {
182+
r.Err = err
183+
return
184+
}
185+
resp, err := c.Post(scheduleBGPSpeakersURL(c, agentID), b, nil, &gophercloud.RequestOpts{
186+
OkCodes: []int{201},
187+
})
188+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
189+
return
190+
}
191+
192+
// RemoveBGPSpeaker removes a BGP speaker from a BGP agent
193+
// DELETE /v2.0/agents/{agent-id}/bgp-drinstances
194+
func RemoveBGPSpeaker(c *gophercloud.ServiceClient, agentID string, speakerID string) (r RemoveBGPSpeakerResult) {
195+
resp, err := c.Delete(removeBGPSpeakersURL(c, agentID, speakerID), nil)
196+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
197+
return
198+
}
199+
200+
// ListDRAgentHostingBGPSpeakers the dragents that are hosting a specific bgp speaker
201+
// GET /v2.0/bgp-speakers/{bgp-speaker-id}/bgp-dragents
202+
func ListDRAgentHostingBGPSpeakers(c *gophercloud.ServiceClient, bgpSpeakerID string) pagination.Pager {
203+
url := listDRAgentHostingBGPSpeakersURL(c, bgpSpeakerID)
204+
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
205+
return AgentPage{pagination.LinkedPageBase{PageResult: r}}
206+
})
207+
}

openstack/networking/v2/extensions/agents/results.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ type RemoveDHCPNetworkResult struct {
5555
gophercloud.ErrResult
5656
}
5757

58+
// ScheduleBGPSpeakerResult represents the result of adding a BGP speaker to a
59+
// BGP DR Agent. ExtractErr method to determine if the request succeeded or
60+
// failed.
61+
type ScheduleBGPSpeakerResult struct {
62+
gophercloud.ErrResult
63+
}
64+
65+
// RemoveBGPSpeakerResult represents the result of removing a BGP speaker from a
66+
// BGP DR Agent. ExtractErr method to determine if the request succeeded or
67+
// failed.
68+
type RemoveBGPSpeakerResult struct {
69+
gophercloud.ErrResult
70+
}
71+
5872
// Agent represents a Neutron agent.
5973
type Agent struct {
6074
// ID is the id of the agent.

openstack/networking/v2/extensions/agents/testing/fixtures.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,93 @@ const ListBGPSpeakersResult = `
255255
]
256256
}
257257
`
258+
const ScheduleBGPSpeakerRequest = `
259+
{
260+
"bgp_speaker_id": "8edb2c68-0654-49a9-b3fe-030f92e3ddf6"
261+
}
262+
`
263+
264+
var BGPAgent1 = agents.Agent{
265+
ID: "60d78b78-b56b-4d91-a174-2c03159f6bb6",
266+
AdminStateUp: true,
267+
AgentType: "BGP dynamic routing agent",
268+
Alive: true,
269+
Binary: "neutron-bgp-dragent",
270+
Configurations: map[string]interface{}{
271+
"advertise_routes": float64(2),
272+
"bgp_peers": float64(2),
273+
"bgp_speakers": float64(1),
274+
},
275+
CreatedAt: time.Date(2020, 9, 17, 20, 8, 58, 0, time.UTC),
276+
StartedAt: time.Date(2021, 5, 4, 11, 13, 12, 0, time.UTC),
277+
HeartbeatTimestamp: time.Date(2021, 9, 13, 19, 55, 1, 0, time.UTC),
278+
Host: "agent1.example.com",
279+
Topic: "bgp_dragent",
280+
}
281+
282+
var BGPAgent2 = agents.Agent{
283+
ID: "d0bdcea2-1d02-4c1d-9e79-b827e77acc22",
284+
AdminStateUp: true,
285+
AgentType: "BGP dynamic routing agent",
286+
Alive: true,
287+
Binary: "neutron-bgp-dragent",
288+
Configurations: map[string]interface{}{
289+
"advertise_routes": float64(2),
290+
"bgp_peers": float64(2),
291+
"bgp_speakers": float64(1),
292+
},
293+
CreatedAt: time.Date(2020, 9, 17, 20, 8, 15, 0, time.UTC),
294+
StartedAt: time.Date(2021, 5, 4, 11, 13, 13, 0, time.UTC),
295+
HeartbeatTimestamp: time.Date(2021, 9, 13, 19, 54, 47, 0, time.UTC),
296+
Host: "agent2.example.com",
297+
Topic: "bgp_dragent",
298+
}
299+
300+
const ListDRAgentHostingBGPSpeakersResult = `
301+
{
302+
"agents": [
303+
{
304+
"binary": "neutron-bgp-dragent",
305+
"description": null,
306+
"availability_zone": null,
307+
"heartbeat_timestamp": "2021-09-13 19:55:01",
308+
"admin_state_up": true,
309+
"resources_synced": null,
310+
"alive": true,
311+
"topic": "bgp_dragent",
312+
"host": "agent1.example.com",
313+
"agent_type": "BGP dynamic routing agent",
314+
"resource_versions": {},
315+
"created_at": "2020-09-17 20:08:58",
316+
"started_at": "2021-05-04 11:13:12",
317+
"id": "60d78b78-b56b-4d91-a174-2c03159f6bb6",
318+
"configurations": {
319+
"advertise_routes": 2,
320+
"bgp_peers": 2,
321+
"bgp_speakers": 1
322+
}
323+
},
324+
{
325+
"binary": "neutron-bgp-dragent",
326+
"description": null,
327+
"availability_zone": null,
328+
"heartbeat_timestamp": "2021-09-13 19:54:47",
329+
"admin_state_up": true,
330+
"resources_synced": null,
331+
"alive": true,
332+
"topic": "bgp_dragent",
333+
"host": "agent2.example.com",
334+
"agent_type": "BGP dynamic routing agent",
335+
"resource_versions": {},
336+
"created_at": "2020-09-17 20:08:15",
337+
"started_at": "2021-05-04 11:13:13",
338+
"id": "d0bdcea2-1d02-4c1d-9e79-b827e77acc22",
339+
"configurations": {
340+
"advertise_routes": 2,
341+
"bgp_peers": 2,
342+
"bgp_speakers": 1
343+
}
344+
}
345+
]
346+
}
347+
`

0 commit comments

Comments
 (0)