@@ -396,13 +396,9 @@ public boolean deleteNatRule(DeleteNetrisNatRuleCommand cmd) {
396396 NatGetBody existingNatRule = netrisNatRuleExists (natRuleName );
397397 // Backward compatibility: rules created before the public-IP suffix was added use the legacy name
398398 if (existingNatRule == null && "STATICNAT" .equals (cmd .getNatRuleType ())) {
399- String legacyName = getLegacyStaticNatRuleName (natRuleName );
400- if (legacyName != null ) {
401- logger .debug ("Static NAT rule not found with name '{}', falling back to legacy name '{}'" , natRuleName , legacyName );
402- existingNatRule = netrisNatRuleExists (legacyName );
403- if (existingNatRule != null ) {
404- natRuleName = legacyName ;
405- }
399+ existingNatRule = findLegacyStaticNatRule (natRuleName , cmd .getNatIp ());
400+ if (existingNatRule != null ) {
401+ natRuleName = existingNatRule .getName ();
406402 }
407403 }
408404 boolean ruleExists = Objects .nonNull (existingNatRule );
@@ -1170,6 +1166,14 @@ private VPCListing getOrCreateL2Vpc(NetrisCommand cmd) {
11701166 return getVpcByNameAndTenant (l2VpcName );
11711167 }
11721168
1169+ private void rollbackL2Vpc (VPCListing l2Vpc ) {
1170+ try {
1171+ deleteVpcInternal (l2Vpc );
1172+ } catch (Exception e ) {
1173+ logger .error ("Failed to rollback L2 VPC {} after vNet creation failure - manual cleanup may be required: {}" , l2Vpc .getName (), e .getMessage ());
1174+ }
1175+ }
1176+
11731177 private VPCListing getVpcByNameAndTenant (String vpcName ) {
11741178 try {
11751179 List <VPCListing > vpcListings = listVPCs ();
@@ -1256,9 +1260,11 @@ public boolean createVnet(CreateNetrisVnetCommand cmd) {
12561260 String netrisV6IpamAllocationName = null ;
12571261 String netrisV6SubnetName = null ;
12581262 boolean createdIpv6Allocation = false ;
1263+ boolean createdL2Vpc = false ;
12591264
12601265 try {
12611266 if (isL2 ) {
1267+ createdL2Vpc = getVpcByNameAndTenant (getL2VpcName (cmd , cmd .getName ())) == null ;
12621268 associatedVpc = getOrCreateL2Vpc (cmd );
12631269 if (associatedVpc == null ) {
12641270 logger .error ("Failed to get or create dedicated L2 VPC to create the corresponding vNet for L2 network {}" , networkName );
@@ -1309,13 +1315,17 @@ public boolean createVnet(CreateNetrisVnetCommand cmd) {
13091315 if (!isL2 ) {
13101316 rollbackVnetResources (associatedVpc , netrisSubnetName , netrisV6SubnetName ,
13111317 createdIpv6Allocation ? netrisV6IpamAllocationName : null , networkName );
1318+ } else if (createdL2Vpc ) {
1319+ rollbackL2Vpc (associatedVpc );
13121320 }
13131321 return false ;
13141322 }
13151323 } catch (Exception e ) {
13161324 if (!isL2 && associatedVpc != null ) {
13171325 rollbackVnetResources (associatedVpc , netrisSubnetName , netrisV6SubnetName ,
13181326 createdIpv6Allocation ? netrisV6IpamAllocationName : null , networkName );
1327+ } else if (isL2 && createdL2Vpc && associatedVpc != null ) {
1328+ rollbackL2Vpc (associatedVpc );
13191329 }
13201330 throw new CloudRuntimeException (String .format ("Failed to create Netris vNet %s" , networkName ), e );
13211331 }
@@ -1710,9 +1720,9 @@ public boolean createStaticNatRule(CreateOrUpdateNetrisNatCommand cmd) {
17101720 return true ;
17111721 }
17121722 // Backward compatibility: rule with legacy naming convention (no public IP post-fixed) exists - don't create a duplicate
1713- String legacyName = getLegacyStaticNatRuleName (staticNatRuleName );
1714- if (legacyName != null && netrisNatRuleExists ( legacyName ) != null ) {
1715- logger .debug ("Legacy static NAT rule '{}' already exists on Netris, skipping creation of '{}'" , legacyName , staticNatRuleName );
1723+ NatGetBody legacyRule = findLegacyStaticNatRule (staticNatRuleName , cmd . getNatIp () );
1724+ if (legacyRule != null ) {
1725+ logger .debug ("Legacy static NAT rule '{}' already exists on Netris, skipping creation of '{}'" , legacyRule . getName () , staticNatRuleName );
17161726 return true ;
17171727 }
17181728 // Create a /32 subnet for the DNAT IP
@@ -2194,6 +2204,21 @@ private String getLegacyStaticNatRuleName(String newName) {
21942204 return newName .substring (0 , idx + "-STATICNAT" .length ());
21952205 }
21962206
2207+ /**
2208+ * Returns the legacy-named static NAT rule only if it was created for the given public IP.
2209+ */
2210+ private NatGetBody findLegacyStaticNatRule (String newName , String natIp ) {
2211+ String legacyName = getLegacyStaticNatRuleName (newName );
2212+ if (legacyName == null ) {
2213+ return null ;
2214+ }
2215+ NatGetBody legacyRule = netrisNatRuleExists (legacyName );
2216+ if (legacyRule == null || !(natIp + "/32" ).equals (legacyRule .getDestinationAddress ())) {
2217+ return null ;
2218+ }
2219+ return legacyRule ;
2220+ }
2221+
21972222 private VPCListing getNetrisVpcResource (String netrisVpcName ) {
21982223 VPCListing vpcResource = getVpcByNameAndTenant (netrisVpcName );
21992224 if (vpcResource == null ) {
0 commit comments