Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ k8s_resource(new_name='peer-spine1', objects=['leaf1-spine1:bgppeer'], resource_
k8s_resource(new_name='peer-spine2', objects=['leaf1-spine2:bgppeer'], resource_deps=['bgp', 'lo0'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
k8s_resource(new_name='peer-spine1-filtered', objects=['leaf1-spine1-filtered:bgppeer'], resource_deps=['bgp', 'lo0', 'bgp-import-policy'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
k8s_resource(new_name='peer-spine1-unnumbered', objects=['leaf1-spine1-unnumbered:bgppeer'], resource_deps=['bgp', 'eth1-4'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
k8s_resource(new_name='peer-spine1-next-hop-self', objects=['leaf1-spine1-next-hop-self:bgppeer'], resource_deps=['bgp', 'lo0', 'bgp-next-hop-self'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])

k8s_yaml('./config/samples/v1alpha1_ospf.yaml')
k8s_resource(new_name='ospf-underlay', objects=['underlay:ospf'], resource_deps=['lo0', 'lo1', 'eth1-1', 'eth1-2'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
Expand All @@ -166,6 +167,7 @@ k8s_resource(new_name='ccloud-prefixset', objects=['ccloud-prefixset:prefixset']

k8s_yaml('./config/samples/v1alpha1_routingpolicy.yaml')
k8s_resource(new_name='bgp-import-policy', objects=['bgp-import-policy:routingpolicy', 'internal-networks:prefixset', 'partner-networks:prefixset', 'blocked-networks:prefixset', 'communityset-regular:communityset', 'communityset-extended:communityset'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
k8s_resource(new_name='bgp-next-hop-self', objects=['bgp-next-hop-self:routingpolicy'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])

k8s_yaml('./config/samples/cisco/nx/v1alpha1_vpcdomain.yaml')
k8s_resource(new_name='vpcdomain', objects=['leaf1-vpcdomain:vpcdomain', 'leaf1-vrfvpckeepalive:vrf', 'eth1-30:interface', 'vpc-peer-link:interfaceconfig', 'eth1-31:interface','eth1-32:interface', 'po1:interface'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ type BGPPeerAddressFamily struct {
// +optional
RouteReflectorClient bool `json:"routeReflectorClient,omitempty"`

// NextHopSelf enables next-hop-self for routes advertised to this peer in this address family.
// If a RoutingPolicy also sets the next hop, then the precedence depends on the provider and platform implementation.
// +optional
NextHopSelf bool `json:"nextHopSelf,omitempty"`

// InboundRoutingPolicyRef references a RoutingPolicy applied to routes received from this peer
// for this address family.
// +optional
Expand Down
13 changes: 12 additions & 1 deletion api/core/v1alpha1/routingpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ const (
)

// BgpActions defines BGP-specific actions for a policy statement.
// +kubebuilder:validation:XValidation:rule="has(self.setCommunity) || has(self.setExtCommunity) || has(self.setASPath)",message="at least one BGP action must be specified"
// +kubebuilder:validation:XValidation:rule="has(self.setCommunity) || has(self.setExtCommunity) || has(self.setASPath) || has(self.setNextHop)",message="at least one BGP action must be specified"
type BgpActions struct {
// SetCommunity configures BGP standard community attributes.
// +optional
Expand All @@ -143,6 +143,17 @@ type BgpActions struct {
// SetASPath configures modifications to the BGP AS path attribute.
// +optional
SetASPath *SetASPathAction `json:"setASPath,omitempty"`

// SetNextHop configures the BGP next-hop for advertised routes.
// +optional
SetNextHop *SetNextHopAction `json:"setNextHop,omitempty"`
Comment thread
nikatza marked this conversation as resolved.
Resolved
}

// SetNextHopAction defines how to replace the BGP next-hop for advertised routes.
type SetNextHopAction struct {
// IPAddr specifies the next-hop IP address to set for advertised routes.
// +required
IPAddr IPAddr `json:"ipAddr"`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't use abbreviations. ref/ https://github.com/kubernetes/community/blob/main/contributors/devel/sig-architecture/api-conventions.md#naming-conventions

Given we already use just the word "Address" in many places. I'd favor just that.

}

// SetASPathAction defines actions to modify the BGP AS path attribute.
Expand Down
21 changes: 21 additions & 0 deletions api/core/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions config/crd/bases/networking.metal.ironcore.dev_bgppeers.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions config/samples/v1alpha1_bgppeer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,25 @@ spec:
addressFamilies:
ipv4Unicast:
enabled: true
---
apiVersion: networking.metal.ironcore.dev/v1alpha1
kind: BGPPeer
metadata:
labels:
app.kubernetes.io/name: network-operator
app.kubernetes.io/managed-by: kustomize
name: leaf1-spine1-next-hop-self
spec:
deviceRef:
name: leaf1
bgpRef:
name: bgp
address: 10.0.0.4
asNumber: 65000
localAddress:
interfaceRef:
name: lo0
addressFamilies:
ipv4Unicast:
enabled: true
nextHopSelf: true
19 changes: 19 additions & 0 deletions config/samples/v1alpha1_routingpolicy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,22 @@ spec:
regex: "65200:[0-9][0-9]"
- sequence: 15
regex: "65300:[0-9]+"
---
apiVersion: networking.metal.ironcore.dev/v1alpha1
kind: RoutingPolicy
metadata:
labels:
app.kubernetes.io/name: network-operator
app.kubernetes.io/managed-by: kustomize
name: bgp-next-hop-self
spec:
deviceRef:
name: leaf1
name: BGP-NEXT-HOP-SELF
statements:
- sequence: 10
actions:
routeDisposition: AcceptRoute
bgpActions:
setNextHop:
ipAddr: 10.0.0.1
19 changes: 19 additions & 0 deletions docs/api-reference/index.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -372,15 +372,18 @@ func (r *BGPPeerReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manag
}),
).
// Watches enqueues BGPPeers when a referenced RoutingPolicy is created or deleted.
// Only triggers on create and delete events since RoutingPolicy names are immutable.
// Triggers on create, delete, and update events when the RoutingPolicy's readiness changes.
// Policies attached to the peer may include actions in conflict with activated control settings, i.e., NextHopSelf.
Watches(
&v1alpha1.RoutingPolicy{},
handler.EnqueueRequestsFromMapFunc(r.routingPolicyToBGPPeers),
builder.WithPredicates(predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
return false
oldPolicy := e.ObjectOld.(*v1alpha1.RoutingPolicy)
newPolicy := e.ObjectNew.(*v1alpha1.RoutingPolicy)
return conditions.IsReady(oldPolicy) != conditions.IsReady(newPolicy)
},
GenericFunc: func(e event.GenericEvent) bool {
GenericFunc: func(event.GenericEvent) bool {
return false
},
}),
Expand Down Expand Up @@ -722,9 +725,8 @@ func (r *BGPPeerReconciler) reconcileVRF(ctx context.Context, peer *v1alpha1.BGP

// reconcileRoutingPolicies resolves the inbound and outbound routing policy references
// on each enabled address family of the BGPPeer.
// Returns two maps from BGPAddressFamilyType to the device-level policy name.
// Sets ConfiguredCondition and returns a terminal error when a referenced policy
// is not found or belongs to a different device.
// is not found, belongs to a different device, or it is not yet ready.
func (r *BGPPeerReconciler) reconcileRoutingPolicies(ctx context.Context, peer *v1alpha1.BGPPeer, device *v1alpha1.Device) (inbound, outbound map[v1alpha1.BGPAddressFamilyType]string, err error) {
inbound = make(map[v1alpha1.BGPAddressFamilyType]string)
outbound = make(map[v1alpha1.BGPAddressFamilyType]string)
Expand Down Expand Up @@ -766,6 +768,15 @@ func (r *BGPPeerReconciler) reconcileRoutingPolicies(ctx context.Context, peer *
})
return nil, nil, reconcile.TerminalError(fmt.Errorf("routing policy %s belongs to different device", ref.Name))
}
if !conditions.IsReady(rp) {
conditions.Set(peer, metav1.Condition{
Type: v1alpha1.ConfiguredCondition,
Status: metav1.ConditionFalse,
Reason: v1alpha1.WaitingForDependenciesReason,
Message: fmt.Sprintf("Waiting for RoutingPolicy %s to become ready", ref.Name),
})
return nil, nil, reconcile.TerminalError(fmt.Errorf("routing policy %s is not yet ready", ref.Name))
}
(*m)[afType] = rp.Spec.Name
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ var _ = Describe("BGPPeer Controller", func() {
g.Expect(list.Items).To(BeEmpty())
}).Should(Succeed())

By("Cleaning up RoutingPolicy resources for this device")
policyList := &v1alpha1.RoutingPolicyList{}
Expect(k8sClient.List(ctx, policyList, client.InNamespace(metav1.NamespaceDefault))).To(Succeed())
for i := range policyList.Items {
if policyList.Items[i].Spec.DeviceRef.Name == device.Name {
Expect(client.IgnoreNotFound(k8sClient.Delete(ctx, &policyList.Items[i]))).To(Succeed())
Comment on lines +78 to +79

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to use a IndexField for this which we setup on all resources based on the referenced device.

}
}

By("Cleaning up Interface resources for this device")
intfList := &v1alpha1.InterfaceList{}
Expect(k8sManager.GetClient().List(ctx, intfList, client.InNamespace(metav1.NamespaceDefault), client.MatchingFields{v1alpha1.DeviceRefIndexKey: device.Name})).To(Succeed())
Expand Down
Loading
Loading