Skip to content
Draft
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
1 change: 1 addition & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extend-ignore-re = [
[default.extend-words]
ser = "ser"
otu = "otu"
ISSU = "ISSU"
# Typo in name used by Cisco NX-OS for a configurable property.
# See: https://pubhub.devnetcloud.com/media/dme-docs-10-4-3/docs/System/snmp%3ACommSecP/#configurable-properties
acess = "acess"
Expand Down
2 changes: 2 additions & 0 deletions Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ k8s_yaml('./config/samples/v1alpha1_bgppeer.yaml')
k8s_resource(new_name='peer-spine1', objects=['leaf1-spine1:bgppeer'], resource_deps=['bgp', 'lo0'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
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_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 @@ -158,6 +159,7 @@ k8s_resource(new_name='vpcdomain', objects=['leaf1-vpcdomain:vpcdomain', 'leaf1-
k8s_yaml('./config/samples/cisco/nx/v1alpha1_interfaceconfig.yaml')
k8s_resource(new_name='spanning-tree-network', objects=['spanning-tree-network:interfaceconfig'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
k8s_resource(new_name='lacp-vpc', objects=['lacp-vpc:interfaceconfig'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])
k8s_resource(new_name='unnumbered-uplink', objects=['unnumbered-uplink:interfaceconfig'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False, labels=['samples'])

k8s_yaml('./config/samples/v1alpha1_nve.yaml')
k8s_resource(new_name='nve1', objects=['nve1:networkvirtualizationedge'], trigger_mode=TRIGGER_MODE_MANUAL, resource_deps=['lo0', 'lo1'], auto_init=False, labels=['samples'])
Expand Down
32 changes: 32 additions & 0 deletions api/cisco/nx/v1alpha1/interfaceconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,38 @@ type InterfaceConfigSpec struct {
// EVPNMultihoming defines EVPN ESI multihoming settings for the interface.
// +optional
EVPNMultihoming *EVPNMultihoming `json:"evpnMultihoming,omitempty"`

// IPv6 defines IPv6 settings for the interface that have no equivalent in
// the core Interface API. Neighbor Discovery settings are only managed while
// this is set: other ND settings on the device are left untouched, and
// removing it leaves the last applied settings on the device.
// +optional
IPv6 *InterfaceConfigIPv6 `json:"ipv6,omitempty"`
}

// InterfaceConfigIPv6 defines IPv6 settings for an interface.
type InterfaceConfigIPv6 struct {
// SuppressRouterAdvertisement stops the interface from sending IPv6 Router
// Advertisements. NX-OS sends them by default. Neighbours that discover
// each other over their link-local addresses, such as unnumbered BGP peers,
// depend on them, so this must stay disabled for such interfaces.
// Only applied to interfaces that carry IPv6 configuration.
// Maps to CLI command: ipv6 nd suppress-ra
// +required
SuppressRouterAdvertisement bool `json:"suppressRouterAdvertisement"`

// RouterAdvertisementInterval is the maximum interval between periodic
// IPv6 Router Advertisements, between 4s and 30m in whole seconds. The
// minimum interval is derived from it the way NX-OS does for the CLI
// command: a third of this value, but no less than 3s.
// Unnumbered BGP peers only discover each other once an advertisement is
// received, so a short interval speeds up session establishment.
// If not specified, the NX-OS default of 600s applies.
// Maps to CLI command: ipv6 nd ra-interval
// +optional
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$"
RouterAdvertisementInterval *metav1.Duration `json:"routerAdvertisementInterval,omitempty"`
}

// SpanningTree defines the spanning tree configuration for an interface.
Expand Down
25 changes: 25 additions & 0 deletions api/cisco/nx/v1alpha1/zz_generated.deepcopy.go

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

42 changes: 40 additions & 2 deletions api/core/v1alpha1/bgp_peer_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import (
)

// BGPPeerSpec defines the desired state of BGPPeer
// +kubebuilder:validation:XValidation:rule="has(self.address) != has(self.interfaceRef)", message="exactly one of address or interfaceRef must be specified"
// +kubebuilder:validation:XValidation:rule="!has(self.interfaceRef) || !has(self.localAddress)", message="localAddress must not be specified for interface-based peers"
// +kubebuilder:validation:XValidation:rule="type(self.asNumber) != string || self.asNumber != 'external' || has(self.interfaceRef)", message="asNumber external requires interfaceRef"
// +kubebuilder:validation:XValidation:rule="(!has(self.address) && !has(oldSelf.address)) || (has(self.address) && has(oldSelf.address) && self.address == oldSelf.address)",message="Address is immutable"
// +kubebuilder:validation:XValidation:rule="(!has(self.interfaceRef) && !has(oldSelf.interfaceRef)) || (has(self.interfaceRef) && has(oldSelf.interfaceRef) && self.interfaceRef == oldSelf.interfaceRef)",message="InterfaceRef is immutable"
type BGPPeerSpec struct {
// DeviceName is the name of the Device this object belongs to. The Device object must exist in the same namespace.
// Immutable.
Expand All @@ -27,7 +32,9 @@ type BGPPeerSpec struct {

// BgpRef is a reference to the BGP instance this peer belongs to.
// The BGP object must exist in the same namespace.
// Immutable.
// +required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="BgpRef is immutable"
BgpRef LocalObjectReference `json:"bgpRef"`

// AdminState indicates whether this BGP peer is administratively up or down.
Expand All @@ -37,12 +44,26 @@ type BGPPeerSpec struct {
AdminState AdminState `json:"adminState,omitempty"`

// Address is the IPv4 address of the BGP peer.
// +required
// Mutually exclusive with InterfaceRef: exactly one of both must be specified.
// Immutable.
// +optional
// +kubebuilder:validation:Format=ipv4
Address string `json:"address"`
Address string `json:"address,omitempty"`

// InterfaceRef is a reference to an Interface resource over which an unnumbered
// (interface-based) BGP session is established. The peers discover each other over
// their IPv6 link-local addresses, so the link needs no addressing of its own.
// The referenced Interface must belong to the same Device, exist in the same namespace,
// and be configured for link-local operation (spec.ipv6.useLinkLocalOnly).
// Mutually exclusive with Address: exactly one of both must be specified.
// Immutable.
// +optional
InterfaceRef *LocalObjectReference `json:"interfaceRef,omitempty"`

// ASNumber is the autonomous system number (ASN) of the BGP peer.
// Supports both plain format (1-4294967295) and dotted notation (0-65535.0-65535) as per RFC 5396.
// The special value "external" configures a dynamic AS number, accepting any AS number
// that differs from the local one. It is only valid together with InterfaceRef.
// +required
ASNumber intstr.IntOrString `json:"asNumber"`

Expand All @@ -66,6 +87,16 @@ type BGPPeerSpec struct {
LocalAS *LocalAS `json:"localAS,omitempty"`
}

// BGPPeerASNumberExternal is the value of BGPPeerSpec.ASNumber that requests a dynamic
// AS number for the peer. The session is established with any AS number that differs from
// the local one, which is the common setup for unnumbered eBGP peerings.
const BGPPeerASNumberExternal = "external"

// IsExternalASNumber reports whether the peer is configured with a dynamic AS number.
func (s *BGPPeerSpec) IsExternalASNumber() bool {
return s.ASNumber.Type == intstr.String && s.ASNumber.StrVal == BGPPeerASNumberExternal
}

// LocalAS defines the local AS configuration and how it factors in BGP announcements.
type LocalAS struct {
// ASNumber specifies a local AS number to present in BGP sessions with this peer.
Expand Down Expand Up @@ -178,6 +209,12 @@ type BGPPeerStatus struct {
// +patchMergeKey=afiSafi
AddressFamilies []AddressFamilyStatus `json:"addressFamilies,omitempty"`

// PeerInterface is the device-level name of the interface an unnumbered peer is
// configured over. It is recorded so that the peer can still be removed from the
// device after the referenced Interface has been deleted.
// +optional
PeerInterface string `json:"peerInterface,omitempty"`

// ObservedGeneration reflects the .metadata.generation that was last processed by the controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
Expand Down Expand Up @@ -254,6 +291,7 @@ const (
// +kubebuilder:resource:singular=bgppeer
// +kubebuilder:resource:shortName=peer;bgpneighbor
// +kubebuilder:printcolumn:name="Peer Address",type=string,JSONPath=`.spec.address`
// +kubebuilder:printcolumn:name="Peer Interface",type=string,JSONPath=`.spec.interfaceRef.name`
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Admin State",type=string,JSONPath=`.spec.adminState`
// +kubebuilder:printcolumn:name="AS Number",type=string,JSONPath=`.spec.asNumber`
Expand Down
12 changes: 12 additions & 0 deletions api/core/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ const VRFLabel = "networking.metal.ironcore.dev/vrf-name"
// to trigger certain disruptive operations, such as reboots or firmware upgrades.
const DeviceMaintenanceAnnotation = "networking.metal.ironcore.dev/maintenance"

// DeviceMaintenanceFirmwareTargetAnnotation specifies the target firmware image for a firmware upgrade.
// It also includes the MD5 checksum of the firmware image if available.
// The value format is {"url": "<url>", "md5": "<md5>"}
const DeviceMaintenanceFirmwareTargetAnnotation = "networking.metal.ironcore.dev/maintenance-target-firmware"

// PhysicalInterfaceNeighborLabel identifies the peer Interface resource on the other end of a physical link.
// The value must be the name of another Interface resource in the same namespace.
// This label is only valid for interfaces of type Physical.
Expand Down Expand Up @@ -113,6 +118,10 @@ const (
// spec.provisioning is defined.
// The annotation is always consumed once the device reaches Running.
DeviceMaintenanceSkipProvisioning = "skip-provisioning"
// DeviceMaintenanceFirmwareUpgrade triggers a firmware upgrade on the device. The provider initiates
// the upgrade workflow, which will apply the new firmware and reboot the device as necessary.
// The target firmware image is specified by the DeviceMaintenanceFirmwareTargetAnnotation.
DeviceMaintenanceFirmwareUpgrade = "firmware-upgrade"
)

// Condition types that are used across different objects.
Expand Down Expand Up @@ -267,6 +276,9 @@ const (
const (
// MaintenanceFailedReason indicates that a requested maintenance operation (e.g., reboot or factory reset) failed.
MaintenanceFailedReason = "MaintenanceFailed"
// MaintenanceInProgressReason indicates that a long-running maintenance
// operation (e.g., firmware upgrade) is still in progress and will be retried.
MaintenanceInProgressReason = "MaintenanceInProgress"
)

// Reasons that are specific to [RoutingPolicy] objects.
Expand Down
5 changes: 5 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.

Loading