@@ -1093,7 +1093,7 @@ protected ExtensionResourceMap registerExtensionWithPhysicalNetwork(PhysicalNetw
10931093 }
10941094 }
10951095
1096- // Resolve which services this extension provides from its network.capabilities detail
1096+ // Resolve which services this extension provides from its network.services detail
10971097 Set <String > services = resolveExtensionServices (extension );
10981098
10991099 return Transaction .execute ((TransactionCallbackWithException <ExtensionResourceMap , CloudRuntimeException >) status -> {
@@ -1135,22 +1135,98 @@ protected ExtensionResourceMap registerExtensionWithPhysicalNetwork(PhysicalNetw
11351135
11361136 /**
11371137 * Resolves the set of network service names declared in the extension's
1138- * {@code network.capabilities} detail. Falls back to the full default set
1139- * if no capabilities are declared.
1138+ * {@code network.services} detail. Falls back to an empty set if not present
11401139 */
11411140 private Set <String > resolveExtensionServices (Extension extension ) {
11421141 Map <String , String > extDetails = extensionDetailsDao .listDetailsKeyPairs (extension .getId ());
1143- if (extDetails != null && extDetails .containsKey (ExtensionHelper .NETWORK_CAPABILITIES_DETAIL_KEY )) {
1144- Set <String > parsed = parseServicesFromCapabilitiesJson (
1145- extDetails .get (ExtensionHelper .NETWORK_CAPABILITIES_DETAIL_KEY ));
1146- if (!parsed .isEmpty ()) {
1147- return parsed ;
1148- }
1142+ Set <String > parsed = parseServicesFromDetailKeys (extDetails );
1143+ if (!parsed .isEmpty ()) {
1144+ return parsed ;
11491145 }
11501146 // Default: the full set of services NetworkExtensionElement supports
11511147 return new HashSet <>();
11521148 }
11531149
1150+ /**
1151+ * Resolves the set of service names from the extension detail map.
1152+ * From {@code network.services} comma-separated key.
1153+ */
1154+ @ SuppressWarnings ("deprecation" )
1155+ private Set <String > parseServicesFromDetailKeys (Map <String , String > extDetails ) {
1156+ if (extDetails == null ) {
1157+ return Collections .emptySet ();
1158+ }
1159+ // New format: "network.services" = "SourceNat,StaticNat,..."
1160+ if (extDetails .containsKey (ExtensionHelper .NETWORK_SERVICES_DETAIL_KEY )) {
1161+ String value = extDetails .get (ExtensionHelper .NETWORK_SERVICES_DETAIL_KEY );
1162+ if (StringUtils .isNotBlank (value )) {
1163+ Set <String > services = new HashSet <>();
1164+ for (String s : value .split ("," )) {
1165+ String trimmed = s .trim ();
1166+ if (!trimmed .isEmpty ()) {
1167+ services .add (trimmed );
1168+ }
1169+ }
1170+ if (!services .isEmpty ()) {
1171+ return services ;
1172+ }
1173+ }
1174+ }
1175+
1176+ return Collections .emptySet ();
1177+ }
1178+
1179+ /**
1180+ * Builds a full {@code Map<Service, Map<Capability, String>>} from the
1181+ * extension detail map. From the split keys
1182+ * {@code network.services} + {@code network.service.capabilities}.
1183+ */
1184+ @ SuppressWarnings ("deprecation" )
1185+ private Map <Service , Map <Capability , String >> buildCapabilitiesFromDetailKeys (
1186+ Map <String , String > extDetails ) {
1187+ if (extDetails == null ) {
1188+ return new HashMap <>();
1189+ }
1190+ // New split format
1191+ if (extDetails .containsKey (ExtensionHelper .NETWORK_SERVICES_DETAIL_KEY )) {
1192+ Set <String > serviceNames = parseServicesFromDetailKeys (extDetails );
1193+ if (!serviceNames .isEmpty ()) {
1194+ JsonObject capsObj = null ;
1195+ if (extDetails .containsKey (ExtensionHelper .NETWORK_SERVICE_CAPABILITIES_DETAIL_KEY )) {
1196+ try {
1197+ capsObj = JsonParser .parseString (
1198+ extDetails .get (ExtensionHelper .NETWORK_SERVICE_CAPABILITIES_DETAIL_KEY ))
1199+ .getAsJsonObject ();
1200+ } catch (Exception e ) {
1201+ logger .warn ("Failed to parse network.service.capabilities JSON: {}" , e .getMessage ());
1202+ }
1203+ }
1204+ Map <Service , Map <Capability , String >> result = new HashMap <>();
1205+ for (String svcName : serviceNames ) {
1206+ Service service = Service .getService (svcName );
1207+ if (service == null ) {
1208+ logger .warn ("Unknown network service '{}' in network.services — skipping" , svcName );
1209+ continue ;
1210+ }
1211+ Map <Capability , String > capMap = new HashMap <>();
1212+ if (capsObj != null && capsObj .has (svcName )) {
1213+ JsonObject svcCaps = capsObj .getAsJsonObject (svcName );
1214+ for (Map .Entry <String , JsonElement > entry : svcCaps .entrySet ()) {
1215+ Capability cap = Capability .getCapability (entry .getKey ());
1216+ if (cap != null ) {
1217+ capMap .put (cap , entry .getValue ().getAsString ());
1218+ }
1219+ }
1220+ }
1221+ result .put (service , capMap );
1222+ }
1223+ return result ;
1224+ }
1225+ }
1226+
1227+ return new HashMap <>();
1228+ }
1229+
11541230 /**
11551231 * Sets the boolean service-provided flags on a {@link PhysicalNetworkServiceProviderVO}
11561232 * based on a set of service names.
@@ -1176,23 +1252,18 @@ private void applyServicesToNsp(PhysicalNetworkServiceProviderVO nsp, Set<String
11761252
11771253 /**
11781254 * Validates that the comma-separated or JSON-array {@code servicesValue} is a
1179- * subset of the services declared in the extension's {@code network.capabilities }
1180- * detail. Throws {@link InvalidParameterValueException} if any service in the
1181- * request is not offered by the extension.
1255+ * subset of the services declared in the extension's {@code network.services }
1256+ * Throws {@link InvalidParameterValueException} if any service in the request is not
1257+ * offered by the extension.
11821258 */
11831259 protected void validateNetworkServicesSubset (Extension extension , String servicesValue ) {
11841260 if (StringUtils .isBlank (servicesValue )) {
11851261 return ;
11861262 }
1187- // Parse the extension's network.capabilities JSON to get the declared services
11881263 Map <String , String > extDetails = extensionDetailsDao .listDetailsKeyPairs (extension .getId ());
1189- if (extDetails == null || !extDetails .containsKey ("network.capabilities" )) {
1190- // No capabilities declared → accept any services
1191- return ;
1192- }
1193- String capsJson = extDetails .get ("network.capabilities" );
1194- Set <String > allowedServices = parseServicesFromCapabilitiesJson (capsJson );
1264+ Set <String > allowedServices = parseServicesFromDetailKeys (extDetails );
11951265 if (allowedServices .isEmpty ()) {
1266+ // No services declared → accept any
11961267 return ;
11971268 }
11981269
@@ -1209,31 +1280,6 @@ protected void validateNetworkServicesSubset(Extension extension, String service
12091280 }
12101281 }
12111282
1212- /**
1213- * Parses the {@code services} array from a {@code network.capabilities} JSON string.
1214- * Returns an empty set if parsing fails or services are not defined.
1215- */
1216- private Set <String > parseServicesFromCapabilitiesJson (String json ) {
1217- if (StringUtils .isBlank (json )) {
1218- return Collections .emptySet ();
1219- }
1220- try {
1221- JsonObject root = JsonParser .parseString (json ).getAsJsonObject ();
1222- JsonArray arr = root .getAsJsonArray ("services" );
1223- if (arr == null ) {
1224- return Collections .emptySet ();
1225- }
1226- Set <String > services = new HashSet <>();
1227- for (JsonElement el : arr ) {
1228- services .add (el .getAsString ());
1229- }
1230- return services ;
1231- } catch (Exception e ) {
1232- logger .warn ("Failed to parse network.capabilities JSON: {}" , e .getMessage ());
1233- return Collections .emptySet ();
1234- }
1235- }
1236-
12371283 /**
12381284 * Parses a services list from either a comma-separated string (e.g.
12391285 * {@code "SourceNat,StaticNat"}) or a JSON array (e.g.
@@ -2342,61 +2388,6 @@ public Map<Service, Map<Capability, String>> getNetworkCapabilitiesForProvider(L
23422388 return new HashMap <>();
23432389 }
23442390 Map <String , String > extDetails = extensionDetailsDao .listDetailsKeyPairs (extension .getId ());
2345- if (extDetails == null || !extDetails .containsKey (ExtensionHelper .NETWORK_CAPABILITIES_DETAIL_KEY )) {
2346- return new HashMap <>();
2347- }
2348- return parseCapabilitiesFromJson (extDetails .get (ExtensionHelper .NETWORK_CAPABILITIES_DETAIL_KEY ));
2349- }
2350-
2351- /**
2352- * Parses a {@code network.capabilities} JSON string into a
2353- * {@code Map<Service, Map<Capability, String>>} suitable for
2354- * {@link com.cloud.network.element.NetworkElement#getCapabilities()}.
2355- *
2356- * <p>Expected JSON format:</p>
2357- * <pre>
2358- * {
2359- * "services": ["SourceNat", "StaticNat", ...],
2360- * "capabilities": {
2361- * "SourceNat": { "SupportedSourceNatTypes": "peraccount", "RedundantRouter": "false" }
2362- * }
2363- * }
2364- * </pre>
2365- */
2366- private Map <Service , Map <Capability , String >> parseCapabilitiesFromJson (String json ) {
2367- Map <Service , Map <Capability , String >> result = new HashMap <>();
2368- if (StringUtils .isBlank (json )) {
2369- return result ;
2370- }
2371- try {
2372- JsonObject root = JsonParser .parseString (json ).getAsJsonObject ();
2373- JsonArray servicesArr = root .getAsJsonArray ("services" );
2374- if (servicesArr == null ) {
2375- return result ;
2376- }
2377- JsonObject capsObj = root .has ("capabilities" ) ? root .getAsJsonObject ("capabilities" ) : null ;
2378- for (JsonElement el : servicesArr ) {
2379- String svcName = el .getAsString ();
2380- Service service = Service .getService (svcName );
2381- if (service == null ) {
2382- logger .warn ("Unknown network service '{}' in extension capabilities JSON — skipping" , svcName );
2383- continue ;
2384- }
2385- Map <Capability , String > capMap = new HashMap <>();
2386- if (capsObj != null && capsObj .has (svcName )) {
2387- JsonObject svcCaps = capsObj .getAsJsonObject (svcName );
2388- for (Map .Entry <String , JsonElement > entry : svcCaps .entrySet ()) {
2389- Capability cap = Capability .getCapability (entry .getKey ());
2390- if (cap != null ) {
2391- capMap .put (cap , entry .getValue ().getAsString ());
2392- }
2393- }
2394- }
2395- result .put (service , capMap );
2396- }
2397- } catch (Exception e ) {
2398- logger .warn ("Failed to parse network.capabilities JSON: {}" , e .getMessage ());
2399- }
2400- return result ;
2391+ return buildCapabilitiesFromDetailKeys (extDetails );
24012392 }
24022393}
0 commit comments