Skip to content

Commit

Permalink
feat: support checking VLAN availability of port
Browse files Browse the repository at this point in the history
  • Loading branch information
MegaportPhilipBrowne committed Sep 25, 2024
1 parent bc8a91b commit 23c55ee
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 5 deletions.
3 changes: 3 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ var ErrCostCentreTooLong = errors.New("cost centre must be less than 255 charact

// ErrManagedAccountNotFound is returned when a managed account can't be found
var ErrManagedAccountNotFound = errors.New("managed account not found")

// ErrInvalidVLAN is returned when a VLAN is invalid
var ErrInvalidVLAN = errors.New("invalid VLAN, must be between 0 and 4094")
48 changes: 48 additions & 0 deletions port.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"slices"
"time"
)
Expand All @@ -30,6 +31,8 @@ type PortService interface {
LockPort(ctx context.Context, portId string) (*LockPortResponse, error)
// UnlockPort unlocks a port in the Megaport Port API.
UnlockPort(ctx context.Context, portId string) (*UnlockPortResponse, error)
// CheckPortVLANAvailability checks if a VLAN is available on a port in the Megaport Products API.
CheckPortVLANAvailability(ctx context.Context, portId string, vlan int) (bool, error)
}

// NewPortService creates a new instance of the Port Service.
Expand Down Expand Up @@ -432,3 +435,48 @@ func (svc *PortServiceOp) UnlockPort(ctx context.Context, portId string) (*Unloc
return nil, ErrPortNotLocked
}
}

func (svc *PortServiceOp) CheckPortVLANAvailability(ctx context.Context, portId string, vlan int) (bool, error) {
if vlan < 0 || vlan > 4094 {
return false, ErrInvalidVLAN
}

_, err := svc.GetPort(ctx, portId)
if err != nil {
return false, err
}

path := "/v2/product/port/" + portId + "/vlan"
params := url.Values{}
params.Add("vlan", fmt.Sprintf("%d", vlan))
url := svc.Client.BaseURL.JoinPath(path)
url.RawQuery = params.Encode()
urlStr := url.String()

clientReq, err := svc.Client.NewRequest(ctx, http.MethodGet, urlStr, nil)
if err != nil {
return false, err
}

response, err := svc.Client.Do(ctx, clientReq, nil)
if err != nil {
return false, err
}
defer response.Body.Close()

body, fileErr := io.ReadAll(response.Body)
if fileErr != nil {
return false, err
}

vlanResponse := PortVLANAvailabilityAPIResponse{}
unmarshalErr := json.Unmarshal(body, &vlanResponse)
if unmarshalErr != nil {
return false, unmarshalErr
}

if slices.Contains(vlanResponse.Data, vlan) {
return true, nil
}
return false, nil
}
6 changes: 6 additions & 0 deletions port_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,9 @@ type PortTerminatedServiceDetailsInterface struct {
Up int `json:"up"`
Shutdown bool `json:"shutdown"`
}

type PortVLANAvailabilityAPIResponse struct {
Message string `json:"message"`
Terms string `json:"terms"`
Data []int `json:"data"`
}
90 changes: 85 additions & 5 deletions vxc_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,39 @@ func (suite *VXCIntegrationTestSuite) TestVXCBuy() {
}
serviceKeyID := serviceKeyRes.ServiceKeyUID

var aEndVLAN, bEndVLAN int
var aEndAvailable, bEndAvailable bool
var err error

for i := 0; i < 10; i++ {
aEndVLAN = GenerateRandomVLAN()
aEndAvailable, err = portSvc.CheckPortVLANAvailability(ctx, aEndUid, aEndVLAN)
if err != nil {
suite.FailNowf("cannot check a end vlan availability", "cannot check a end vlan availability %v", err)
}
if aEndAvailable {
break
}
}

if !aEndAvailable {
suite.FailNowf("a end vlan not available after 10 attempts", "a end vlan %d is not available after 10 attempts", aEndVLAN)
}

for i := 0; i < 10; i++ {
bEndVLAN = GenerateRandomVLAN()
bEndAvailable, err = portSvc.CheckPortVLANAvailability(ctx, bEndUid, bEndVLAN)
if err != nil {
suite.FailNowf("cannot check b end vlan availability", "cannot check b end vlan availability %v", err)
}
if bEndAvailable {
break
}
}
if !bEndAvailable {
suite.FailNowf("b end vlan not available after 10 attempts", "b end vlan %d is not available after 10 attempts", bEndVLAN)
}

logger.InfoContext(ctx, "buying vxc")

buyVxcRes, vxcErr := vxcSvc.BuyVXC(ctx, &BuyVXCRequest{
Expand All @@ -132,10 +165,10 @@ func (suite *VXCIntegrationTestSuite) TestVXCBuy() {
Shutdown: false,
ServiceKey: serviceKeyID,
AEndConfiguration: VXCOrderEndpointConfiguration{
VLAN: GenerateRandomVLAN(),
VLAN: aEndVLAN,
},
BEndConfiguration: VXCOrderEndpointConfiguration{
VLAN: GenerateRandomVLAN(),
VLAN: bEndVLAN,
ProductUID: bEndUid,
},
WaitForProvision: true,
Expand All @@ -147,11 +180,41 @@ func (suite *VXCIntegrationTestSuite) TestVXCBuy() {
vxcUid := buyVxcRes.TechnicalServiceUID
suite.True(IsGuid(vxcUid), "invalid guid for vxc uid")

newAVLAN := GenerateRandomVLAN()
newBVLAN := GenerateRandomVLAN()
var newAEndAvailable, newBEndAvailable bool
var newAVLAN, newBVLAN int

newCostCentre := "Test Cost Centre 2"
newTerm := 24

for i := 0; i < 10; i++ {
newAVLAN = GenerateRandomVLAN()
newAEndAvailable, err = portSvc.CheckPortVLANAvailability(ctx, aEndUid, newAVLAN)
if err != nil {
suite.FailNowf("cannot check new a end vlan availability", "cannot check new a end vlan availability %v", err)
}
if newAEndAvailable {
break
}
}
if !newAEndAvailable {
suite.FailNowf("new a end vlan not available after 10 attempts", "new a end vlan %d is not available after 10 attempts", newAVLAN)
}

for i := 0; i < 10; i++ {
newBVLAN = GenerateRandomVLAN()
newBEndAvailable, err = portSvc.CheckPortVLANAvailability(ctx, bEndUid, newBVLAN)
if err != nil {
suite.FailNowf("cannot check new b end vlan availability", "cannot check new b end vlan availability %v", err)
}
if newBEndAvailable {
break
}
}

if !newBEndAvailable {
suite.FailNowf("new b end vlan not available after 10 attempts", "new b end vlan %d is not available after 10 attempts", newBVLAN)
}

updateRes, updateErr := vxcSvc.UpdateVXC(ctx, vxcUid, &UpdateVXCRequest{
AEndVLAN: &newAVLAN,
BEndVLAN: &newBVLAN,
Expand Down Expand Up @@ -409,14 +472,31 @@ func (suite *VXCIntegrationTestSuite) TestAWSVIFConnectionBuy() {

logger.InfoContext(ctx, "buying aws vif connection (b-end)")

var aEndVLAN int
var vlanAvailable bool
var err error
for i := 0; i < 10; i++ {
aEndVLAN = GenerateRandomVLAN()
vlanAvailable, err = portSvc.CheckPortVLANAvailability(ctx, portUid, aEndVLAN)
if err != nil {
suite.FailNowf("cannot check vlan availability", "cannot check vlan availability %v", err)
}
if vlanAvailable {
break
}
}
if !vlanAvailable {
suite.FailNowf("vlan not available after 10 attempts", "vlan %d is not available after 10 attempts", aEndVLAN)
}

buyVxcRes, vxcErr := vxcSvc.BuyVXC(ctx, &BuyVXCRequest{
PortUID: portUid,
VXCName: "Hosted AWS VIF Test Connection",
RateLimit: 500,
Term: 1,
Shutdown: false,
AEndConfiguration: VXCOrderEndpointConfiguration{
VLAN: GenerateRandomVLAN(),
VLAN: aEndVLAN,
},
BEndConfiguration: VXCOrderEndpointConfiguration{
ProductUID: "87860c28-81ef-4e79-8cc7-cfc5a4c4bc86",
Expand Down

0 comments on commit 23c55ee

Please sign in to comment.