Skip to content

Commit

Permalink
add redundancy group info methods
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismarget-j committed Jan 7, 2025
1 parent 2a69aac commit ce5bfcf
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 18 deletions.
1 change: 1 addition & 0 deletions apstra/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const (
ErrCannotChangeTransform
ErrRangeOverlap
ErrAuthFail
ErrInvalidApiResponse
ErrCompatibility
ErrConflict
ErrExists
Expand Down
25 changes: 25 additions & 0 deletions apstra/enum/enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,28 @@ var (
VnTypeVlan = VnType{Value: "vlan"}
VnTypeVxlan = VnType{Value: "vxlan"}
)

type RgType oenum.Member[string]

var (
RgTypeEsi = RgType{Value: "esi"}
RgTypeMlag = RgType{Value: "mlag"}
)

type SystemType oenum.Member[string]

var (
SystemTypeServer = SystemType{Value: "server"}
SystemTypeSwitch = SystemType{Value: "switch"}
)

type NodeRole oenum.Member[string]

var (
NodeRoleAccess = NodeRole{Value: "access"}
NodeRoleGeneric = NodeRole{Value: "generic"}
NodeRoleLeaf = NodeRole{Value: "leaf"}
NodeRoleRemoteGateway = NodeRole{Value: "remote_gateway"}
NodeRoleSpine = NodeRole{Value: "spine"}
NodeRoleSuperspine = NodeRole{Value: "superspine"}
)
115 changes: 115 additions & 0 deletions apstra/enum/generated_enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,37 @@ func (o *JunosEvpnIrbMode) UnmarshalJSON(bytes []byte) error {
return o.FromString(s)
}

var (
_ enum = (*NodeRole)(nil)
_ json.Marshaler = (*NodeRole)(nil)
_ json.Unmarshaler = (*NodeRole)(nil)
)

func (o NodeRole) String() string {
return o.Value
}

func (o *NodeRole) FromString(s string) error {
if NodeRoles.Parse(s) == nil {
return newEnumParseError(o, s)
}
o.Value = s
return nil
}

func (o *NodeRole) MarshalJSON() ([]byte, error) {
return json.Marshal(o.String())
}

func (o *NodeRole) UnmarshalJSON(bytes []byte) error {
var s string
err := json.Unmarshal(bytes, &s)
if err != nil {
return err
}
return o.FromString(s)
}

var (
_ enum = (*PolicyApplicationPointType)(nil)
_ json.Marshaler = (*PolicyApplicationPointType)(nil)
Expand Down Expand Up @@ -539,6 +570,37 @@ func (o *ResourcePoolType) UnmarshalJSON(bytes []byte) error {
return o.FromString(s)
}

var (
_ enum = (*RgType)(nil)
_ json.Marshaler = (*RgType)(nil)
_ json.Unmarshaler = (*RgType)(nil)
)

func (o RgType) String() string {
return o.Value
}

func (o *RgType) FromString(s string) error {
if RgTypes.Parse(s) == nil {
return newEnumParseError(o, s)
}
o.Value = s
return nil
}

func (o *RgType) MarshalJSON() ([]byte, error) {
return json.Marshal(o.String())
}

func (o *RgType) UnmarshalJSON(bytes []byte) error {
var s string
err := json.Unmarshal(bytes, &s)
if err != nil {
return err
}
return o.FromString(s)
}

var (
_ enum = (*RoutingZoneConstraintMode)(nil)
_ json.Marshaler = (*RoutingZoneConstraintMode)(nil)
Expand Down Expand Up @@ -663,6 +725,37 @@ func (o *SviIpv6Mode) UnmarshalJSON(bytes []byte) error {
return o.FromString(s)
}

var (
_ enum = (*SystemType)(nil)
_ json.Marshaler = (*SystemType)(nil)
_ json.Unmarshaler = (*SystemType)(nil)
)

func (o SystemType) String() string {
return o.Value
}

func (o *SystemType) FromString(s string) error {
if SystemTypes.Parse(s) == nil {
return newEnumParseError(o, s)
}
o.Value = s
return nil
}

func (o *SystemType) MarshalJSON() ([]byte, error) {
return json.Marshal(o.String())
}

func (o *SystemType) UnmarshalJSON(bytes []byte) error {
var s string
err := json.Unmarshal(bytes, &s)
if err != nil {
return err
}
return o.FromString(s)
}

var (
_ enum = (*TcpStateQualifier)(nil)
_ json.Marshaler = (*TcpStateQualifier)(nil)
Expand Down Expand Up @@ -800,6 +893,16 @@ var (
JunosEvpnIrbModeAsymmetric,
)

_ enum = new(NodeRole)
NodeRoles = oenum.New(
NodeRoleAccess,
NodeRoleGeneric,
NodeRoleLeaf,
NodeRoleRemoteGateway,
NodeRoleSpine,
NodeRoleSuperspine,
)

_ enum = new(PolicyApplicationPointType)
PolicyApplicationPointTypes = oenum.New(
PolicyApplicationPointTypeGroup,
Expand Down Expand Up @@ -859,6 +962,12 @@ var (
ResourcePoolTypeVni,
)

_ enum = new(RgType)
RgTypes = oenum.New(
RgTypeEsi,
RgTypeMlag,
)

_ enum = new(RoutingZoneConstraintMode)
RoutingZoneConstraintModes = oenum.New(
RoutingZoneConstraintModeNone,
Expand Down Expand Up @@ -906,6 +1015,12 @@ var (
SviIpv6ModeLinkLocal,
)

_ enum = new(SystemType)
SystemTypes = oenum.New(
SystemTypeServer,
SystemTypeSwitch,
)

_ enum = new(TcpStateQualifier)
TcpStateQualifiers = oenum.New(
TcpStateQualifierEstablished,
Expand Down
27 changes: 9 additions & 18 deletions apstra/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ func testBlueprintD(ctx context.Context, t *testing.T, client *Client) *TwoStage
return bpClient
}

func testBlueprintE(ctx context.Context, t *testing.T, client *Client) (*TwoStageL3ClosClient, func(context.Context) error) {
func testBlueprintE(ctx context.Context, t *testing.T, client *Client) *TwoStageL3ClosClient {
bpId, err := client.CreateBlueprintFromTemplate(ctx, &CreateBlueprintFromTemplateRequest{
RefDesign: RefDesignTwoStageL3Clos,
Label: randString(5, "hex"),
Expand All @@ -374,10 +374,7 @@ func testBlueprintE(ctx context.Context, t *testing.T, client *Client) (*TwoStag
if err != nil {
t.Fatal(err)
}

bpDeleteFunc := func(ctx context.Context) error {
return client.DeleteBlueprint(ctx, bpId)
}
t.Cleanup(func() { require.NoError(t, client.DeleteBlueprint(ctx, bpId)) })

leafQuery := new(PathQuery).
SetBlueprintId(bpId).
Expand All @@ -397,17 +394,14 @@ func testBlueprintE(ctx context.Context, t *testing.T, client *Client) (*TwoStag
} `json:"items"`
}
err = leafQuery.Do(ctx, &leafResponse)
if err != nil {
t.Fatal(errors.Join(err, bpDeleteFunc(ctx)))
}
require.NoError(t, err)

leafAssignements := make(SystemIdToInterfaceMapAssignment)
for _, item := range leafResponse.Items {
leafAssignements[item.Leaf.ID] = "Juniper_vQFX__AOS-7x10-Leaf"
}
err = bpClient.SetInterfaceMapAssignments(ctx, leafAssignements)
if err != nil {
t.Fatal(errors.Join(err, bpDeleteFunc(ctx)))
}
require.NoError(t, err)

accessQuery := new(PathQuery).
SetBlueprintId(bpId).
Expand All @@ -427,19 +421,16 @@ func testBlueprintE(ctx context.Context, t *testing.T, client *Client) (*TwoStag
} `json:"items"`
}
err = accessQuery.Do(ctx, &accessResponse)
if err != nil {
t.Fatal(errors.Join(err, bpDeleteFunc(ctx)))
}
require.NoError(t, err)

accessAssignements := make(SystemIdToInterfaceMapAssignment)
for _, item := range accessResponse.Items {
accessAssignements[item.Leaf.ID] = "Juniper_vQFX__AOS-8x10-1"
}
err = bpClient.SetInterfaceMapAssignments(ctx, accessAssignements)
if err != nil {
t.Fatal(errors.Join(err, bpDeleteFunc(ctx)))
}
require.NoError(t, err)

return bpClient, bpDeleteFunc
return bpClient
}

// testBlueprintH creates a test blueprint using client and returns a *TwoStageL3ClosClient.
Expand Down
118 changes: 118 additions & 0 deletions apstra/two_stage_l3_clos_redundancy_group_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package apstra

import (
"context"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra/enum"
)

type RedundancyGroupInfo struct {
Id ObjectId
Type enum.RgType
SystemType enum.SystemType
SystemRole enum.NodeRole
SystemIds [2]ObjectId
}

func (o *TwoStageL3ClosClient) GetRedundancyGroupInfo(ctx context.Context, id ObjectId) (*RedundancyGroupInfo, error) {
resultMap, err := o.getRedundancyGroupInfo(ctx, id)
if err != nil {
return nil, err
}

result, ok := resultMap[id]
if !ok {
return nil, ClientErr{
errType: ErrNotfound,
err: fmt.Errorf("redundancy group %q not found", id),
}
}

return &result, nil
}

func (o *TwoStageL3ClosClient) GetAllRedundancyGroupInfo(ctx context.Context) (map[ObjectId]RedundancyGroupInfo, error) {
return o.getRedundancyGroupInfo(ctx, "")
}

func (o *TwoStageL3ClosClient) getRedundancyGroupInfo(ctx context.Context, id ObjectId) (map[ObjectId]RedundancyGroupInfo, error) {
rgNodeAttrs := []QEEAttribute{
NodeTypeRedundancyGroup.QEEAttribute(),
{Key: "name", Value: QEStringVal("n_redundancy_group")},
}
if id != "" {
rgNodeAttrs = append(rgNodeAttrs, QEEAttribute{Key: "id", Value: QEStringVal(id)})
}

query := new(PathQuery).
SetBlueprintId(o.blueprintId).
SetClient(o.client).
Node(rgNodeAttrs).
Out([]QEEAttribute{RelationshipTypeComposedOfSystems.QEEAttribute()}).
Node([]QEEAttribute{NodeTypeSystem.QEEAttribute(), {Key: "name", Value: QEStringVal("n_system")}})

var queryResult struct {
Items []struct {
RedundancyGroup struct {
Id ObjectId `json:"id"`
Type enum.RgType `json:"rg_type"`
} `json:"n_redundancy_group"`
System struct {
Id ObjectId `json:"id"`
Role enum.NodeRole `json:"role"`
Type enum.SystemType `json:"system_type"`
} `json:"n_system"`
} `json:"items"`
}

err := query.Do(ctx, &queryResult)
if err != nil {
return nil, fmt.Errorf("graph query %q failed - %w", query, err)
}

result := make(map[ObjectId]RedundancyGroupInfo, len(queryResult.Items)/2)
for _, item := range queryResult.Items {
rgInfo, ok := result[item.RedundancyGroup.Id]
if !ok {
// create the map entry
result[item.RedundancyGroup.Id] = RedundancyGroupInfo{
Id: item.RedundancyGroup.Id,
Type: item.RedundancyGroup.Type,
SystemType: item.System.Type,
SystemRole: item.System.Role,
SystemIds: [2]ObjectId{item.System.Id, ""},
}
continue
}

// validate the existing map entry
if rgInfo.Type != item.RedundancyGroup.Type {
return nil, fmt.Errorf("graph query %q returned inconsistent redundancy group types for group %q", query, item.RedundancyGroup.Id)
}
if rgInfo.SystemType != item.System.Type {
return nil, fmt.Errorf("graph query %q returned inconsistent system types for group %q", query, item.RedundancyGroup.Id)
}
if rgInfo.SystemRole != item.System.Role {
return nil, fmt.Errorf("graph query %q returned inconsistent system roles for group %q", query, item.RedundancyGroup.Id)
}
if rgInfo.SystemIds[1] != "" {
return nil, fmt.Errorf("graph query %q returned too many system nodes for redundancy group %q", query, item.RedundancyGroup.Id)
}

// add the second system ID to the existing map entry
rgInfo.SystemIds[1] = item.System.Id
result[item.RedundancyGroup.Id] = rgInfo
}

// ensure that each redundancy group has both system IDs
for k, v := range result {
if v.SystemIds[0] == "" || v.SystemIds[1] == "" {
return nil, ClientErr{
errType: ErrInvalidApiResponse,
err: fmt.Errorf("graph query %q didn't find system pairs for redundancy group %q, got: %q", query, k, v),
}
}
}

return result, nil
}
Loading

0 comments on commit ce5bfcf

Please sign in to comment.