Skip to content

Commit ce4de83

Browse files
authored
Adapt network interfaces sorting (#6456)
This PR is the adaption of #6381 in networkd and xapi. Legacy: Keep the legacy behaviour, use host-installer, sort-script to sort and rename the network interfaces to `ethx`. New: Use `Network_device_order.sort` to sort the interfaces, store the result in networkd `config.interface_order`. Compatibility is offered by check the sort-script `interface-rename-data` dir. Add new interface [Interface.get_interface_positions](b2e7113) to pass interfaces and positions from networkd to xapi.
2 parents d248e2f + dbd1a3e commit ce4de83

File tree

9 files changed

+292
-74
lines changed

9 files changed

+292
-74
lines changed

ocaml/networkd/bin/network_server.ml

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,84 @@ let write_config () =
3535
try Network_config.write_config !config
3636
with Network_config.Write_error -> ()
3737

38+
let sort last_order =
39+
match last_order with
40+
| Some last_order -> (
41+
match Network_device_order.sort last_order with
42+
| Ok (interface_order, changes) ->
43+
(Some interface_order, changes)
44+
| Error err ->
45+
error "Failed to sort interface order [%s]"
46+
(Network_device_order.string_of_error err) ;
47+
(Some last_order, [])
48+
)
49+
| None ->
50+
(None, [])
51+
52+
let update_changes last_config changed_interfaces =
53+
let update_name name =
54+
let new_name =
55+
List.assoc_opt name changed_interfaces |> Option.value ~default:name
56+
in
57+
if name <> new_name then
58+
debug "Renaming %s to %s" name new_name ;
59+
new_name
60+
in
61+
let update_port (port, port_conf) =
62+
( update_name port
63+
, {port_conf with interfaces= List.map update_name port_conf.interfaces}
64+
)
65+
in
66+
let bridge_config =
67+
List.map
68+
(fun (bridge, bridge_conf) ->
69+
( bridge
70+
, {bridge_conf with ports= List.map update_port bridge_conf.ports}
71+
)
72+
)
73+
last_config.bridge_config
74+
in
75+
let interface_config =
76+
List.map
77+
(fun (name, conf) -> (update_name name, conf))
78+
last_config.interface_config
79+
in
80+
(bridge_config, interface_config)
81+
3882
let read_config () =
3983
try
4084
config := Network_config.read_config () ;
41-
debug "Read configuration from networkd.db file."
85+
debug "Read configuration from networkd.db file." ;
86+
let interface_order, changes = sort !config.interface_order in
87+
let bridge_config, interface_config = update_changes !config changes in
88+
config := {!config with bridge_config; interface_config; interface_order}
4289
with Network_config.Read_error -> (
4390
try
4491
(* No configuration file found. Try to get the initial network setup from
4592
* the first-boot data written by the host installer. *)
46-
config := Network_config.read_management_conf () ;
93+
let interface_order, _ = sort Network_config.initial_interface_order in
94+
config := Network_config.read_management_conf interface_order ;
4795
debug "Read configuration from management.conf file."
4896
with Network_config.Read_error ->
49-
debug "Could not interpret the configuration in management.conf"
97+
error "Could not interpret the configuration in management.conf"
5098
)
5199

100+
let get_index_from_ethx name =
101+
if String.starts_with ~prefix:"eth" name then
102+
let index = String.sub name 3 (String.length name - 3) in
103+
int_of_string_opt index
104+
else
105+
None
106+
107+
let sort_based_on_ethx () =
108+
Sysfs.list ()
109+
|> List.filter_map (fun name ->
110+
if Sysfs.is_physical name then
111+
get_index_from_ethx name |> Option.map (fun i -> (name, i))
112+
else
113+
None
114+
)
115+
52116
let on_shutdown signal =
53117
let dbg = "shutdown" in
54118
Debug.with_thread_associated dbg
@@ -63,13 +127,17 @@ let on_timer () = write_config ()
63127

64128
let clear_state () =
65129
write_lock := true ;
66-
config := Network_config.empty_config
130+
(* Do not clear interface_order, it is only maintained by networkd *)
131+
config :=
132+
{Network_config.empty_config with interface_order= !config.interface_order}
67133

68134
let sync_state () =
69135
write_lock := false ;
70136
write_config ()
71137

72-
let reset_state () = config := Network_config.read_management_conf ()
138+
let reset_state () =
139+
let interface_order, _ = sort Network_config.initial_interface_order in
140+
config := Network_config.read_management_conf interface_order
73141

74142
let set_gateway_interface _dbg name =
75143
(* Remove dhclient conf (if any) for the old and new gateway interfaces.
@@ -269,6 +337,24 @@ module Interface = struct
269337
let get_all dbg () =
270338
Debug.with_thread_associated dbg (fun () -> Sysfs.list ()) ()
271339

340+
let get_interface_positions dbg () =
341+
Debug.with_thread_associated dbg
342+
(fun () ->
343+
match !config.interface_order with
344+
| Some order ->
345+
List.filter_map
346+
(fun dev ->
347+
if dev.present then
348+
Some (dev.name, dev.position)
349+
else
350+
None
351+
)
352+
order
353+
| None ->
354+
sort_based_on_ethx ()
355+
)
356+
()
357+
272358
let exists dbg name =
273359
Debug.with_thread_associated dbg
274360
(fun () -> List.mem name (Sysfs.list ()))

ocaml/networkd/bin/networkd.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ let bind () =
183183
S.set_gateway_interface set_gateway_interface ;
184184
S.set_dns_interface set_dns_interface ;
185185
S.Interface.get_all Interface.get_all ;
186+
S.Interface.get_interface_positions Interface.get_interface_positions ;
186187
S.Interface.exists Interface.exists ;
187188
S.Interface.get_mac Interface.get_mac ;
188189
S.Interface.get_pci_bus_path Interface.get_pci_bus_path ;

ocaml/networkd/lib/network_config.ml

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,36 @@ exception Read_error
2222

2323
exception Write_error
2424

25-
let empty_config = default_config
25+
(* If the interface-rename script dir exists, the devices are already renamed
26+
to eth<N>, the <N> indicates device order *)
27+
let device_already_renamed =
28+
let dir = "/etc/sysconfig/network-scripts/interface-rename-data" in
29+
Sys.file_exists dir && Sys.is_directory dir
30+
31+
(* If devices have already been renamed, then interface_order is None,
32+
since the order is now reflected in their names. *)
33+
let initial_interface_order = if device_already_renamed then None else Some []
34+
35+
let empty_config =
36+
{default_config with interface_order= initial_interface_order}
2637

2738
let config_file_path = "/var/lib/xcp/networkd.db"
2839

2940
let temp_vlan = "xentemp"
3041

31-
let bridge_naming_convention (device : string) =
32-
if Astring.String.is_prefix ~affix:"eth" device then
42+
let bridge_name_of_device (device : string) =
43+
if String.starts_with ~prefix:"eth" device then
3344
"xenbr" ^ String.sub device 3 (String.length device - 3)
3445
else
3546
"br" ^ device
3647

48+
let bridge_naming_convention (device : string) pos_opt =
49+
match pos_opt with
50+
| Some index ->
51+
"xenbr" ^ string_of_int index
52+
| None ->
53+
bridge_name_of_device device
54+
3755
let get_list_from ~sep ~key args =
3856
List.assoc_opt key args
3957
|> Option.map (fun v -> Astring.String.cuts ~empty:false ~sep v)
@@ -79,7 +97,11 @@ let parse_dns_config args =
7997
let domains = get_list_from ~sep:" " ~key:"DOMAIN" args in
8098
(nameservers, domains)
8199

82-
let read_management_conf () =
100+
let write_manage_iface_to_inventory bridge_name =
101+
info "Writing management interface to inventory: %s" bridge_name ;
102+
Inventory.update Inventory._management_interface bridge_name
103+
104+
let read_management_conf interface_order =
83105
try
84106
let management_conf =
85107
Xapi_stdext_unix.Unixext.string_of_file
@@ -114,7 +136,12 @@ let read_management_conf () =
114136
| _, hd :: _ ->
115137
hd
116138
in
117-
Inventory.reread_inventory () ;
139+
let pos_opt =
140+
Option.bind interface_order @@ fun order ->
141+
List.find_map
142+
(fun x -> if x.name = device then Some x.position else None)
143+
order
144+
in
118145
let bridge_name =
119146
let inventory_bridge =
120147
try Some (Inventory.lookup Inventory._management_interface)
@@ -124,14 +151,16 @@ let read_management_conf () =
124151
| Some "" | None ->
125152
let bridge =
126153
if vlan = None then
127-
bridge_naming_convention device
154+
bridge_naming_convention device pos_opt
128155
else
129156
(* At this point, we don't know what the VLAN bridge name will be,
130157
* so use a temporary name. Xapi will replace the bridge once the name
131158
* has been decided on. *)
132159
temp_vlan
133160
in
134161
debug "No management bridge in inventory file... using %s" bridge ;
162+
if not device_already_renamed then
163+
write_manage_iface_to_inventory bridge ;
135164
bridge
136165
| Some bridge ->
137166
debug "Management bridge in inventory file: %s" bridge ;
@@ -176,7 +205,7 @@ let read_management_conf () =
176205
, [(bridge_name, primary_bridge_conf)]
177206
)
178207
| Some vlan ->
179-
let parent = bridge_naming_convention device in
208+
let parent = bridge_naming_convention device pos_opt in
180209
let secondary_bridge_conf =
181210
{
182211
default_bridge with
@@ -203,7 +232,7 @@ let read_management_conf () =
203232
; bridge_config
204233
; gateway_interface= Some bridge_name
205234
; dns_interface= Some bridge_name
206-
; interface_order= None
235+
; interface_order
207236
}
208237
with e ->
209238
error "Error while trying to read firstboot data: %s\n%s"

ocaml/networkd/lib/network_device_order.ml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ type error =
3636
| Duplicate_position
3737
| Invalid_biosdevname_key_value of (string * string)
3838

39+
let string_of_error = function
40+
| Pci_addr_parse_error s ->
41+
Printf.sprintf "Invalid PCI address: %s" s
42+
| Mac_addr_parse_error s ->
43+
Printf.sprintf "Invalid MAC address: %s" s
44+
| Rule_parse_error s ->
45+
Printf.sprintf "Invalid rule: %s" s
46+
| Missing_biosdevname_key k ->
47+
Printf.sprintf "Missing key in biosdevname output: %s" k
48+
| Duplicate_mac_address ->
49+
"Duplicate MAC address"
50+
| Duplicate_position ->
51+
"Duplicate position"
52+
| Invalid_biosdevname_key_value (k, v) ->
53+
Printf.sprintf "Invalid key-value pair in biosdevname output: %s=%s" k v
54+
3955
module Pciaddr = struct
4056
type t = Xcp_pci.address
4157

ocaml/networkd/lib/network_device_order.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ type error =
2424
| Duplicate_position
2525
| Invalid_biosdevname_key_value of (string * string)
2626

27+
val string_of_error : error -> string
28+
(** [string_of_error e] returns a string representation of the error [e]. *)
29+
2730
(** PCI address in format SBDF: domain:bus:device:function *)
2831
module Pciaddr : sig
2932
(** Type of the PCI address *)

ocaml/xapi-idl/network/network_interface.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,18 @@ module Interface_API (R : RPC) = struct
420420
["Get list of all interface names"]
421421
(debug_info_p @-> unit_p @-> returning iface_list_p err)
422422

423+
let get_interface_positions =
424+
let module T = struct
425+
type _iface_position_list_t = (iface * int) list [@@deriving rpcty]
426+
end in
427+
let iface_position_list_p =
428+
Param.mk ~description:["interface postion list"]
429+
T._iface_position_list_t
430+
in
431+
declare "Interface.get_interface_positions"
432+
["Get list of interface names and their positions"]
433+
(debug_info_p @-> unit_p @-> returning iface_position_list_p err)
434+
423435
let exists =
424436
let result = Param.mk ~description:["existence"] Types.bool in
425437
declare "Interface.exists"

ocaml/xapi/helpers.ml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,11 @@ let call_script ?(log_output = Always) ?env ?stdin ?timeout script args =
129129
raise e
130130

131131
(** Construct a descriptive network name (used as name_label) for a give network interface. *)
132-
let choose_network_name_for_pif device =
133-
Printf.sprintf "Pool-wide network associated with %s" device
132+
let choose_network_name_for_pif device pos_opt =
133+
let pos_str =
134+
Option.fold ~none:"" ~some:(Printf.sprintf " (slot %d)") pos_opt
135+
in
136+
Printf.sprintf "Pool-wide network associated with %s%s" device pos_str
134137

135138
(* !! FIXME - trap proper MISSINGREFERENCE exception when this has been defined *)
136139
(* !! FIXME(2) - this code could be shared with the CLI? *)

0 commit comments

Comments
 (0)