|
67 | 67 | import com.cloud.network.element.FirewallServiceProvider; |
68 | 68 | import com.cloud.network.element.IpDeployer; |
69 | 69 | import com.cloud.network.element.LoadBalancingServiceProvider; |
| 70 | +import com.cloud.network.element.NetworkACLServiceProvider; |
70 | 71 | import com.cloud.network.element.NetworkElement; |
71 | 72 | import com.cloud.network.element.PortForwardingServiceProvider; |
72 | 73 | import com.cloud.network.element.SourceNatServiceProvider; |
73 | 74 | import com.cloud.network.element.StaticNatServiceProvider; |
74 | 75 | import com.cloud.network.element.UserDataServiceProvider; |
| 76 | +import com.cloud.network.element.VpcProvider; |
| 77 | +import com.cloud.network.vpc.NetworkACLItem; |
| 78 | +import com.cloud.network.vpc.PrivateGateway; |
| 79 | +import com.cloud.network.vpc.StaticRouteProfile; |
| 80 | +import com.cloud.network.vpc.Vpc; |
75 | 81 | import com.cloud.network.lb.LoadBalancingRule; |
76 | 82 | import com.cloud.network.rules.FirewallRule; |
77 | 83 | import com.cloud.network.rules.FirewallRuleVO; |
@@ -204,7 +210,7 @@ public class NetworkExtensionElement extends AdapterBase implements |
204 | 210 | PortForwardingServiceProvider, IpDeployer, NetworkCustomActionProvider, |
205 | 211 | DhcpServiceProvider, DnsServiceProvider, FirewallServiceProvider, |
206 | 212 | UserDataServiceProvider, LoadBalancingServiceProvider, |
207 | | - AggregatedCommandExecutor { |
| 213 | + VpcProvider, NetworkACLServiceProvider, AggregatedCommandExecutor { |
208 | 214 |
|
209 | 215 | private static final Map<Service, Map<Capability, String>> DEFAULT_CAPABILITIES = new HashMap<>(); |
210 | 216 |
|
@@ -446,11 +452,11 @@ public boolean implement(Network network, NetworkOffering offering, DeployDestin |
446 | 452 | return false; |
447 | 453 | } |
448 | 454 |
|
449 | | - // Step 3: Configure source NAT if supported. |
450 | | - if (canHandle(network, Service.SourceNat)) { |
| 455 | + // Step 3: Configure source NAT for non-VPC networks. |
| 456 | + // VPC source NAT is managed at implementVpc(). |
| 457 | + if (network.getVpcId() == null && canHandle(network, Service.SourceNat)) { |
451 | 458 | try { |
452 | 459 | Account owner = accountService.getAccount(network.getAccountId()); |
453 | | - PublicIp sourceNatIp = null; |
454 | 460 | PublicIpAddress existingIp = networkModel.getSourceNatIpAddressForGuestNetwork(owner, network); |
455 | 461 | if (existingIp != null) { |
456 | 462 | applyIps(network, List.of(existingIp), Set.of(Service.SourceNat)); |
@@ -522,7 +528,9 @@ public boolean destroy(Network network, ReservationContext context) |
522 | 528 | args.add("--network-id"); args.add(String.valueOf(network.getId())); |
523 | 529 | args.add("--vlan"); args.add(safeStr(getVlanId(network))); |
524 | 530 | args.addAll(getVpcIdArgs(network)); |
525 | | - boolean result = executeScript(network, "destroy", args.toArray(new String[0])); |
| 531 | + // For VPC tiers, keep shared namespace until shutdownVpc() by using shutdown here. |
| 532 | + final String action = network.getVpcId() == null ? "destroy" : "shutdown"; |
| 533 | + boolean result = executeScript(network, action, args.toArray(new String[0])); |
526 | 534 | if (result) { |
527 | 535 | cleanupPlaceholderNicIp(network, context); |
528 | 536 | networkDetailsDao.removeDetail(network.getId(), NETWORK_DETAIL_EXTENSION_DETAILS); |
@@ -2137,4 +2145,160 @@ private String buildRestoreNetworkData(Network network, List<NicVO> nics, |
2137 | 2145 |
|
2138 | 2146 | return Base64.getEncoder().encodeToString(json.toString().getBytes(StandardCharsets.UTF_8)); |
2139 | 2147 | } |
| 2148 | + |
| 2149 | + // ---- VpcProvider ---- |
| 2150 | + |
| 2151 | + protected Network findVpcAnchorNetwork(long vpcId) { |
| 2152 | + final List<? extends Network> networks = networkModel.listNetworksByVpc(vpcId); |
| 2153 | + if (networks == null || networks.isEmpty()) { |
| 2154 | + return null; |
| 2155 | + } |
| 2156 | + |
| 2157 | + for (final Network network : networks) { |
| 2158 | + if (canHandle(network, null)) { |
| 2159 | + return network; |
| 2160 | + } |
| 2161 | + } |
| 2162 | + return null; |
| 2163 | + } |
| 2164 | + |
| 2165 | + protected PublicIpAddress getVpcSourceNatIp(long vpcId) { |
| 2166 | + final List<IPAddressVO> ips = ipAddressDao.listByAssociatedVpc(vpcId, true); |
| 2167 | + if (ips == null || ips.isEmpty()) { |
| 2168 | + return null; |
| 2169 | + } |
| 2170 | + IPAddressVO selected = null; |
| 2171 | + for (final IPAddressVO ip : ips) { |
| 2172 | + if (ip.getState() != IpAddress.State.Releasing) { |
| 2173 | + selected = ip; |
| 2174 | + break; |
| 2175 | + } |
| 2176 | + } |
| 2177 | + if (selected == null) { |
| 2178 | + selected = ips.get(0); |
| 2179 | + } |
| 2180 | + |
| 2181 | + final VlanVO vlan = vlanDao.findById(selected.getVlanId()); |
| 2182 | + if (vlan == null) { |
| 2183 | + logger.warn("No VLAN found for VPC source NAT IP {} (vpc={})", selected.getAddress(), vpcId); |
| 2184 | + return null; |
| 2185 | + } |
| 2186 | + return PublicIp.createFromAddrAndVlan(selected, vlan); |
| 2187 | + } |
| 2188 | + |
| 2189 | + /** |
| 2190 | + * Creates the VPC namespace and applies VPC source NAT upfront. |
| 2191 | + */ |
| 2192 | + @Override |
| 2193 | + public boolean implementVpc(Vpc vpc, DeployDestination dest, ReservationContext context) |
| 2194 | + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { |
| 2195 | + final Network anchorNetwork = findVpcAnchorNetwork(vpc.getId()); |
| 2196 | + if (anchorNetwork == null) { |
| 2197 | + logger.debug("implementVpc: no VPC tier network found for vpc {}", vpc.getId()); |
| 2198 | + return true; |
| 2199 | + } |
| 2200 | + |
| 2201 | + ensureExtensionDetails(anchorNetwork); |
| 2202 | + final String extensionIp = ensureExtensionIp(anchorNetwork); |
| 2203 | + |
| 2204 | + final List<String> args = new ArrayList<>(); |
| 2205 | + args.add("--network-id"); args.add(String.valueOf(anchorNetwork.getId())); |
| 2206 | + args.add("--vlan"); args.add(safeStr(getVlanId(anchorNetwork))); |
| 2207 | + args.add("--gateway"); args.add(safeStr(anchorNetwork.getGateway())); |
| 2208 | + args.add("--cidr"); args.add(safeStr(anchorNetwork.getCidr())); |
| 2209 | + args.add("--extension-ip"); args.add(safeStr(extensionIp)); |
| 2210 | + args.addAll(getVpcIdArgs(anchorNetwork)); |
| 2211 | + |
| 2212 | + if (!executeScript(anchorNetwork, "implement", args.toArray(new String[0]))) { |
| 2213 | + return false; |
| 2214 | + } |
| 2215 | + |
| 2216 | + if (canHandle(anchorNetwork, Service.SourceNat)) { |
| 2217 | + final PublicIpAddress sourceNatIp = getVpcSourceNatIp(vpc.getId()); |
| 2218 | + if (sourceNatIp != null) { |
| 2219 | + applyIps(anchorNetwork, List.of(sourceNatIp), Set.of(Service.SourceNat)); |
| 2220 | + } |
| 2221 | + } |
| 2222 | + |
| 2223 | + return true; |
| 2224 | + } |
| 2225 | + |
| 2226 | + /** |
| 2227 | + * Removes the shared VPC namespace by destroying all extension-backed VPC tiers. |
| 2228 | + */ |
| 2229 | + @Override |
| 2230 | + public boolean shutdownVpc(Vpc vpc, ReservationContext context) |
| 2231 | + throws ConcurrentOperationException, ResourceUnavailableException { |
| 2232 | + final List<? extends Network> networks = networkModel.listNetworksByVpc(vpc.getId()); |
| 2233 | + if (networks == null || networks.isEmpty()) { |
| 2234 | + return true; |
| 2235 | + } |
| 2236 | + |
| 2237 | + boolean result = true; |
| 2238 | + for (final Network network : networks) { |
| 2239 | + if (!canHandle(network, null)) { |
| 2240 | + continue; |
| 2241 | + } |
| 2242 | + |
| 2243 | + final List<String> args = new ArrayList<>(); |
| 2244 | + args.add("--network-id"); args.add(String.valueOf(network.getId())); |
| 2245 | + args.add("--vlan"); args.add(safeStr(getVlanId(network))); |
| 2246 | + args.addAll(getVpcIdArgs(network)); |
| 2247 | + |
| 2248 | + final boolean tierResult = executeScript(network, "destroy", args.toArray(new String[0])); |
| 2249 | + if (tierResult) { |
| 2250 | + networkDetailsDao.removeDetail(network.getId(), NETWORK_DETAIL_EXTENSION_DETAILS); |
| 2251 | + } |
| 2252 | + result = result && tierResult; |
| 2253 | + } |
| 2254 | + |
| 2255 | + return result; |
| 2256 | + } |
| 2257 | + |
| 2258 | + /** Private gateways are not supported by the network extension element. */ |
| 2259 | + @Override |
| 2260 | + public boolean createPrivateGateway(PrivateGateway gateway) |
| 2261 | + throws ConcurrentOperationException, ResourceUnavailableException { |
| 2262 | + return true; |
| 2263 | + } |
| 2264 | + |
| 2265 | + /** Private gateways are not supported by the network extension element. */ |
| 2266 | + @Override |
| 2267 | + public boolean deletePrivateGateway(PrivateGateway gateway) |
| 2268 | + throws ConcurrentOperationException, ResourceUnavailableException { |
| 2269 | + return true; |
| 2270 | + } |
| 2271 | + |
| 2272 | + /** Static routes are not supported by the network extension element. */ |
| 2273 | + @Override |
| 2274 | + public boolean applyStaticRoutes(Vpc vpc, List<StaticRouteProfile> routes) |
| 2275 | + throws ResourceUnavailableException { |
| 2276 | + return true; |
| 2277 | + } |
| 2278 | + |
| 2279 | + /** ACL items on private gateways are not supported by the network extension element. */ |
| 2280 | + @Override |
| 2281 | + public boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List<? extends NetworkACLItem> rules) |
| 2282 | + throws ResourceUnavailableException { |
| 2283 | + return true; |
| 2284 | + } |
| 2285 | + |
| 2286 | + @Override |
| 2287 | + public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) { |
| 2288 | + return true; |
| 2289 | + } |
| 2290 | + |
| 2291 | + @Override |
| 2292 | + public boolean applyNetworkACLs(Network config, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException { |
| 2293 | + if (!canHandle(config, Service.NetworkACL)) { |
| 2294 | + return true; |
| 2295 | + } |
| 2296 | + // ACL semantics for this extension are handled by script policy/rule processing. |
| 2297 | + return true; |
| 2298 | + } |
| 2299 | + |
| 2300 | + @Override |
| 2301 | + public boolean reorderAclRules(Vpc vpc, List<? extends Network> networks, List<? extends NetworkACLItem> networkACLItems) { |
| 2302 | + return true; |
| 2303 | + } |
2140 | 2304 | } |
0 commit comments