Skip to content

Commit 1be83eb

Browse files
committed
Add support for ironic-networking standalone service
In support of: metal3-io/metal3-docs#586 Add the ironic-networking service container entry point and configuration to enable standalone network management of baremetal nodes via networking-generic-switch (NGS). This allows Ironic to manage switch port configurations during node provisioning, cleaning, inspection, servicing, and rescuing operations. Changes: - Add runironic-networking entry point and configure-ironic-networking script with associated environment variable defaults - Add ironic-networking.conf.j2 Jinja2 configuration template - Add NGS_SOURCE and INSTALL_NGS build arguments to Dockerfile and build-wheels.sh for optional networking-generic-switch installation - Update ironic.conf.j2 to conditionally enable the ironic-networking network interface and configure JSON-RPC for the networking service - Update auth-common.sh to accept a configurable config group name so it can be reused for ironic_networking_json_rpc authentication - Add inspection hook local-link-connection and force_dhcp setting when networking is enabled - Document new environment variables in README.md - Update hack/prepare-irso-tests.sh for NGS source builds Signed-off-by: Allain Legacy <alegacy@redhat.com>
1 parent a9b2ccc commit 1be83eb

14 files changed

+233
-12
lines changed

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,15 @@ FROM $BASE_IMAGE AS ironic-wheel-builder
6161
ARG UPPER_CONSTRAINTS_FILE=upper-constraints.txt
6262
ARG IRONIC_SOURCE=eda12b9caf56ce6c454b0ff75a8e50acfe5f5f58 # master
6363
ARG SUSHY_SOURCE
64+
ARG NGS_SOURCE=e10fe3898cb4bd69941ceab668692eaf3ce65ac7 # master
65+
ARG INSTALL_NGS=true
6466
ARG PIP_VERSION
6567
ARG SETUPTOOLS_VERSION
6668

6769
ENV IRONIC_SOURCE=${IRONIC_SOURCE} \
6870
SUSHY_SOURCE=${SUSHY_SOURCE} \
71+
NGS_SOURCE=${NGS_SOURCE} \
72+
INSTALL_NGS=${INSTALL_NGS} \
6973
UPPER_CONSTRAINTS_FILE=${UPPER_CONSTRAINTS_FILE} \
7074
PIP_VERSION=${PIP_VERSION} \
7175
SETUPTOOLS_VERSION=${SETUPTOOLS_VERSION}
@@ -142,6 +146,7 @@ cp /tmp/ironic-config/inspector.ipxe.j2 /tmp/ironic-config/httpd-ironic-api.conf
142146
/templates/
143147
mkdir -p /etc/ironic
144148
cp /tmp/ironic-config/ironic.conf.j2 /etc/ironic/
149+
cp /tmp/ironic-config/ironic-networking.conf.j2 /etc/ironic/
145150
cp /tmp/ironic-config/httpd.conf.j2 /etc/httpd/conf/
146151
cp /tmp/ironic-config/httpd-modules.conf /etc/httpd/conf.modules.d/
147152
cp /tmp/ironic-config/apache2-vmedia.conf.j2 /templates/httpd-vmedia.conf.j2

README.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ The following entry points are provided:
2727
the provisioning of baremetal nodes. Details on Ironic can be found at
2828
<https://docs.openstack.org/ironic/latest/>. This is the default entry point
2929
used by the Dockerfile.
30+
- `runironic-networking` - Starts the ironic-networking service for standalone
31+
network management of baremetal nodes. This service handles switch port
32+
configuration for node provisioning, cleaning, and inspection operations.
3033
- `rundnsmasq` - Runs the dnmasq dhcp server to provide addresses and initiate
3134
PXE boot of baremetal nodes. This includes a lightweight TFTP server.
3235
Details on dnsmasq can be found at
@@ -262,6 +265,39 @@ media HTTP server configuration:
262265
`true` will make the server enforce its cipher list ordering for TLS version
263266
up to 1.2, defaults to `false`
264267

268+
## Ironic Networking
269+
270+
The ironic networking configuration can be overridden by various environment
271+
variables. The following can serve as an example of those most common settings.
272+
273+
- `IRONIC_NETWORKING_ENABLED` - Enable standalone networking service
274+
(default `false`)
275+
- `IRONIC_NETWORKING_JSON_RPC_HOST` - JSON-RPC host for networking service
276+
(default `localhost`)
277+
- `IRONIC_NETWORKING_JSON_RPC_PORT` - JSON-RPC port for networking service
278+
(default `8090`)
279+
- `IRONIC_NETWORKING_ENABLED_SWITCH_DRIVERS` - Enabled switch drivers (default
280+
`generic-switch`)
281+
- `IRONIC_NETWORKING_SWITCH_CONFIGS` - Path to switch configuration file.
282+
Switch configurations and SSH keys should be stored in a Secret and mounted
283+
to the Pod as a file and referenced by this variable.
284+
(default `${IRONIC_CONF_DIR}/networking/switch-configs.conf`)
285+
- `IRONIC_NETWORKING_PROVISIONING_NETWORK` - Network details for the
286+
provisioning network (e.g., mode=access/native_vlan=123)
287+
- `IRONIC_NETWORKING_INSPECTION_NETWORK` - Network details for the inspection
288+
network (e.g., mode=access/native_vlan=123)
289+
- `IRONIC_NETWORKING_SERVICING_NETWORK` - Network details for the servicing
290+
network (e.g., mode=access/native_vlan=123)
291+
- `IRONIC_NETWORKING_CLEANING_NETWORK` - Network details for the cleaning
292+
network (e.g., mode=access/native_vlan=123)
293+
- `IRONIC_NETWORKING_RESCUING_NETWORK` - Network details for the rescuing
294+
network (e.g., mode=access/native_vlan=123)
295+
- `IRONIC_NETWORKING_IDLE_NETWORK` - Network details for the idle
296+
network (e.g., mode=access/native_vlan=123)
297+
298+
**Note:** The ironic-networking service requires the `networking-generic-switch`
299+
package.
300+
265301
## Using a read-only root filesystem
266302

267303
The ironic-image can operate with a read-only root filesystem. However,
@@ -277,8 +313,12 @@ share runtime data between Ironic and HTTPD.
277313
## Custom source for ironic software
278314

279315
When building the ironic image, it is also possible to specify a
280-
different source for ironic or the sushy library using the build
281-
arguments **IRONIC_SOURCE** and **SUSHY_SOURCE**.
316+
different source for ironic, the sushy library, or NGS using the build
317+
arguments **IRONIC_SOURCE**, **SUSHY_SOURCE**, and **NGS_SOURCE**.
318+
The **INSTALL_NGS** build argument (default `true`) controls whether the
319+
`networking-generic-switch` package is included in the image. Setting
320+
**NGS_SOURCE** specifies the version to install but is only used when
321+
**INSTALL_NGS** is `true`.
282322
The accepted formats are gerrit refs, like _refs/changes/89/860689/2_,
283323
commit hashes, like _a1fe6cb41e6f0a1ed0a43ba5e17745714f206f1f_,
284324
repo tags or branches, or a local directory that needs to be under the

build-wheels.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ if [[ -n ${SUSHY_SOURCE:-} ]]; then
2929
sed -i '/^sushy===/d' "${UPPER_CONSTRAINTS_PATH}"
3030
fi
3131

32+
# Remove networking-generic-switch constraint if building from source
33+
if [[ -n ${NGS_SOURCE:-} ]]; then
34+
sed -i '/^networking-generic-switch===/d' "${UPPER_CONSTRAINTS_PATH}"
35+
fi
36+
3237
# Build wheels for all packages
3338
# Note: some packages may not produce wheels (pure Python), but pip wheel handles this
3439
python3.12 -m pip wheel \

hack/prepare-irso-tests.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ if [[ -n "${SUSHY_SOURCE:-}" ]]; then
2828
fi
2929
build_args+=(--build-arg SUSHY_SOURCE=sushy)
3030
fi
31+
if [[ -n "${NGS_SOURCE:-}" ]]; then
32+
if [[ -d "${NGS_SOURCE}" ]]; then
33+
cp -ra "${NGS_SOURCE}" sources/networking-generic-switch
34+
rm -rf sources/networking-generic-switch/.tox
35+
to_delete+=(sources/networking-generic-switch)
36+
fi
37+
build_args+=(--build-arg NGS_SOURCE=networking-generic-switch)
38+
fi
3139

3240
"${CONTAINER_RUNTIME}" build -t "${IRONIC_CUSTOM_IMAGE}" "${build_args[@]}" .
3341

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[DEFAULT]
2+
debug = true
3+
auth_strategy = http_basic
4+
http_basic_auth_user_file = {{ env.IRONIC_RPC_HTPASSWD_FILE }}
5+
rpc_transport = json-rpc
6+
use_stderr = true
7+
8+
[ironic_networking_json_rpc]
9+
auth_strategy = http_basic
10+
http_basic_auth_user_file = {{ env.IRONIC_RPC_HTPASSWD_FILE }}
11+
host_ip = {{ env.IRONIC_NETWORKING_JSON_RPC_HOST }}
12+
port = {{ env.IRONIC_NETWORKING_JSON_RPC_PORT }}
13+
{% if env.IRONIC_TLS_SETUP == "true" %}
14+
use_ssl = true
15+
cafile = {{ env.IRONIC_CACERT_FILE }}
16+
insecure = {{ env.IRONIC_INSECURE }}
17+
{% endif %}
18+
19+
{% if env.IRONIC_TLS_SETUP == "true" %}
20+
[ssl]
21+
cert_file = {{ env.IRONIC_CERT_FILE }}
22+
key_file = {{ env.IRONIC_KEY_FILE }}
23+
{% endif %}
24+
25+
[ironic_networking]
26+
enabled_switch_drivers = {{ env.IRONIC_NETWORKING_ENABLED_SWITCH_DRIVERS }}
27+
driver_config_dir = {{ env.IRONIC_NETWORKING_DRIVER_CONFIG_DIR }}
28+
switch_config_file = {{ env.IRONIC_NETWORKING_SWITCH_CONFIGS }}

ironic-config/ironic.conf.j2

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ auth_strategy = noauth
33
debug = true
44
default_deploy_interface = direct
55
default_inspect_interface = agent
6+
{%- if env.IRONIC_NETWORKING_ENABLED | lower == "true" %}
7+
default_network_interface = ironic-networking
8+
{% else %}
69
default_network_interface = noop
10+
{% endif -%}
711
enabled_bios_interfaces = no-bios,redfish,idrac-redfish
812
enabled_boot_interfaces = ipxe,pxe,fake,redfish-virtual-media,idrac-redfish-virtual-media,redfish-https
913
enabled_deploy_interfaces = direct,fake,ramdisk,custom-agent,bootc
@@ -13,7 +17,11 @@ enabled_firmware_interfaces = no-firmware,fake,redfish
1317
enabled_hardware_types = ipmi,idrac,fake-hardware,redfish,manual-management
1418
enabled_inspect_interfaces = agent,fake,redfish
1519
enabled_management_interfaces = ipmitool,fake,redfish,idrac-redfish,noop
20+
{%- if env.IRONIC_NETWORKING_ENABLED | lower == "true" %}
21+
enabled_network_interfaces = ironic-networking,noop
22+
{% else %}
1623
enabled_network_interfaces = noop
24+
{% endif -%}
1725
enabled_power_interfaces = ipmitool,fake,redfish,idrac-redfish
1826
enabled_raid_interfaces = no-raid,agent,fake,redfish,idrac-redfish
1927
enabled_vendor_interfaces = no-vendor,ipmitool,idrac-redfish,redfish,fake
@@ -155,9 +163,10 @@ power_off = {{ false if env.IRONIC_FAST_TRACK == "true" else true }}
155163
# Also keep in mind that only parameters unique for inspection go here.
156164
# No need to duplicate pxe_append_params/kernel_append_params.
157165
extra_kernel_params = ipa-inspection-collectors={{ env.IRONIC_IPA_COLLECTORS }} ipa-enable-vlan-interfaces={{ env.IRONIC_ENABLE_VLAN_INTERFACES }} ipa-inspection-dhcp-all-interfaces=1 ipa-collect-lldp={{ env.IRONIC_IPA_COLLECT_LLDP | default('1') }}
158-
hooks = $default_hooks{% if env.IRONIC_IPA_COLLECT_LLDP | default('1') == '1' %},parse-lldp{% endif %}
166+
hooks = $default_hooks{% if env.IRONIC_IPA_COLLECT_LLDP | default('1') == '1' %},parse-lldp{% if env.IRONIC_NETWORKING_ENABLED | lower == "true" %},local-link-connection{% endif %}{% endif %}
159167
add_ports = all
160168
keep_ports = present
169+
force_dhcp = {{ env.IRONIC_FORCE_DHCP }}
161170

162171
[auto_discovery]
163172
enabled = {{ env.IRONIC_ENABLE_DISCOVERY }}
@@ -265,3 +274,39 @@ key_file = {{ env.IRONIC_KEY_FILE }}
265274
{% if env.IRONIC_OCI_AUTH_CONFIG is defined %}
266275
authentication_config = {{ env.IRONIC_OCI_AUTH_CONFIG }}
267276
{% endif %}
277+
278+
{% if env.IRONIC_NETWORKING_ENABLED | lower == "true" %}
279+
[ironic_networking_json_rpc]
280+
auth_strategy = http_basic
281+
http_basic_auth_user_file = {{ env.IRONIC_RPC_HTPASSWD_FILE }}
282+
host_ip = {{ env.IRONIC_NETWORKING_JSON_RPC_HOST }}
283+
port = {{ env.IRONIC_NETWORKING_JSON_RPC_PORT }}
284+
{% if env.IRONIC_TLS_SETUP == "true" %}
285+
use_ssl = true
286+
cafile = {{ env.IRONIC_CACERT_FILE }}
287+
insecure = {{ env.IRONIC_INSECURE }}
288+
{% endif %}
289+
{% endif %}
290+
291+
{% if env.IRONIC_NETWORKING_ENABLED | lower == "true" %}
292+
[ironic_networking]
293+
rpc_transport = json-rpc
294+
{%- if env.IRONIC_NETWORKING_IDLE_NETWORK is defined %}
295+
idle_network = {{ env.IRONIC_NETWORKING_IDLE_NETWORK }}
296+
{%- endif %}
297+
{%- if env.IRONIC_NETWORKING_PROVISIONING_NETWORK is defined %}
298+
provisioning_network = {{ env.IRONIC_NETWORKING_PROVISIONING_NETWORK }}
299+
{%- endif %}
300+
{%- if env.IRONIC_NETWORKING_INSPECTION_NETWORK is defined %}
301+
inspection_network = {{ env.IRONIC_NETWORKING_INSPECTION_NETWORK }}
302+
{%- endif %}
303+
{%- if env.IRONIC_NETWORKING_CLEANING_NETWORK is defined %}
304+
cleaning_network = {{ env.IRONIC_NETWORKING_CLEANING_NETWORK }}
305+
{%- endif %}
306+
{%- if env.IRONIC_NETWORKING_RESCUING_NETWORK is defined %}
307+
rescuing_network = {{ env.IRONIC_NETWORKING_RESCUING_NETWORK }}
308+
{%- endif %}
309+
{%- if env.IRONIC_NETWORKING_SERVICING_NETWORK is defined %}
310+
servicing_network = {{ env.IRONIC_NETWORKING_SERVICING_NETWORK }}
311+
{%- endif %}
312+
{% endif %}

ironic-packages-list

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,14 @@ sushy @ git+https://opendev.org/openstack/sushy@{{ env.SUSHY_SOURCE }}
1616
{% else %}
1717
sushy
1818
{% endif %}
19+
{% if env.INSTALL_NGS | lower == "true" %}
20+
{% if env.NGS_SOURCE %}
21+
{% if path.isdir('/sources/' + env.NGS_SOURCE) %}
22+
git+file:///sources/{{ env.NGS_SOURCE }}
23+
{% else %}
24+
networking-generic-switch @ git+https://opendev.org/openstack/networking-generic-switch@{{ env.NGS_SOURCE }}
25+
{% endif %}
26+
{% else %}
27+
networking-generic-switch
28+
{% endif %}
29+
{% endif %}

renovate.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@
9696
],
9797
"datasourceTemplate": "pypi",
9898
"depNameTemplate": "setuptools"
99+
},
100+
{
101+
"customType": "regex",
102+
"managerFilePatterns": [
103+
"/^Dockerfile$/"
104+
],
105+
"matchStrings": [
106+
"ARG NGS_SOURCE=(?<currentDigest>[a-f0-9]{40})\\s+#\\s*(?<currentValue>[^\\n]+)"
107+
],
108+
"datasourceTemplate": "git-refs",
109+
"depNameTemplate": "networking-generic-switch",
110+
"packageNameTemplate": "https://github.com/openstack/networking-generic-switch.git",
111+
"versioningTemplate": "loose",
112+
"autoReplaceStringTemplate": "ARG NGS_SOURCE={{#if newDigest}}{{{newDigest}}}{{else}}{{{newValue}}}{{/if}} # {{{currentValue}}}"
99113
}
100114
]
101115
}

scripts/auth-common.sh

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,20 @@ fi
4646

4747
configure_json_rpc_auth()
4848
{
49-
if [[ "${IRONIC_EXPOSE_JSON_RPC}" != "true" ]]; then
50-
return
51-
fi
49+
local GROUP=${1:-json_rpc}
5250

5351
local auth_config_file="/auth/ironic-rpc/auth-config"
5452
local username_file="/auth/ironic-rpc/username"
5553
local password_file="/auth/ironic-rpc/password"
5654
if [[ -f "${username_file}" ]] && [[ -f "${password_file}" ]]; then
57-
crudini --set "${IRONIC_CONFIG}" json_rpc username "$(<${username_file})"
55+
crudini --set "${IRONIC_CONFIG}" "${GROUP}" username "$(<${username_file})"
5856
set +x
59-
crudini --set "${IRONIC_CONFIG}" json_rpc password "$(<${password_file})"
57+
crudini --set "${IRONIC_CONFIG}" "${GROUP}" password "$(<${password_file})"
6058
set -x
6159
elif [[ -f "${auth_config_file}" ]]; then
6260
echo "WARNING: using auth-config is deprecated, mount a secret directly"
63-
# Merge configurations in the "auth" directory into the default ironic configuration file
64-
crudini --merge "${IRONIC_CONFIG}" < "${auth_config_file}"
61+
# Merge configurations in the "auth" directory into the target group
62+
crudini --merge "${IRONIC_CONFIG}" "${GROUP}" < "${auth_config_file}"
6563
else
6664
echo "FATAL: no client-side credentials provided for JSON RPC"
6765
echo "HINT: mount a secret with username and password fields under /auth/ironic-rpc"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/bash
2+
3+
set -euxo pipefail
4+
5+
# No need to rely on this being set by whoever created this service since this
6+
# only gets run when ironic-networking is enabled.
7+
export IRONIC_NETWORKING_ENABLED=true
8+
9+
# shellcheck disable=SC1091
10+
. /bin/tls-common.sh
11+
# shellcheck disable=SC1091
12+
. /bin/ironic-common.sh
13+
# shellcheck disable=SC1091
14+
. /bin/ironic-networking-common.sh
15+
# shellcheck disable=SC1091
16+
. /bin/auth-common.sh
17+
18+
# zero makes it do cpu number detection on Ironic side
19+
export NUMWORKERS=${NUMWORKERS:-0}
20+
21+
if [[ -f "${IRONIC_CONF_DIR}/ironic.conf" ]]; then
22+
# Make a copy of the original supposed empty configuration file
23+
cp "${IRONIC_CONF_DIR}/ironic.conf" "${IRONIC_CONF_DIR}/ironic.conf.orig"
24+
fi
25+
26+
# The original ironic.conf is empty, and can be found in ironic.conf.orig
27+
render_j2_config "/etc/ironic/ironic-networking.conf.j2" \
28+
"${IRONIC_CONF_DIR}/ironic.conf"
29+
30+
configure_json_rpc_auth "ironic_networking_json_rpc"
31+
32+
# Make sure ironic traffic bypasses any proxies
33+
export NO_PROXY="${NO_PROXY:-},${IRONIC_IP}"
34+
35+
# Ensure driver config directory exists
36+
mkdir -p "${IRONIC_NETWORKING_DRIVER_CONFIG_DIR}"

0 commit comments

Comments
 (0)