|
16 | 16 | ; |
17 | 17 |
|
18 | 18 | # Internal network host entry |
| 19 | + # TODO Add sockets |
19 | 20 | hostEntrySubmodule = types.submodule { |
20 | 21 | options = { |
21 | 22 | name = mkOption { |
22 | 23 | type = types.str; |
23 | | - description = "Host name as string."; |
| 24 | + description = '' |
| 25 | + Host name as string. |
| 26 | + ''; |
24 | 27 | }; |
25 | 28 | mac = mkOption { |
26 | 29 | type = types.str; |
27 | | - description = "MAC address as string."; |
| 30 | + description = '' |
| 31 | + MAC address as string. |
| 32 | + ''; |
28 | 33 | }; |
29 | 34 | ipv4 = mkOption { |
30 | 35 | type = types.str; |
31 | | - description = "IPv4 address as string."; |
| 36 | + description = '' |
| 37 | + IPv4 address as string. |
| 38 | + ''; |
32 | 39 | }; |
33 | 40 | ipv6 = mkOption { |
34 | 41 | type = types.str; |
35 | | - description = "IPv6 address as string."; |
| 42 | + description = '' |
| 43 | + IPv6 address as string. |
| 44 | + ''; |
36 | 45 | }; |
37 | 46 | cid = mkOption { |
38 | 47 | type = types.int; |
39 | | - description = "Vsock CID (Context IDentifier) as integer."; |
| 48 | + description = '' |
| 49 | + Vsock CID (Context IDentifier) as integer: |
| 50 | + - VMADDR_CID_HYPERVISOR (0) is reserved for services built into the hypervisor |
| 51 | + - VMADDR_CID_LOCAL (1) is the well-known address for local communication (loopback) |
| 52 | + - VMADDR_CID_HOST (2) is the well-known address of the host |
| 53 | + ''; |
40 | 54 | }; |
41 | 55 | }; |
42 | 56 | }; |
|
77 | 91 |
|
78 | 92 | # Evaluate generated hosts as attrset |
79 | 93 | generatedHostAttrs = listToAttrs (map (host: nameValuePair host.name host) generatedHosts); |
80 | | - combinedHosts = |
81 | | - generatedHostAttrs |
82 | | - // builtins.trace "extraHosts: ${builtins.toJSON config.ghaf.networking.extraHosts}" config.ghaf.networking.extraHosts; |
| 94 | + # Extract names of all extra hosts |
| 95 | + extraHostNames = lib.attrNames config.ghaf.common.extraNetworking.hosts; |
| 96 | + |
| 97 | + # Merge logic per host |
| 98 | + mergedExtraHosts = listToAttrs ( |
| 99 | + map ( |
| 100 | + name: |
| 101 | + let |
| 102 | + gen = generatedHostAttrs.${name}; |
| 103 | + extra = config.ghaf.common.extraNetworking.hosts.${name}; |
| 104 | + in |
| 105 | + nameValuePair name { |
| 106 | + inherit name; |
| 107 | + mac = if extra ? mac && extra.mac != null then extra.mac else gen.mac; |
| 108 | + ipv4 = if extra ? ipv4 && extra.ipv4 != null then extra.ipv4 else gen.ipv4; |
| 109 | + ipv6 = if extra ? ipv6 && extra.ipv6 != null then extra.ipv6 else gen.ipv6; |
| 110 | + inherit (gen) cid; |
| 111 | + } |
| 112 | + ) extraHostNames |
| 113 | + ); |
| 114 | + |
| 115 | + # Combine generated and extra hosts (extra overrides or extends) |
| 116 | + combinedHosts = generatedHostAttrs // mergedExtraHosts; |
83 | 117 |
|
84 | 118 | # Trace |
85 | 119 | tracedCombinedHosts = builtins.trace "ghaf.networking.hosts (merged): ${builtins.toJSON combinedHosts}" combinedHosts; |
|
91 | 125 | }) (lib.attrValues combinedHosts) |
92 | 126 | ); |
93 | 127 | tracedNetworkingHosts = builtins.trace "networking.hosts (JSON): ${builtins.toJSON networkingHosts}" networkingHosts; |
| 128 | + # Extract values to check for uniqueness |
| 129 | + allHosts = lib.attrValues combinedHosts; |
| 130 | + getField = field: map (h: h.${field}) allHosts; |
| 131 | + |
| 132 | + checkUnique = |
| 133 | + field: |
| 134 | + let |
| 135 | + values = builtins.trace "Values: ${builtins.toJSON (getField field)}" (getField field); |
| 136 | + unique = lib.lists.unique values; |
| 137 | + |
| 138 | + # Find duplicates by filtering values that occur more than once |
| 139 | + duplicates = lib.lists.filter ( |
| 140 | + value: lib.lists.length (lib.lists.filter (x: x == value) values) > 1 |
| 141 | + ) unique; |
94 | 142 |
|
| 143 | + # Create a list of duplicates with the corresponding host names |
| 144 | + duplicateNames = lib.lists.filter ( |
| 145 | + host: lib.lists.length (lib.lists.filter (x: x == host.${field}) values) > 1 |
| 146 | + ) allHosts; |
| 147 | + |
| 148 | + in |
| 149 | + { |
| 150 | + inherit field; |
| 151 | + ok = values == unique; |
| 152 | + inherit duplicates; |
| 153 | + duplicateNames = map (host: host.name) duplicateNames; # Extract host names for duplicates |
| 154 | + }; |
| 155 | + |
| 156 | + uniquenessChecks = map checkUnique [ |
| 157 | + "mac" |
| 158 | + "ipv4" |
| 159 | + "ipv6" |
| 160 | + "cid" |
| 161 | + "name" |
| 162 | + ]; |
| 163 | + |
| 164 | + uniquenessAssertions = map (check: { |
| 165 | + assertion = check.ok; |
| 166 | + message = "Duplicate ${check.field} values detected: ${lib.concatStringsSep ", " check.duplicates}, conflict between:${lib.concatStringsSep ", " check.duplicateNames}"; |
| 167 | + |
| 168 | + }) uniquenessChecks; |
95 | 169 | in |
96 | 170 | { |
97 | 171 | options.ghaf.networking = { |
|
101 | 175 | default = { }; |
102 | 176 | }; |
103 | 177 |
|
104 | | - extraHosts = mkOption { |
105 | | - type = types.attrsOf hostEntrySubmodule; |
106 | | - description = "Extra host entries that override or extend the generated ones."; |
107 | | - default = { }; |
108 | | - }; |
109 | 178 | }; |
110 | 179 |
|
111 | 180 | config = { |
|
114 | 183 | assertion = lib.length config.ghaf.common.vms < 255; |
115 | 184 | message = "Too many VMs defined - maximum is 254"; |
116 | 185 | } |
117 | | - ]; |
| 186 | + ] ++ uniquenessAssertions; |
118 | 187 |
|
119 | 188 | ghaf.networking.hosts = tracedCombinedHosts; |
120 | 189 |
|
|
0 commit comments