Skip to content

Commit f2993a8

Browse files
alegacyclaude
andcommitted
Add BareMetalSwitch CRD and controller for switch config generation
Introduces the BareMetalSwitch (BMS) CRD for defining Top-of-Rack switches managed by the Ironic Networking Service. The controller watches BMS resources and credential secrets, then generates two output secrets: 1. Switch configs secret (IRONIC_SWITCH_CONFIGS_SECRET): a single INI-format file with per-switch sections including address, credentials, driver/device type, and optional fields. 2. Switch credentials secret (IRONIC_SWITCH_CREDENTIALS_SECRET): SSH private key files (keyed by <mac-address>.key) for switches using publickey authentication. Password-authenticated switches store credentials inline in the INI config instead. The controller is only registered when IRONIC_NETWORKING_ENABLED=true and requires IRONIC_SWITCH_CONFIGS_SECRET, IRONIC_SWITCH_CREDENTIALS_SECRET, and IRONIC_SWITCH_CREDENTIALS_PATH to be set at startup. Credential secret changes (e.g. password rotation, key replacement) trigger config regeneration via a secret watch with a mapper that filters to only secrets referenced by BMS resources. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Allain Legacy <alegacy@redhat.com>
1 parent 2fa94e5 commit f2993a8

22 files changed

Lines changed: 2443 additions & 1 deletion
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
Copyright 2025 The Metal3 Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
// SwitchCredentialType defines the type of credential used for switch authentication.
24+
type SwitchCredentialType string
25+
26+
const (
27+
// SwitchCredentialTypePassword indicates password-based authentication.
28+
SwitchCredentialTypePassword SwitchCredentialType = "password"
29+
// SwitchCredentialTypePublicKey indicates SSH public key-based authentication.
30+
SwitchCredentialTypePublicKey SwitchCredentialType = "publickey"
31+
)
32+
33+
// SwitchCredentials defines the credentials used to access the switch.
34+
type SwitchCredentials struct {
35+
// Type is the type of switch credentials.
36+
// This is currently limited to "password", but will be expanded to others.
37+
// +kubebuilder:validation:Enum=password;publickey
38+
// +kubebuilder:default=password
39+
Type SwitchCredentialType `json:"type"`
40+
41+
// The name of the secret containing the switch credentials.
42+
// For password authentication, requires keys "username" and "password"
43+
// For SSH key authentication, requires keys "username" and "ssh-privatekey".
44+
// In both cases, an optional "enable-secret" key can be provided if needed
45+
// to enable privileged mode.
46+
// +kubebuilder:validation:MinLength=1
47+
SecretName string `json:"secretName"`
48+
}
49+
50+
// BareMetalSwitchSpec defines the desired state of BareMetalSwitch.
51+
type BareMetalSwitchSpec struct {
52+
// Address is the network address of the switch (IP address or hostname).
53+
// +kubebuilder:validation:Required
54+
Address string `json:"address"`
55+
56+
// MACAddress is the MAC address of the switch management interface.
57+
// Used to correlate LLDP information from nodes to identify which switch they're connected to.
58+
// +kubebuilder:validation:Pattern=`^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}$`
59+
// +kubebuilder:validation:Required
60+
MACAddress string `json:"macAddress"`
61+
62+
// Driver specifies the driver to use. Currently only "generic-switch" is supported.
63+
// +kubebuilder:validation:Enum=generic-switch
64+
// +kubebuilder:default=generic-switch
65+
// +optional
66+
Driver string `json:"driver,omitempty"`
67+
68+
// DeviceType specifies the device type for the generic-switch driver.
69+
// Must be one of the device types supported by networking-generic-switch.
70+
// Examples: netmiko_cisco_ios, netmiko_dell_force10, netmiko_dell_os10,
71+
// netmiko_juniper_junos, netmiko_arista_eos
72+
// See https://github.com/openstack/networking-generic-switch/blob/master/setup.cfg
73+
// +kubebuilder:validation:Required
74+
DeviceType string `json:"deviceType"`
75+
76+
// Credentials references the secret containing switch authentication credentials.
77+
// +kubebuilder:validation:Required
78+
Credentials SwitchCredentials `json:"credentials"`
79+
80+
// Port specifies the management port to connect to (e.g., SSH port 22, HTTPS port 443).
81+
// If not specified, the driver will use its default port based on the device type.
82+
// +kubebuilder:validation:Minimum=1
83+
// +kubebuilder:validation:Maximum=65535
84+
// +optional
85+
Port *int32 `json:"port,omitempty"`
86+
87+
// DisableCertificateVerification disables TLS certificate verification when using
88+
// HTTPS to connect to the switch. This is required for self-signed certificates,
89+
// but is insecure as it allows man-in-the-middle attacks.
90+
// +optional
91+
DisableCertificateVerification *bool `json:"disableCertificateVerification,omitempty"`
92+
}
93+
94+
// +kubebuilder:object:root=true
95+
// +kubebuilder:resource:scope=Namespaced,shortName=bms
96+
// +kubebuilder:printcolumn:name="Driver",type="string",JSONPath=".spec.driver",description="Switch driver type"
97+
// +kubebuilder:printcolumn:name="Device Type",type="string",JSONPath=".spec.deviceType",description="Switch device type"
98+
// +kubebuilder:printcolumn:name="Address",type="string",JSONPath=".spec.address",description="Switch address"
99+
// +kubebuilder:printcolumn:name="Credential Type",type="string",JSONPath=".spec.credentials.type",description="Credential Type"
100+
101+
// BareMetalSwitch represents a Top-of-Rack switch managed by Ironic Networking.
102+
type BareMetalSwitch struct {
103+
metav1.TypeMeta `json:",inline"`
104+
metav1.ObjectMeta `json:"metadata,omitempty"`
105+
106+
Spec BareMetalSwitchSpec `json:"spec,omitempty"`
107+
}
108+
109+
// +kubebuilder:object:root=true
110+
111+
// BareMetalSwitchList contains a list of BareMetalSwitch.
112+
type BareMetalSwitchList struct {
113+
metav1.TypeMeta `json:",inline"`
114+
metav1.ListMeta `json:"metadata,omitempty"`
115+
Items []BareMetalSwitch `json:"items"`
116+
}
117+
118+
func init() {
119+
SchemeBuilder.Register(&BareMetalSwitch{}, &BareMetalSwitchList{})
120+
}

apis/metal3.io/v1alpha1/zz_generated.deepcopy.go

Lines changed: 99 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
annotations:
6+
controller-gen.kubebuilder.io/version: v0.16.5
7+
name: baremetalswitches.metal3.io
8+
spec:
9+
group: metal3.io
10+
names:
11+
kind: BareMetalSwitch
12+
listKind: BareMetalSwitchList
13+
plural: baremetalswitches
14+
shortNames:
15+
- bms
16+
singular: baremetalswitch
17+
scope: Namespaced
18+
versions:
19+
- additionalPrinterColumns:
20+
- description: Switch driver type
21+
jsonPath: .spec.driver
22+
name: Driver
23+
type: string
24+
- description: Switch device type
25+
jsonPath: .spec.deviceType
26+
name: Device Type
27+
type: string
28+
- description: Switch address
29+
jsonPath: .spec.address
30+
name: Address
31+
type: string
32+
- description: Credential Type
33+
jsonPath: .spec.credentials.type
34+
name: Credential Type
35+
type: string
36+
name: v1alpha1
37+
schema:
38+
openAPIV3Schema:
39+
description: BareMetalSwitch represents a Top-of-Rack switch managed by Ironic
40+
Networking.
41+
properties:
42+
apiVersion:
43+
description: |-
44+
APIVersion defines the versioned schema of this representation of an object.
45+
Servers should convert recognized schemas to the latest internal value, and
46+
may reject unrecognized values.
47+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
48+
type: string
49+
kind:
50+
description: |-
51+
Kind is a string value representing the REST resource this object represents.
52+
Servers may infer this from the endpoint the client submits requests to.
53+
Cannot be updated.
54+
In CamelCase.
55+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
56+
type: string
57+
metadata:
58+
type: object
59+
spec:
60+
description: BareMetalSwitchSpec defines the desired state of BareMetalSwitch.
61+
properties:
62+
address:
63+
description: Address is the network address of the switch (IP address
64+
or hostname).
65+
type: string
66+
credentials:
67+
description: Credentials references the secret containing switch authentication
68+
credentials.
69+
properties:
70+
secretName:
71+
description: |-
72+
The name of the secret containing the switch credentials.
73+
For password authentication, requires keys "username" and "password"
74+
For SSH key authentication, requires keys "username" and "ssh-privatekey".
75+
In both cases, an optional "enable-secret" key can be provided if needed
76+
to enable privileged mode.
77+
minLength: 1
78+
type: string
79+
type:
80+
default: password
81+
description: |-
82+
Type is the type of switch credentials.
83+
This is currently limited to "password", but will be expanded to others.
84+
enum:
85+
- password
86+
- publickey
87+
type: string
88+
required:
89+
- secretName
90+
- type
91+
type: object
92+
deviceType:
93+
description: |-
94+
DeviceType specifies the device type for the generic-switch driver.
95+
Must be one of the device types supported by networking-generic-switch.
96+
Examples: netmiko_cisco_ios, netmiko_dell_force10, netmiko_dell_os10,
97+
netmiko_juniper_junos, netmiko_arista_eos
98+
See https://github.com/openstack/networking-generic-switch/blob/master/setup.cfg
99+
type: string
100+
disableCertificateVerification:
101+
description: |-
102+
DisableCertificateVerification disables TLS certificate verification when using
103+
HTTPS to connect to the switch. This is required for self-signed certificates,
104+
but is insecure as it allows man-in-the-middle attacks.
105+
type: boolean
106+
driver:
107+
default: generic-switch
108+
description: Driver specifies the driver to use. Currently only "generic-switch"
109+
is supported.
110+
enum:
111+
- generic-switch
112+
type: string
113+
macAddress:
114+
description: |-
115+
MACAddress is the MAC address of the switch management interface.
116+
Used to correlate LLDP information from nodes to identify which switch they're connected to.
117+
pattern: ^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}$
118+
type: string
119+
port:
120+
description: |-
121+
Port specifies the management port to connect to (e.g., SSH port 22, HTTPS port 443).
122+
If not specified, the driver will use its default port based on the device type.
123+
format: int32
124+
maximum: 65535
125+
minimum: 1
126+
type: integer
127+
required:
128+
- address
129+
- credentials
130+
- deviceType
131+
- macAddress
132+
type: object
133+
type: object
134+
served: true
135+
storage: true
136+
subresources: {}

config/base/crds/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ resources:
1515
- bases/metal3.io_hostupdatepolicies.yaml
1616
- bases/metal3.io_hostclaims.yaml
1717
- bases/metal3.io_hostdeploypolicies.yaml
18+
- bases/metal3.io_baremetalswitches.yaml
1819
#+kubebuilder:scaffold:crdkustomizeresource
1920

2021
patches:

config/base/rbac/role.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ rules:
8989
- apiGroups:
9090
- metal3.io
9191
resources:
92+
- baremetalswitches
9293
- hostdeploypolicies
9394
verbs:
9495
- get
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
resources:
4+
- ../roles-rolebindings
5+
- namespace.yaml
6+
7+
namespace: baremetalswitch

0 commit comments

Comments
 (0)