]> git.ipfire.org Git - thirdparty/dracut.git/blame - modules.d/95nvmf/parse-nvmf-boot-connections.sh
feat(nvmf): add code for parsing the NBFT
[thirdparty/dracut.git] / modules.d / 95nvmf / parse-nvmf-boot-connections.sh
CommitLineData
2f03d69f
HR
1#!/bin/sh
2#
3# Supported formats:
e405501e
AAF
4# rd.nvmf.hostnqn=<hostnqn>
5# rd.nvmf.hostid=<hostid>
6# rd.nvmf.discover=<transport>,<traddr>,<host-traddr>,<trsvcid>
2f03d69f
HR
7#
8# Examples:
e405501e
AAF
9# rd.nvmf.hostnqn=nqn.2014-08.org.nvmexpress:uuid:37303738-3034-584d-5137-333230423843
10# rd.nvmf.discover=rdma,192.168.1.3,,4420
11# rd.nvmf.discover=tcp,192.168.1.3,,4420
12# rd.nvmf.discover=tcp,192.168.1.3
13# rd.nvmf.discover=fc,nn-0x200400a098d85236:pn-0x201400a098d85236,nn-0x200000109b7db455:pn-0x100000109b7db455
14# rd.nvmf.discover=fc,auto
2f03d69f
HR
15#
16# Note: FC does autodiscovery, so typically there is no need to
17# specify any discover parameters for FC.
18#
19
b490f6f7
MW
20command -v getarg > /dev/null || . /lib/dracut-lib.sh
21command -v is_ip > /dev/null || . /lib/net-lib.sh
22
23## Sample NBFT output from nvme show-nbft -H -s -d -o json
24# [
25# {
26# "filename":"/sys/firmware/acpi/tables/NBFT",
27# "host":{
28# "nqn":"nqn.2014-08.org.nvmexpress:uuid:d6f07002-7eb5-4841-a185-400e296afae4",
29# "id":"111919da-21ea-cc4e-bafe-216d8372dd31",
30# "host_id_configured":0,
31# "host_nqn_configured":0,
32# "primary_admin_host_flag":"not indicated"
33# },
34# "subsystem":[
35# {
36# "index":1,
37# "num_hfis":1,
38# "hfis":[
39# 1
40# ],
41# "transport":"tcp",
42# "transport_address":"192.168.100.216",
43# "transport_svcid":"4420",
44# "subsys_port_id":0,
45# "nsid":1,
46# "nid_type":"uuid",
47# "nid":"424d1c8a-8ef9-4681-b2fc-8c343bd8fa69",
48# "subsys_nqn":"timberland-01",
49# "controller_id":0,
50# "asqsz":0,
51# "pdu_header_digest_required":0,
52# "data_digest_required":0
53# }
54# ],
55# "hfi":[
56# {
57# "index":1,
58# "transport":"tcp",
59# "pcidev":"0:0:2.0",
60# "mac_addr":"52:54:00:4f:97:e9",
61# "vlan":0,
62# "ip_origin":63,
63# "ipaddr":"192.168.100.217",
64# "subnet_mask_prefix":24,
65# "gateway_ipaddr":"0.0.0.0",
66# "route_metric":0,
67# "primary_dns_ipaddr":"0.0.0.0",
68# "secondary_dns_ipaddr":"0.0.0.0",
69# "dhcp_server_ipaddr":"",
70# "this_hfi_is_default_route":1
71# }
72# ],
73# "discovery":[
74# ]
75# }
76# ]
77#
78# If the IP address is derived from DHCP, it sets the field
79# "hfi.dhcp_server_ipaddr" to a non-emtpy value.
80#
81#
82
83nbft_run_jq() {
84 local st
85 local opts="-e"
86
87 while [ $# -gt 0 ]; do
88 case $1 in
89 -*)
90 opts="$opts $1"
91 ;;
92 *)
93 break
94 ;;
95 esac
96 shift
97 done
98 # Not quoting is intentional here. We won't get glob expressions passed.
99 # shellcheck disable=SC2086
100 jq $opts "$1" << EOF
101$2
102EOF
103 st=$?
104 if [ $st -ne 0 ]; then
105 warn "NBFT: jq error while processing \"$1\""
106 return $st
107 else
108 return 0
109 fi
110}
111
112nbft_check_empty_address() {
113 # suppress meaningless or empty IP addresses
114 # "null" is returned by jq if no match found for expression
115 case $1 in
116 null | "::" | "0.0.0.0") ;;
117 *)
118 echo "$1"
119 ;;
120 esac
121}
122
123nbft_parse_hfi() {
124 # false positive of shellcheck - no expansion in variable assignments
125 # shellcheck disable=2086
126 local hfi_json=$1
127 local mac iface ipaddr prefix vlan gateway dns1 dns2 hostname adrfam dhcp
128
129 mac=$(nbft_run_jq -r .mac_addr "$hfi_json") || return 1
130 iface=$(set_ifname nbft "$mac")
131
132 vlan=$(nbft_run_jq .vlan "$hfi_json") || vlan=0
133 # treat VLAN zero as "no vlan"
134 [ "$vlan" -ne 0 ] || vlan=
135
136 [ ! -e /tmp/net."${iface}${vlan:+.$vlan}".has_ibft_config ] || return 0
137
138 dhcp=$(nbft_run_jq -r .dhcp_server_ipaddr "$hfi_json")
139 # We need to check $? here as the above is an assignment
140 # shellcheck disable=2181
141 if [ $? -eq 0 ] && [ "$dhcp" ] && [ "$dhcp" != null ]; then
142 case $dhcp in
143 *:*)
144 echo ip="$iface${vlan:+.$vlan}:dhcp6"
145 ;;
146 *.*.*.*)
147 echo ip="$iface${vlan:+.$vlan}:dhcp"
148 ;;
149 *)
150 warn "Invalid value for dhcp_server_ipaddr: $dhcp"
151 return 1
152 ;;
153 esac
154 else
155 ipaddr=$(nbft_run_jq -r .ipaddr "$hfi_json") || return 1
156
157 case $ipaddr in
158 *.*.*.*)
159 adrfam=ipv4
160 ;;
161 *:*)
162 adrfam=ipv6
163 ;;
164 *)
165 warn "invalid address: $ipaddr"
166 return 1
167 ;;
168 esac
169 prefix=$(nbft_run_jq -r .subnet_mask_prefix "$hfi_json")
170 # Need to check $? here as he above is an assignment
171 # shellcheck disable=2181
172 if [ $? -ne 0 ] && [ "$adrfam" = ipv6 ]; then
173 prefix=128
174 fi
175 # Use brackets for IPv6
176 if [ "$adrfam" = ipv6 ]; then
177 ipaddr="[$ipaddr]"
178 fi
179
180 gateway=$(nbft_check_empty_address \
181 "$(nbft_run_jq -r .gateway_ipaddr "$hfi_json")")
182 dns1=$(nbft_check_empty_address \
183 "$(nbft_run_jq -r .primary_dns_ipaddr "$hfi_json")")
184 dns2=$(nbft_check_empty_address \
185 "$(nbft_run_jq -r .secondary_dns_ipaddr "$hfi_json")")
186 hostname=$(nbft_run_jq -r .host_name "$hfi_json" 2> /dev/null) || hostname=
187
188 echo "ip=$ipaddr::$gateway:$prefix:$hostname:$iface${vlan:+.$vlan}:none${dns1:+:$dns1}${dns2:+:$dns2}"
189 fi
190
191 if [ "$vlan" ]; then
192 echo "vlan=$iface.$vlan:$iface"
193 echo "$mac" > "/tmp/net.$iface.$vlan.has_ibft_config"
194 else
195 echo "$mac" > "/tmp/net.$iface.has_ibft_config"
196 fi
197 : > /tmp/valid_nbft_entry_found
198}
199
200nbft_parse() {
201 local nbft_json n_nbft all_hfi_json n_hfi
202 local j=0 i
203
204 nbft_json=$(nvme nbft show -H -o json) || return 0
205 n_nbft=$(nbft_run_jq ". | length" "$nbft_json") || return 0
206
207 while [ "$j" -lt "$n_nbft" ]; do
208 all_hfi_json=$(nbft_run_jq ".[$j].hfi" "$nbft_json") || continue
209 n_hfi=$(nbft_run_jq ". | length" "$all_hfi_json") || continue
210 i=0
211
212 while [ "$i" -lt "$n_hfi" ]; do
213 nbft_parse_hfi "$(nbft_run_jq ".[$i]" "$all_hfi_json")"
214 i=$((i + 1))
215 done
216 j=$((j + 1))
217 done >> /etc/cmdline.d/40-nbft.conf
218}
019610af 219
9a52c3fd 220if getargbool 0 rd.nonvmf; then
019610af
EM
221 warn "rd.nonvmf=0: skipping nvmf"
222 return 0
223fi
224
b490f6f7
MW
225if getargbool 0 rd.nvmf.nostatic; then
226 rm -f /etc/cmdline.d/95nvmf-args.conf
227 rm -f /etc/nvme/discovery.conf /etc/nvme/config.json
228fi
229
230if ! getargbool 0 rd.nvmf.nonbft; then
231 for _x in /sys/firmware/acpi/tables/NBFT*; do
232 if [ -f "$_x" ]; then
233 nbft_parse
234 break
235 fi
236 done
237fi
238
a3cf4ec9 239initqueue --onetime modprobe --all -b -q nvme_tcp nvme_core nvme_fabrics
019610af 240
2f03d69f 241parse_nvmf_discover() {
4087fd4d
HR
242 traddr="none"
243 trtype="none"
244 hosttraddr="none"
245 trsvcid=4420
2f03d69f 246 OLDIFS="$IFS"
4087fd4d 247 IFS=,
c305877c
HH
248 # shellcheck disable=SC2086
249 set -- $1
2f03d69f
HR
250 IFS="$OLDIFS"
251
252 case $# in
253 2)
019610af
EM
254 [ -n "$1" ] && trtype=$1
255 [ -n "$2" ] && traddr=$2
2f03d69f
HR
256 ;;
257 3)
019610af
EM
258 [ -n "$1" ] && trtype=$1
259 [ -n "$2" ] && traddr=$2
260 [ -n "$3" ] && hosttraddr=$3
2f03d69f
HR
261 ;;
262 4)
019610af
EM
263 [ -n "$1" ] && trtype=$1
264 [ -n "$2" ] && traddr=$2
265 [ -n "$3" ] && hosttraddr=$3
266 [ -n "$4" ] && trsvcid=$4
2f03d69f
HR
267 ;;
268 *)
e405501e 269 warn "Invalid arguments for rd.nvmf.discover=$1"
251b4247 270 return 0
2f03d69f
HR
271 ;;
272 esac
9a52c3fd 273 if [ "$traddr" = "none" ]; then
2f03d69f 274 warn "traddr is mandatory for $trtype"
9a52c3fd 275 return 0
2f03d69f 276 fi
cf8986af
MW
277 if [ "$trtype" = "tcp" ]; then
278 : > /tmp/nvmf_needs_network
279 elif [ "$trtype" = "fc" ]; then
9a52c3fd 280 if [ "$traddr" = "auto" ]; then
f07117d6 281 rm -f /etc/nvme/discovery.conf /etc/nvme/config.json
251b4247
HR
282 return 1
283 fi
9a52c3fd 284 if [ "$hosttraddr" = "none" ]; then
2f03d69f 285 warn "host traddr is mandatory for fc"
251b4247 286 return 0
2f03d69f 287 fi
cf8986af 288 elif [ "$trtype" != "rdma" ]; then
2f03d69f 289 warn "unsupported transport $trtype"
251b4247 290 return 0
019610af 291 fi
9a52c3fd 292 if [ "$trtype" = "fc" ]; then
4087fd4d
HR
293 echo "--transport=$trtype --traddr=$traddr --host-traddr=$hosttraddr" >> /etc/nvme/discovery.conf
294 else
295 echo "--transport=$trtype --traddr=$traddr --host-traddr=$hosttraddr --trsvcid=$trsvcid" >> /etc/nvme/discovery.conf
296 fi
251b4247 297 return 0
2f03d69f
HR
298}
299
e405501e 300nvmf_hostnqn=$(getarg rd.nvmf.hostnqn -d nvmf.hostnqn=)
9a52c3fd 301if [ -n "$nvmf_hostnqn" ]; then
2f03d69f
HR
302 echo "$nvmf_hostnqn" > /etc/nvme/hostnqn
303fi
e405501e 304nvmf_hostid=$(getarg rd.nvmf.hostid -d nvmf.hostid=)
9a52c3fd 305if [ -n "$nvmf_hostid" ]; then
2f03d69f
HR
306 echo "$nvmf_hostid" > /etc/nvme/hostid
307fi
308
b490f6f7 309rm -f /tmp/nvmf-fc-auto
e405501e 310for d in $(getargs rd.nvmf.discover -d nvmf.discover=); do
556ef46a 311 parse_nvmf_discover "$d" || {
b490f6f7 312 : > /tmp/nvmf-fc-auto
556ef46a
MW
313 break
314 }
2f03d69f
HR
315done
316
b490f6f7 317if [ -e /tmp/nvmf_needs_network ] || [ -e /tmp/valid_nbft_entry_found ]; then
cf8986af 318 echo "rd.neednet=1" > /etc/cmdline.d/nvmf-neednet.conf
b490f6f7
MW
319 # netroot is a global variable that is present in all "sourced" scripts
320 # shellcheck disable=SC2034
321 netroot=nbft
cf8986af
MW
322 rm -f /tmp/nvmf_needs_network
323fi
324
b490f6f7
MW
325/sbin/initqueue --settled --onetime --name nvmf-connect-settled /sbin/nvmf-autoconnect.sh settled
326/sbin/initqueue --timeout --onetime --name nvmf-connect-timeout /sbin/nvmf-autoconnect.sh timeout