mirror of https://github.com/apache/cloudstack.git
Merge ae23e19bb8 into 5893ba5a8c
This commit is contained in:
commit
5cf44714fa
|
|
@ -854,6 +854,7 @@ public class EventTypes {
|
|||
public static final String EVENT_EXTENSION_DELETE = "EXTENSION.DELETE";
|
||||
public static final String EVENT_EXTENSION_RESOURCE_REGISTER = "EXTENSION.RESOURCE.REGISTER";
|
||||
public static final String EVENT_EXTENSION_RESOURCE_UNREGISTER = "EXTENSION.RESOURCE.UNREGISTER";
|
||||
public static final String EVENT_EXTENSION_RESOURCE_UPDATE = "EXTENSION.RESOURCE.UPDATE";
|
||||
public static final String EVENT_EXTENSION_CUSTOM_ACTION_ADD = "EXTENSION.CUSTOM.ACTION.ADD";
|
||||
public static final String EVENT_EXTENSION_CUSTOM_ACTION_UPDATE = "EXTENSION.CUSTOM.ACTION.UPDATE";
|
||||
public static final String EVENT_EXTENSION_CUSTOM_ACTION_DELETE = "EXTENSION.CUSTOM.ACTION.DELETE";
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
|
|||
public static final Service NetworkACL = new Service("NetworkACL", Capability.SupportedProtocols);
|
||||
public static final Service Connectivity = new Service("Connectivity", Capability.DistributedRouter, Capability.RegionLevelVpc, Capability.StretchedL2Subnet,
|
||||
Capability.NoVlan, Capability.PublicAccess);
|
||||
public static final Service CustomAction = new Service("CustomAction");
|
||||
|
||||
private final String name;
|
||||
private final Capability[] caps;
|
||||
|
|
@ -207,6 +208,7 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
|
|||
|
||||
public static final Provider Nsx = new Provider("Nsx", false);
|
||||
public static final Provider Netris = new Provider("Netris", false);
|
||||
public static final Provider NetworkExtension = new Provider("NetworkExtension", false, true);
|
||||
|
||||
private final String name;
|
||||
private final boolean isExternal;
|
||||
|
|
@ -250,11 +252,47 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
|
|||
return null;
|
||||
}
|
||||
|
||||
/** Private constructor for transient (non-registered) providers. */
|
||||
private Provider(String name) {
|
||||
this.name = name;
|
||||
this.isExternal = false;
|
||||
this.needCleanupOnShutdown = true;
|
||||
// intentionally NOT added to supportedProviders
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transient (non-registered) {@link Provider} with the given name.
|
||||
*
|
||||
* <p>The new instance is <em>not</em> added to {@code supportedProviders}, so it
|
||||
* will never be returned by {@link #getProvider(String)} and will not pollute the
|
||||
* global provider registry. Use this for dynamic / extension-backed providers
|
||||
* whose names are only known at runtime (e.g. NetworkOrchestrator extensions).</p>
|
||||
*
|
||||
* @param name the provider name (typically the extension name)
|
||||
* @return a transient {@link Provider} instance with the given name
|
||||
*/
|
||||
public static Provider createTransientProvider(String name) {
|
||||
return new Provider(name);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("name", name)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof Provider)) return false;
|
||||
Provider provider = (Provider) obj;
|
||||
return this.name.equals(provider.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Capability {
|
||||
|
|
|
|||
|
|
@ -187,6 +187,8 @@ public interface NetworkModel {
|
|||
|
||||
boolean canElementEnableIndividualServices(Provider provider);
|
||||
|
||||
boolean canElementEnableIndividualServicesByName(String providerName);
|
||||
|
||||
boolean areServicesSupportedInNetwork(long networkId, Service... services);
|
||||
|
||||
boolean isNetworkSystem(Network network);
|
||||
|
|
@ -237,6 +239,18 @@ public interface NetworkModel {
|
|||
|
||||
String getDefaultGuestTrafficLabel(long dcId, HypervisorType vmware);
|
||||
|
||||
/**
|
||||
* Resolves a provider name to a {@link Provider} instance.
|
||||
* For known static providers, delegates to {@link Provider#getProvider(String)}.
|
||||
* For dynamically-registered NetworkOrchestrator extension providers whose names
|
||||
* are not in the static registry, returns a transient {@link Provider} with the
|
||||
* given name so callers can still dispatch correctly.
|
||||
*
|
||||
* @param providerName the provider name from {@code ntwk_service_map} or similar
|
||||
* @return a {@link Provider} instance, or {@code null} if not resolvable
|
||||
*/
|
||||
Provider resolveProvider(String providerName);
|
||||
|
||||
/**
|
||||
* @param providerName
|
||||
* @return
|
||||
|
|
|
|||
|
|
@ -81,7 +81,11 @@ public class Networks {
|
|||
return uri == null ? null : uri.getAuthority();
|
||||
}
|
||||
},
|
||||
Vswitch("vs", String.class), LinkLocal(null, null), Vnet("vnet", Long.class), Storage("storage", Integer.class), Lswitch("lswitch", String.class) {
|
||||
Vswitch("vs", String.class),
|
||||
LinkLocal(null, null),
|
||||
Vnet("vnet", Long.class),
|
||||
Storage("storage", Integer.class),
|
||||
Lswitch("lswitch", String.class) {
|
||||
@Override
|
||||
public <T> URI toUri(T value) {
|
||||
try {
|
||||
|
|
@ -99,7 +103,8 @@ public class Networks {
|
|||
return uri == null ? null : uri.getSchemeSpecificPart();
|
||||
}
|
||||
},
|
||||
Mido("mido", String.class), Pvlan("pvlan", String.class),
|
||||
Mido("mido", String.class),
|
||||
Pvlan("pvlan", String.class),
|
||||
Vxlan("vxlan", Long.class) {
|
||||
@Override
|
||||
public <T> URI toUri(T value) {
|
||||
|
|
|
|||
|
|
@ -146,4 +146,8 @@ public interface NetworkElement extends Adapter {
|
|||
* @return true/false
|
||||
*/
|
||||
boolean verifyServicesCombination(Set<Service> services);
|
||||
|
||||
default boolean rollingRestartSupported() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,4 +62,12 @@ public class CustomActionResultResponse extends BaseResponse {
|
|||
public void setResult(Map<String, String> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public Map<String, String> getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return Boolean.TRUE.equals(success);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ import org.apache.cloudstack.api.InternalIdentity;
|
|||
|
||||
public interface Extension extends InternalIdentity, Identity {
|
||||
enum Type {
|
||||
Orchestrator
|
||||
Orchestrator,
|
||||
NetworkOrchestrator
|
||||
}
|
||||
enum State {
|
||||
Enabled, Disabled;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ import com.google.gson.reflect.TypeToken;
|
|||
|
||||
public interface ExtensionCustomAction extends InternalIdentity, Identity {
|
||||
enum ResourceType {
|
||||
VirtualMachine(com.cloud.vm.VirtualMachine.class);
|
||||
VirtualMachine(com.cloud.vm.VirtualMachine.class),
|
||||
Network(com.cloud.network.Network.class),
|
||||
Vpc(com.cloud.network.vpc.Vpc.class);
|
||||
|
||||
private final Class<?> clazz;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,10 +18,120 @@
|
|||
package org.apache.cloudstack.extension;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.network.Network.Capability;
|
||||
import com.cloud.network.Network.Service;
|
||||
|
||||
public interface ExtensionHelper {
|
||||
Long getExtensionIdForCluster(long clusterId);
|
||||
Extension getExtension(long id);
|
||||
Extension getExtensionForCluster(long clusterId);
|
||||
List<String> getExtensionReservedResourceDetails(long extensionId);
|
||||
|
||||
/**
|
||||
* Detail key used to store the comma-separated list of network services provided
|
||||
* by a NetworkOrchestrator extension (e.g. {@code "SourceNat,StaticNat,Firewall"}).
|
||||
*/
|
||||
String NETWORK_SERVICES_DETAIL_KEY = "network.services";
|
||||
|
||||
/**
|
||||
* Detail key used to store a JSON object mapping each service name to its
|
||||
* CloudStack {@link com.cloud.network.Network.Capability} key/value pairs.
|
||||
* Example: {@code {"SourceNat":{"SupportedSourceNatTypes":"peraccount"}}}.
|
||||
* Used together with {@link #NETWORK_SERVICES_DETAIL_KEY}.
|
||||
*/
|
||||
String NETWORK_SERVICE_CAPABILITIES_DETAIL_KEY = "network.service.capabilities";
|
||||
|
||||
/**
|
||||
* Detail key used by an OVS-backed NetworkOrchestrator extension to declare
|
||||
* how its Logical Switch Port name should be matched against the OVS
|
||||
* {@code external_ids:iface-id} written by libvirt on the hypervisor.
|
||||
*
|
||||
* <p>Currently supported value:</p>
|
||||
* <ul>
|
||||
* <li>{@code "lswitch"} — the framework sets {@code BroadcastDomainType.Lswitch}
|
||||
* on the {@link com.cloud.vm.NicProfile} during {@code prepare(...)} and
|
||||
* propagates {@code nic.getUuid()} to per-NIC script commands as
|
||||
* {@code --nic-uuid}. The extension is then expected to use that UUID as
|
||||
* the LSP name, so it matches the {@code interfaceid} that
|
||||
* {@code OvsVifDriver} emits in the libvirt {@code <virtualport>} for
|
||||
* {@code Lswitch} broadcast type.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>If absent, the framework keeps the network's broadcast type unchanged
|
||||
* (typically {@code Vlan}) and does not propagate {@code --nic-uuid}.</p>
|
||||
*/
|
||||
String VIF_BINDING_DETAIL_KEY = "vif.binding";
|
||||
|
||||
/** Value of {@link #VIF_BINDING_DETAIL_KEY} that selects the Lswitch path. */
|
||||
String VIF_BINDING_LSWITCH = "lswitch";
|
||||
|
||||
String getExtensionScriptPath(Extension extension);
|
||||
|
||||
/**
|
||||
* Finds the extension registered with the given physical network whose name
|
||||
* matches the given provider name (case-insensitive). Returns {@code null}
|
||||
* if no matching extension is found.
|
||||
*
|
||||
* <p>This is the preferred lookup when multiple extensions are registered on
|
||||
* the same physical network: the provider name stored in
|
||||
* {@code ntwk_service_map} is used to pinpoint the exact extension that
|
||||
* handles a given network.</p>
|
||||
*
|
||||
* @param physicalNetworkId the physical network ID
|
||||
* @param providerName the provider name (must equal the extension name)
|
||||
* @return the matching {@link Extension}, or {@code null}
|
||||
*/
|
||||
Extension getExtensionForPhysicalNetworkAndProvider(long physicalNetworkId, String providerName);
|
||||
|
||||
/**
|
||||
* Returns ALL {@code extension_resource_map_details} (including hidden) for
|
||||
* the specific extension registered on the given physical network. Used by
|
||||
* {@code NetworkExtensionElement} to inject device credentials into the script
|
||||
* environment for the correct extension when multiple different extensions are
|
||||
* registered on the same physical network.
|
||||
*
|
||||
* @param physicalNetworkId the physical network ID
|
||||
* @param extensionId the extension ID
|
||||
* @return all key/value details including non-display ones, or an empty map
|
||||
*/
|
||||
Map<String, String> getAllResourceMapDetailsForExtensionOnPhysicalNetwork(long physicalNetworkId, long extensionId);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given provider name is backed by a
|
||||
* {@code NetworkOrchestrator} extension registered on any physical network.
|
||||
* This is used by {@code NetworkModelImpl} to detect extension-backed providers
|
||||
* that are not in the static {@code s_providerToNetworkElementMap}.
|
||||
*
|
||||
* @param providerName the provider / extension name
|
||||
* @return true if the provider is a NetworkExtension provider
|
||||
*/
|
||||
boolean isNetworkExtensionProvider(String providerName);
|
||||
|
||||
/**
|
||||
* List all registered extensions filtered by extension {@link Extension.Type}.
|
||||
* Useful for callers that need to discover available providers of a given
|
||||
* type (e.g. Orchestrator, NetworkOrchestrator).
|
||||
*
|
||||
* @param type extension type to filter by
|
||||
* @return list of matching {@link Extension} instances (empty list if none)
|
||||
*/
|
||||
List<Extension> listExtensionsByType(Extension.Type type);
|
||||
|
||||
/**
|
||||
* Returns the effective {@link Service} → ({@link Capability} → value) capabilities
|
||||
* for the given external network provider, looking it up by name on the given
|
||||
* physical network.
|
||||
*
|
||||
* <p>If {@code physicalNetworkId} is {@code null}, the method searches across all
|
||||
* physical networks that have extensions registered and returns the capabilities for
|
||||
* the first matching extension.</p>
|
||||
*
|
||||
* @param physicalNetworkId physical network ID, or {@code null} for offering-level queries
|
||||
* @param providerName provider / extension name
|
||||
* @return capabilities map, or the default capabilities if no matching extension is found
|
||||
*/
|
||||
Map<Service, Map<Capability, String>> getNetworkCapabilitiesForProvider(Long physicalNetworkId, String providerName);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ import org.apache.cloudstack.api.InternalIdentity;
|
|||
|
||||
public interface ExtensionResourceMap extends InternalIdentity, Identity {
|
||||
enum ResourceType {
|
||||
Cluster
|
||||
Cluster,
|
||||
PhysicalNetwork
|
||||
}
|
||||
|
||||
long getExtensionId();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.extension;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.vpc.Vpc;
|
||||
|
||||
/**
|
||||
* Implemented by network elements that support running custom actions on a
|
||||
* managed network or VPC (e.g. NetworkExtensionElement).
|
||||
*
|
||||
* <p>This interface is looked up by {@code ExtensionsManagerImpl} to dispatch
|
||||
* {@code runCustomAction} requests whose resource type is {@code Network}
|
||||
* or {@code Vpc}.</p>
|
||||
*/
|
||||
public interface NetworkCustomActionProvider {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this provider handles networks whose physical
|
||||
* network has an ExternalNetwork service provider registered.
|
||||
*
|
||||
* @param network the target network
|
||||
* @return {@code true} if this provider can handle the network
|
||||
*/
|
||||
boolean canHandleCustomAction(Network network);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this provider can handle custom actions for
|
||||
* the given VPC.
|
||||
*
|
||||
* @param vpc the target VPC
|
||||
* @return {@code true} if this provider can handle the VPC
|
||||
*/
|
||||
boolean canHandleVpcCustomAction(Vpc vpc);
|
||||
|
||||
/**
|
||||
* Runs a named custom action against the external network device that
|
||||
* manages the given network.
|
||||
*
|
||||
* @param network the CloudStack network on which to run the action
|
||||
* @param actionName the action name (e.g. {@code "reboot-device"}, {@code "dump-config"})
|
||||
* @param parameters optional parameters supplied by the caller
|
||||
* @return output from the action script, or {@code null} on failure
|
||||
*/
|
||||
String runCustomAction(Network network, String actionName, Map<String, Object> parameters);
|
||||
|
||||
/**
|
||||
* Runs a named custom action against the external network device that
|
||||
* manages the given VPC.
|
||||
*
|
||||
* @param vpc the CloudStack VPC on which to run the action
|
||||
* @param actionName the action name
|
||||
* @param parameters optional parameters supplied by the caller
|
||||
* @return output from the action script, or {@code null} on failure
|
||||
*/
|
||||
String runCustomAction(Vpc vpc, String actionName, Map<String, Object> parameters);
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com.cloud.network;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
public class NetworkTest {
|
||||
|
||||
@Test
|
||||
public void testProviderContains() {
|
||||
List<Network.Provider> providers = new ArrayList<>();
|
||||
providers.add(Network.Provider.VirtualRouter);
|
||||
|
||||
// direct instance present
|
||||
assertTrue("List should contain VirtualRouter provider", providers.contains(Network.Provider.VirtualRouter));
|
||||
|
||||
// resolved provider by name (registered provider)
|
||||
Network.Provider resolved = Network.Provider.getProvider("VirtualRouter");
|
||||
assertNotNull("Resolved provider should not be null", resolved);
|
||||
assertTrue("List should contain resolved VirtualRouter provider", providers.contains(resolved));
|
||||
|
||||
// transient provider with same name should be considered equal (equals by name)
|
||||
Network.Provider transientProvider = Network.Provider.createTransientProvider("NetworkExtension");
|
||||
assertFalse("List should not contain the transient provider", providers.contains(transientProvider));
|
||||
|
||||
providers.add(transientProvider);
|
||||
assertTrue("List should contain the transient provider", providers.contains(transientProvider));
|
||||
|
||||
// another transient provider with same name should be considered equal
|
||||
Network.Provider transientProviderNew = Network.Provider.createTransientProvider("NetworkExtension");
|
||||
assertTrue("List should contain the new transient provider with same name", providers.contains(transientProviderNew));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomActionServiceLookup() {
|
||||
Network.Service customAction = Network.Service.getService("CustomAction");
|
||||
assertNotNull("CustomAction service should be available", customAction);
|
||||
assertTrue("CustomAction should be part of the supported services list",
|
||||
Network.Service.listAllServices().contains(customAction));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransientProviderIsNotGloballyRegistered() {
|
||||
Network.Provider transientProvider = Network.Provider.createTransientProvider("TransientOnly");
|
||||
assertNotNull(transientProvider);
|
||||
assertNull("Transient provider should not be retrievable from the global registry",
|
||||
Network.Provider.getProvider("TransientOnly"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.extension;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CustomActionResultResponseTest {
|
||||
|
||||
private CustomActionResultResponse response;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
response = new CustomActionResultResponse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResultReturnsNullByDefault() {
|
||||
assertNull(response.getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResultReturnsSetValue() {
|
||||
Map<String, String> result = Map.of("message", "OK", "details", "All good");
|
||||
response.setResult(result);
|
||||
assertEquals(result, response.getResult());
|
||||
assertEquals("OK", response.getResult().get("message"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSuccessReturnsFalseWhenSuccessIsNull() {
|
||||
// success is null by default
|
||||
assertFalse(response.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSuccessReturnsFalseWhenSuccessIsFalse() {
|
||||
response.setSuccess(false);
|
||||
assertFalse(response.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSuccessReturnsTrueWhenSuccessIsTrue() {
|
||||
response.setSuccess(true);
|
||||
assertTrue(response.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSuccessReturnsNullByDefault() {
|
||||
assertNull(response.getSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSuccessReturnsTrueAfterSetSuccessTrue() {
|
||||
response.setSuccess(true);
|
||||
assertTrue(response.getSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSuccessReturnsFalseAfterSetSuccessFalse() {
|
||||
response.setSuccess(false);
|
||||
assertFalse(response.getSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetResultWithNullResult() {
|
||||
response.setResult(null);
|
||||
assertNull(response.getResult());
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,12 @@ public class ExtensionCustomActionTest {
|
|||
assertEquals(com.cloud.vm.VirtualMachine.class, vmType.getAssociatedClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetworkResourceType() {
|
||||
ExtensionCustomAction.ResourceType networkType = ExtensionCustomAction.ResourceType.Network;
|
||||
assertEquals(com.cloud.network.Network.class, networkType.getAssociatedClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParameterTypeSupportsOptions() {
|
||||
assertTrue(ExtensionCustomAction.Parameter.Type.STRING.canSupportsOptions());
|
||||
|
|
|
|||
|
|
@ -177,6 +177,10 @@ import com.cloud.network.dao.PhysicalNetworkVO;
|
|||
import com.cloud.network.dao.RemoteAccessVpnDao;
|
||||
import com.cloud.network.dao.RemoteAccessVpnVO;
|
||||
import com.cloud.network.dao.RouterNetworkDao;
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement;
|
||||
|
||||
import com.cloud.network.element.AggregatedCommandExecutor;
|
||||
import com.cloud.network.element.ConfigDriveNetworkElement;
|
||||
import com.cloud.network.element.DhcpServiceProvider;
|
||||
|
|
@ -368,6 +372,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
private BGPService bgpService;
|
||||
@Inject
|
||||
private Ipv6GuestPrefixSubnetNetworkMapDao ipv6GuestPrefixSubnetNetworkMapDao;
|
||||
@Inject
|
||||
protected ExtensionHelper extensionHelper;
|
||||
@Inject
|
||||
private NetworkExtensionElement networkExtensionElement;
|
||||
|
||||
@Override
|
||||
public List<NetworkGuru> getNetworkGurus() {
|
||||
|
|
@ -461,6 +469,28 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
|
||||
HashMap<Long, Long> _lastNetworkIdsToFree = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Returns the full list of network elements to iterate when implementing,
|
||||
* shutting down, or otherwise orchestrating a network.
|
||||
*
|
||||
* <p>The base list ({@link #networkElements}, wired by Spring) is extended
|
||||
* at runtime with one transient {@link NetworkExtensionElement} per
|
||||
* registered {@code NetworkOrchestrator} extension. This keeps the
|
||||
* Spring bean list free from {@code NetworkExtensionElement} and allows
|
||||
* dynamic discovery of extensions without a restart.</p>
|
||||
*/
|
||||
private List<NetworkElement> getNetworkElementsIncludingExtensions() {
|
||||
List<Extension> extensions = extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator);
|
||||
if (extensions == null || extensions.isEmpty()) {
|
||||
return networkElements;
|
||||
}
|
||||
List<NetworkElement> combined = new ArrayList<>(networkElements);
|
||||
for (Extension ext : extensions) {
|
||||
combined.add(networkExtensionElement.withProviderName(ext.getName()));
|
||||
}
|
||||
return combined;
|
||||
}
|
||||
|
||||
private void updateRouterDefaultDns(final VirtualMachineProfile vmProfile, final NicProfile nicProfile) {
|
||||
if (!Type.DomainRouter.equals(vmProfile.getType()) || !nicProfile.isDefaultNic()) {
|
||||
return;
|
||||
|
|
@ -1686,7 +1716,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
}
|
||||
}
|
||||
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) {
|
||||
((AggregatedCommandExecutor) element).prepareAggregatedExecution(network, dest);
|
||||
}
|
||||
|
|
@ -1703,7 +1733,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
ex.addProxyObject(_entityMgr.findById(DataCenter.class, network.getDataCenterId()).getUuid());
|
||||
throw ex;
|
||||
}
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) {
|
||||
if (!((AggregatedCommandExecutor) element).completeAggregatedExecution(network, dest)) {
|
||||
logger.warn("Failed to re-program the network as a part of network {} implement due to aggregated commands execution failure!", network);
|
||||
|
|
@ -1717,7 +1747,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
}
|
||||
reconfigureAndApplyStaticRouteForVpcVpn(network);
|
||||
} finally {
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) {
|
||||
((AggregatedCommandExecutor) element).cleanupAggregatedExecution(network, dest);
|
||||
}
|
||||
|
|
@ -1738,7 +1768,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
|
||||
private void implementNetworkElements(final DeployDestination dest, final ReservationContext context, final Network network, final NetworkOffering offering, final List<Provider> providersToImplement)
|
||||
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
|
||||
for (NetworkElement element : networkElements) {
|
||||
for (NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToImplement.contains(element.getProvider())) {
|
||||
if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
|
||||
// The physicalNetworkId will not get translated into a uuid by the response serializer,
|
||||
|
|
@ -2031,7 +2061,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
@Override
|
||||
public void configureUpdateInSequence(Network network) {
|
||||
List<Provider> providers = getNetworkProviders(network.getId());
|
||||
for (NetworkElement element : networkElements) {
|
||||
for (NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providers.contains(element.getProvider())) {
|
||||
if (element instanceof RedundantResource) {
|
||||
((RedundantResource) element).configureResource(network);
|
||||
|
|
@ -2044,7 +2074,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
public int getResourceCount(Network network) {
|
||||
List<Provider> providers = getNetworkProviders(network.getId());
|
||||
int resourceCount = 0;
|
||||
for (NetworkElement element : networkElements) {
|
||||
for (NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providers.contains(element.getProvider())) {
|
||||
//currently only one element implements the redundant resource interface
|
||||
if (element instanceof RedundantResource) {
|
||||
|
|
@ -2075,7 +2105,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
@Override
|
||||
public void finalizeUpdateInSequence(Network network, boolean success) {
|
||||
List<Provider> providers = getNetworkProviders(network.getId());
|
||||
for (NetworkElement element : networkElements) {
|
||||
for (NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providers.contains(element.getProvider())) {
|
||||
//currently only one element implements the redundant resource interface
|
||||
if (element instanceof RedundantResource) {
|
||||
|
|
@ -2102,7 +2132,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
}
|
||||
|
||||
private void setHypervisorHostnameInNetwork(VirtualMachineProfile vm, DeployDestination dest, Network network, NicProfile profile, boolean migrationSuccessful) {
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData) && element instanceof UserDataServiceProvider
|
||||
&& (element instanceof ConfigDriveNetworkElement && !migrationSuccessful || element instanceof VirtualRouterElement && migrationSuccessful)) {
|
||||
String errorMsg = String.format("Failed to add hypervisor host name while applying the userdata during the migration of VM %s, " +
|
||||
|
|
@ -2230,7 +2260,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
updateNic(nic, network, 1);
|
||||
|
||||
final List<Provider> providersToImplement = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToImplement.contains(element.getProvider())) {
|
||||
if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
|
||||
throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
|
||||
|
|
@ -2285,7 +2315,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
}
|
||||
|
||||
final List<Provider> providersToImplement = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToImplement.contains(element.getProvider())) {
|
||||
if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
|
||||
throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
|
||||
|
|
@ -2329,7 +2359,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
}
|
||||
}
|
||||
final List<Provider> providersToImplement = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToImplement.contains(element.getProvider())) {
|
||||
if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
|
||||
throw new CloudRuntimeException(String.format("Service provider %s either doesn't exist or is not enabled in physical network: %s",
|
||||
|
|
@ -2411,7 +2441,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
}
|
||||
|
||||
final List<Provider> providersToImplement = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToImplement.contains(element.getProvider())) {
|
||||
if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
|
||||
throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
|
||||
|
|
@ -2447,7 +2477,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
}
|
||||
|
||||
final List<Provider> providersToImplement = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToImplement.contains(element.getProvider())) {
|
||||
if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
|
||||
throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
|
||||
|
|
@ -2534,7 +2564,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
final Network network = networkToRelease.first();
|
||||
final NicProfile profile = networkToRelease.second();
|
||||
final List<Provider> providersToImplement = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToImplement.contains(element.getProvider())) {
|
||||
logger.debug("Asking {} to release {}", element.getName(), profile);
|
||||
//NOTE: Context appear to never be used in release method
|
||||
|
|
@ -2597,7 +2627,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
*/
|
||||
if (nic.getReservationStrategy() == Nic.ReservationStrategy.Create) {
|
||||
final List<Provider> providersToImplement = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToImplement.contains(element.getProvider())) {
|
||||
logger.debug("Asking {} to release {}, according to the reservation strategy {}.", element.getName(), nic, nic.getReservationStrategy());
|
||||
try {
|
||||
|
|
@ -3328,7 +3358,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
|
||||
// 2) Shutdown all the network elements
|
||||
boolean success = true;
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToShutdown.contains(element.getProvider())) {
|
||||
try {
|
||||
logger.debug("Sending network shutdown to {}", element.getName());
|
||||
|
|
@ -3439,7 +3469,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
|
||||
// get providers to destroy
|
||||
final List<Provider> providersToDestroy = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (providersToDestroy.contains(element.getProvider())) {
|
||||
try {
|
||||
logger.debug("Sending destroy to {}", element);
|
||||
|
|
@ -3810,7 +3840,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
public void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProfile, NicProfile nicProfile) {
|
||||
|
||||
final List<Provider> networkProviders = getNetworkProviders(network.getId());
|
||||
for (final NetworkElement element : networkElements) {
|
||||
for (final NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (networkProviders.contains(element.getProvider())) {
|
||||
if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) {
|
||||
throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: "
|
||||
|
|
@ -3846,7 +3876,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
* @throws InsufficientCapacityException
|
||||
*/
|
||||
private boolean rollingRestartRouters(final NetworkVO network, final NetworkOffering offering, final DeployDestination dest, final ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException {
|
||||
if (!NetworkOrchestrationService.RollingRestartEnabled.value()) {
|
||||
if (!isRollingRestartSupport(network)) {
|
||||
if (shutdownNetworkElementsAndResources(context, true, network)) {
|
||||
implementNetworkElementsAndResources(dest, context, network, offering);
|
||||
return true;
|
||||
|
|
@ -3894,6 +3924,20 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
return areRoutersRunning(routerDao.findByNetwork(network.getId()));
|
||||
}
|
||||
|
||||
private boolean isRollingRestartSupport(final NetworkVO network) {
|
||||
if (!NetworkOrchestrator.RollingRestartEnabled.value()) {
|
||||
return false;
|
||||
}
|
||||
List<NetworkServiceMapVO> services = _ntwkSrvcDao.getServicesInNetwork(network.getId());
|
||||
for (NetworkServiceMapVO service : services) {
|
||||
NetworkElement element = _networkModel.getElementImplementingProvider(service.getProvider());
|
||||
if (element == null || !element.rollingRestartSupported()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setRestartRequired(final NetworkVO network, final boolean restartRequired) {
|
||||
logger.debug("Marking network {} with restartRequired={}", network, restartRequired);
|
||||
network.setRestartRequired(restartRequired);
|
||||
|
|
@ -4455,6 +4499,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
|
||||
if (provider == null) {
|
||||
provider = _networkModel.getDefaultUniqueProviderForService(service).getName();
|
||||
} else {
|
||||
final Provider resolvedProvider = _networkModel.resolveProvider(provider);
|
||||
if (resolvedProvider == null) {
|
||||
throw new InvalidParameterValueException("Invalid provider " + provider + " configured for service " + service);
|
||||
}
|
||||
provider = resolvedProvider.getName();
|
||||
}
|
||||
|
||||
// check that provider is supported
|
||||
|
|
@ -4480,7 +4530,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
final List<String> providerNames = _ntwkSrvcDao.getDistinctProviders(networkId);
|
||||
final List<Provider> providers = new ArrayList<>();
|
||||
for (final String providerName : providerNames) {
|
||||
providers.add(Network.Provider.getProvider(providerName));
|
||||
final Provider provider = _networkModel.resolveProvider(providerName);
|
||||
if (provider != null) {
|
||||
providers.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
return providers;
|
||||
|
|
@ -4646,7 +4699,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
if (providers == null) {
|
||||
providers = new HashSet<>();
|
||||
}
|
||||
providers.add(Provider.getProvider(nsm.getProvider()));
|
||||
providers.add(_networkModel.resolveProvider(nsm.getProvider()));
|
||||
map.put(Service.getService(nsm.getService()), providers);
|
||||
}
|
||||
return map;
|
||||
|
|
@ -4931,10 +4984,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
|
||||
@Override
|
||||
public void expungeLbVmRefs(List<Long> vmIds, Long batchSize) {
|
||||
if (CollectionUtils.isEmpty(networkElements) || CollectionUtils.isEmpty(vmIds)) {
|
||||
if (CollectionUtils.isEmpty(vmIds)) {
|
||||
return;
|
||||
}
|
||||
for (NetworkElement element : networkElements) {
|
||||
for (NetworkElement element : getNetworkElementsIncludingExtensions()) {
|
||||
if (element instanceof LoadBalancingServiceProvider) {
|
||||
LoadBalancingServiceProvider lbProvider = (LoadBalancingServiceProvider)element;
|
||||
lbProvider.expungeLbVmRefs(vmIds, batchSize);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
|
|||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -35,11 +36,15 @@ import com.cloud.dc.DataCenter;
|
|||
import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
|
||||
import com.cloud.network.IpAddressManager;
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
|
@ -68,6 +73,7 @@ import com.cloud.network.dao.NetworkVO;
|
|||
import com.cloud.network.dao.PhysicalNetworkVO;
|
||||
import com.cloud.network.dao.RouterNetworkDao;
|
||||
import com.cloud.network.element.DhcpServiceProvider;
|
||||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.network.guru.GuestNetworkGuru;
|
||||
import com.cloud.network.guru.NetworkGuru;
|
||||
import com.cloud.network.vpc.VpcManager;
|
||||
|
|
@ -105,6 +111,7 @@ public class NetworkOrchestratorTest extends TestCase {
|
|||
private String guruName = "GuestNetworkGuru";
|
||||
private String dhcpProvider = "VirtualRouter";
|
||||
private NetworkGuru guru = mock(NetworkGuru.class);
|
||||
private NetworkExtensionElement networkExtensionElement;
|
||||
|
||||
NetworkOfferingVO networkOffering = mock(NetworkOfferingVO.class);
|
||||
|
||||
|
|
@ -135,6 +142,9 @@ public class NetworkOrchestratorTest extends TestCase {
|
|||
testOrchestrator.routerJoinDao = mock(DomainRouterJoinDao.class);
|
||||
testOrchestrator._ipAddrMgr = mock(IpAddressManager.class);
|
||||
testOrchestrator._entityMgr = mock(EntityManager.class);
|
||||
testOrchestrator.extensionHelper = mock(ExtensionHelper.class);
|
||||
networkExtensionElement = mock(NetworkExtensionElement.class);
|
||||
ReflectionTestUtils.setField(testOrchestrator, "networkExtensionElement", networkExtensionElement);
|
||||
DhcpServiceProvider provider = mock(DhcpServiceProvider.class);
|
||||
|
||||
Map<Network.Capability, String> capabilities = new HashMap<Network.Capability, String>();
|
||||
|
|
@ -1010,4 +1020,63 @@ public class NetworkOrchestratorTest extends TestCase {
|
|||
assertEquals("testtag", nicProfile.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for getNetworkElementsIncludingExtensions
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void getNetworkElementsIncludingExtensionsReturnsBaseListWhenNoExtensions() {
|
||||
when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class);
|
||||
List<NetworkElement> elements = new ArrayList<>(List.of(dhcpProvider));
|
||||
testOrchestrator.networkElements = elements;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<NetworkElement> result =
|
||||
(List<NetworkElement>) ReflectionTestUtils
|
||||
.invokeMethod(testOrchestrator, "getNetworkElementsIncludingExtensions");
|
||||
assertNotNull(result);
|
||||
assertEquals(elements.size(), result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNetworkElementsIncludingExtensionsAddsExtensionElements() {
|
||||
Extension ext = mock(Extension.class);
|
||||
when(ext.getName()).thenReturn("my-net-ext");
|
||||
when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator))
|
||||
.thenReturn(List.of(ext));
|
||||
|
||||
NetworkExtensionElement extElement = mock(NetworkExtensionElement.class);
|
||||
when(networkExtensionElement.withProviderName("my-net-ext")).thenReturn(extElement);
|
||||
|
||||
DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class);
|
||||
testOrchestrator.networkElements = new ArrayList<>(List.of(dhcpProvider));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<NetworkElement> result =
|
||||
(List<NetworkElement>) ReflectionTestUtils
|
||||
.invokeMethod(testOrchestrator, "getNetworkElementsIncludingExtensions");
|
||||
assertNotNull(result);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.contains(extElement));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNetworkElementsIncludingExtensionsReturnsBaseListWhenExtensionHelperReturnsNull() {
|
||||
when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator))
|
||||
.thenReturn(null);
|
||||
|
||||
DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class);
|
||||
testOrchestrator.networkElements = new ArrayList<>(List.of(dhcpProvider));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<NetworkElement> result =
|
||||
(List<NetworkElement>) ReflectionTestUtils
|
||||
.invokeMethod(testOrchestrator, "getNetworkElementsIncludingExtensions");
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import org.springframework.stereotype.Component;
|
|||
import com.cloud.network.Network;
|
||||
import com.cloud.network.Network.Event;
|
||||
import com.cloud.network.Network.GuestType;
|
||||
import com.cloud.network.Network.Provider;
|
||||
import com.cloud.network.Network.Service;
|
||||
import com.cloud.network.Network.State;
|
||||
import com.cloud.network.Networks.BroadcastDomainType;
|
||||
|
|
@ -390,7 +389,7 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
|
|||
final TransactionLegacy txn = TransactionLegacy.currentTxn();
|
||||
txn.start();
|
||||
for (final String service : serviceProviderMap.keySet()) {
|
||||
final NetworkServiceMapVO serviceMap = new NetworkServiceMapVO(networkId, Service.getService(service), Provider.getProvider(serviceProviderMap.get(service)));
|
||||
final NetworkServiceMapVO serviceMap = new NetworkServiceMapVO(networkId, Service.getService(service).getName(), serviceProviderMap.get(service));
|
||||
_ntwkSvcMap.persist(serviceMap);
|
||||
}
|
||||
txn.commit();
|
||||
|
|
|
|||
|
|
@ -81,6 +81,12 @@ public class NetworkServiceMapVO implements InternalIdentity {
|
|||
this.provider = provider.getName();
|
||||
}
|
||||
|
||||
public NetworkServiceMapVO(long networkId, String serviceName, String providerName) {
|
||||
this.networkId = networkId;
|
||||
this.service = serviceName;
|
||||
this.provider = providerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder("[Network Service[");
|
||||
|
|
|
|||
|
|
@ -97,6 +97,9 @@ public class PhysicalNetworkServiceProviderVO implements PhysicalNetworkServiceP
|
|||
@Column(name = "networkacl_service_provided")
|
||||
boolean networkAclServiceProvided;
|
||||
|
||||
@Column(name = "custom_action_service_provided")
|
||||
boolean customActionServiceProvided;
|
||||
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
Date removed;
|
||||
|
||||
|
|
@ -278,6 +281,7 @@ public class PhysicalNetworkServiceProviderVO implements PhysicalNetworkServiceP
|
|||
this.setUserdataServiceProvided(services.contains(Service.UserData));
|
||||
this.setSecuritygroupServiceProvided(services.contains(Service.SecurityGroup));
|
||||
this.setNetworkAclServiceProvided(services.contains(Service.NetworkACL));
|
||||
this.setCustomActionServiceProvided(services.contains(Service.CustomAction));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -316,6 +320,9 @@ public class PhysicalNetworkServiceProviderVO implements PhysicalNetworkServiceP
|
|||
if (this.isSecuritygroupServiceProvided()) {
|
||||
services.add(Service.SecurityGroup);
|
||||
}
|
||||
if (this.isCustomActionServiceProvided()) {
|
||||
services.add(Service.CustomAction);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
@ -327,4 +334,12 @@ public class PhysicalNetworkServiceProviderVO implements PhysicalNetworkServiceP
|
|||
public void setNetworkAclServiceProvided(boolean networkAclServiceProvided) {
|
||||
this.networkAclServiceProvided = networkAclServiceProvided;
|
||||
}
|
||||
|
||||
public boolean isCustomActionServiceProvided() {
|
||||
return customActionServiceProvided;
|
||||
}
|
||||
|
||||
public void setCustomActionServiceProvided(boolean customActionServiceProvided) {
|
||||
this.customActionServiceProvided = customActionServiceProvided;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ import javax.persistence.GenerationType;
|
|||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.cloud.network.Network.Provider;
|
||||
import com.cloud.network.Network.Service;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
@Entity
|
||||
|
|
@ -72,10 +70,10 @@ public class VpcServiceMapVO {
|
|||
public VpcServiceMapVO() {
|
||||
}
|
||||
|
||||
public VpcServiceMapVO(long vpcId, Service service, Provider provider) {
|
||||
public VpcServiceMapVO(long vpcId, String serviceName, String providerName) {
|
||||
this.vpcId = vpcId;
|
||||
this.service = service.getName();
|
||||
this.provider = provider.getName();
|
||||
this.service = serviceName;
|
||||
this.provider = providerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ public class VpcDaoImpl extends GenericDaoBase<VpcVO, Long> implements VpcDao {
|
|||
txn.start();
|
||||
for (String service : serviceProviderMap.keySet()) {
|
||||
for (String provider : serviceProviderMap.get(service)) {
|
||||
VpcServiceMapVO serviceMap = new VpcServiceMapVO(vpcId, Network.Service.getService(service), Network.Provider.getProvider(provider));
|
||||
VpcServiceMapVO serviceMap = new VpcServiceMapVO(vpcId, Network.Service.getService(service).getName(), provider);
|
||||
_vpcSvcMap.persist(serviceMap);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,3 +131,10 @@ CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_tariff_usage` (
|
|||
-- Add the 'keep_mac_address_on_public_nic' column to the 'cloud.networks' and 'cloud.vpc' tables
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.networks', 'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc', 'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
|
||||
|
||||
-- Increase length of value of extension details from 255 to 4096 to support longer details value
|
||||
CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('cloud.extension_details', 'value', 'value', 'VARCHAR(4096)');
|
||||
CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('cloud.extension_resource_map_details', 'value', 'value', 'VARCHAR(4096)');
|
||||
|
||||
-- Add CustomAction service support to physical_network_service_providers
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.physical_network_service_providers', 'custom_action_service_provided', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "Is Custom Action service provided"');
|
||||
|
|
|
|||
|
|
@ -70,6 +70,11 @@ public class ListExtensionsCmd extends BaseListCmd {
|
|||
+ " When no parameters are passed, all the details are returned.")
|
||||
private List<String> details;
|
||||
|
||||
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING,
|
||||
description = "Type of the extension (e.g. Orchestrator, NetworkOrchestrator)",
|
||||
since = "4.23.0")
|
||||
private String type;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -82,6 +87,10 @@ public class ListExtensionsCmd extends BaseListCmd {
|
|||
return extensionId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public EnumSet<ApiConstants.ExtensionDetails> getDetails() throws InvalidParameterValueException {
|
||||
if (CollectionUtils.isEmpty(details)) {
|
||||
return EnumSet.of(ApiConstants.ExtensionDetails.all);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.framework.extensions.api;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.ExtensionResponse;
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.framework.extensions.manager.ExtensionsManager;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "updateRegisteredExtension",
|
||||
description = "Update details for an extension registered with a resource",
|
||||
responseObject = ExtensionResponse.class,
|
||||
responseHasSensitiveInfo = false,
|
||||
entityType = {Extension.class},
|
||||
authorized = {RoleType.Admin},
|
||||
since = "4.23.0")
|
||||
public class UpdateRegisteredExtensionCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
ExtensionsManager extensionsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.EXTENSION_ID, type = CommandType.UUID, required = true,
|
||||
entityType = ExtensionResponse.class, description = "ID of the extension")
|
||||
private Long extensionId;
|
||||
|
||||
@Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true,
|
||||
description = "ID of the resource where the extension is registered")
|
||||
private String resourceId;
|
||||
|
||||
@Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true,
|
||||
description = "Type of the resource")
|
||||
private String resourceType;
|
||||
|
||||
@Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP,
|
||||
description = "Details in key/value pairs using format details[i].keyname=keyvalue. Example: details[0].endpoint.url=urlvalue")
|
||||
protected Map details;
|
||||
|
||||
@Parameter(name = ApiConstants.CLEAN_UP_DETAILS,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "Optional boolean field, which indicates if details should be cleaned up or not " +
|
||||
"(If set to true, details removed for this registration, details field ignored; " +
|
||||
"if false or not set, details can be updated through details map)")
|
||||
private Boolean cleanupDetails;
|
||||
|
||||
public Long getExtensionId() {
|
||||
return extensionId;
|
||||
}
|
||||
|
||||
public String getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
return convertDetailsToMap(details);
|
||||
}
|
||||
|
||||
public Boolean isCleanupDetails() {
|
||||
return cleanupDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ServerApiException {
|
||||
Extension extension = extensionsManager.updateRegisteredExtensionWithResource(this);
|
||||
ExtensionResponse response = extensionsManager.createExtensionResponse(extension,
|
||||
EnumSet.of(ApiConstants.ExtensionDetails.all));
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.Extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getExtensionId();
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,9 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.framework.extensions.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.framework.extensions.vo.ExtensionVO;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
|
@ -23,4 +26,6 @@ import com.cloud.utils.db.GenericDao;
|
|||
public interface ExtensionDao extends GenericDao<ExtensionVO, Long> {
|
||||
|
||||
ExtensionVO findByName(String name);
|
||||
|
||||
List<ExtensionVO> listByType(Extension.Type type);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
package org.apache.cloudstack.framework.extensions.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.framework.extensions.vo.ExtensionVO;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
|
|
@ -39,7 +42,13 @@ public class ExtensionDaoImpl extends GenericDaoBase<ExtensionVO, Long> implemen
|
|||
public ExtensionVO findByName(String name) {
|
||||
SearchCriteria<ExtensionVO> sc = AllFieldSearch.create();
|
||||
sc.setParameters("name", name);
|
||||
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExtensionVO> listByType(Extension.Type type) {
|
||||
SearchCriteria<ExtensionVO> sc = AllFieldSearch.create();
|
||||
sc.setParameters("type", type);
|
||||
return listBy(sc);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,5 +28,9 @@ public interface ExtensionResourceMapDao extends GenericDao<ExtensionResourceMap
|
|||
|
||||
ExtensionResourceMapVO findByResourceIdAndType(long resourceId, ExtensionResourceMap.ResourceType resourceType);
|
||||
|
||||
List<Long> listResourceIdsByExtensionIdAndType(long extensionId,ExtensionResourceMap.ResourceType resourceType);
|
||||
List<ExtensionResourceMapVO> listByResourceIdAndType(long resourceId, ExtensionResourceMap.ResourceType resourceType);
|
||||
|
||||
List<Long> listResourceIdsByExtensionIdAndType(long extensionId, ExtensionResourceMap.ResourceType resourceType);
|
||||
|
||||
List<Long> listResourceIdsByType(ExtensionResourceMap.ResourceType resourceType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,15 @@ public class ExtensionResourceMapDaoImpl extends GenericDaoBase<ExtensionResourc
|
|||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExtensionResourceMapVO> listByResourceIdAndType(long resourceId,
|
||||
ExtensionResourceMap.ResourceType resourceType) {
|
||||
SearchCriteria<ExtensionResourceMapVO> sc = genericSearch.create();
|
||||
sc.setParameters("resourceId", resourceId);
|
||||
sc.setParameters("resourceType", resourceType);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listResourceIdsByExtensionIdAndType(long extensionId, ExtensionResourceMap.ResourceType resourceType) {
|
||||
GenericSearchBuilder<ExtensionResourceMapVO, Long> sb = createSearchBuilder(Long.class);
|
||||
|
|
@ -67,4 +76,15 @@ public class ExtensionResourceMapDaoImpl extends GenericDaoBase<ExtensionResourc
|
|||
sc.setParameters("resourceType", resourceType);
|
||||
return customSearch(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listResourceIdsByType(ExtensionResourceMap.ResourceType resourceType) {
|
||||
GenericSearchBuilder<ExtensionResourceMapVO, Long> sb = createSearchBuilder(Long.class);
|
||||
sb.selectFields(sb.entity().getResourceId());
|
||||
sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
SearchCriteria<Long> sc = sb.create();
|
||||
sc.setParameters("resourceType", resourceType);
|
||||
return customSearch(sc, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import org.apache.cloudstack.framework.extensions.api.RunCustomActionCmd;
|
|||
import org.apache.cloudstack.framework.extensions.api.UnregisterExtensionCmd;
|
||||
import org.apache.cloudstack.framework.extensions.api.UpdateCustomActionCmd;
|
||||
import org.apache.cloudstack.framework.extensions.api.UpdateExtensionCmd;
|
||||
import org.apache.cloudstack.framework.extensions.api.UpdateRegisteredExtensionCmd;
|
||||
import org.apache.cloudstack.framework.extensions.command.ExtensionServerActionBaseCommand;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
|
|
@ -65,6 +66,8 @@ public interface ExtensionsManager extends Manager {
|
|||
|
||||
Extension unregisterExtensionWithResource(UnregisterExtensionCmd cmd);
|
||||
|
||||
Extension updateRegisteredExtensionWithResource(UpdateRegisteredExtensionCmd cmd);
|
||||
|
||||
Extension updateExtension(UpdateExtensionCmd cmd);
|
||||
|
||||
Extension registerExtensionWithResource(RegisterExtensionCmd cmd);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -40,7 +40,7 @@ public class ExtensionDetailsVO implements ResourceDetail {
|
|||
@Column(name = "name", nullable = false, length = 255)
|
||||
private String name;
|
||||
|
||||
@Column(name = "value", nullable = false, length = 255)
|
||||
@Column(name = "value", nullable = false, length = 4096)
|
||||
private String value;
|
||||
|
||||
@Column(name = "display")
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class ExtensionResourceMapDetailsVO implements ResourceDetail {
|
|||
@Column(name = "name", nullable = false, length = 255)
|
||||
private String name;
|
||||
|
||||
@Column(name = "value", nullable = false, length = 255)
|
||||
@Column(name = "value", nullable = false, length = 4096)
|
||||
private String value;
|
||||
|
||||
@Column(name = "display")
|
||||
|
|
|
|||
|
|
@ -33,4 +33,7 @@
|
|||
<bean id="ExtensionsManager" class="org.apache.cloudstack.framework.extensions.manager.ExtensionsManagerImpl" />
|
||||
<bean id="ExtensionCustomActionDaoImpl" class="org.apache.cloudstack.framework.extensions.dao.ExtensionCustomActionDaoImpl" />
|
||||
<bean id="ExtensionCustomActionDetailsDaoImpl" class="org.apache.cloudstack.framework.extensions.dao.ExtensionCustomActionDetailsDaoImpl" />
|
||||
<bean id="networkExtension" class="org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement">
|
||||
<property name="name" value="NetworkExtension" />
|
||||
</bean>
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.cloudstack.framework.extensions.api;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
@ -89,4 +90,21 @@ public class ListExtensionsCmdTest {
|
|||
setPrivateField("details", detailsList);
|
||||
cmd.getDetails();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for type getter
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void testGetTypeReturnsValueWhenSet() {
|
||||
setPrivateField("type", "NetworkOrchestrator");
|
||||
assertEquals("NetworkOrchestrator", cmd.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTypeReturnsNullWhenUnset() {
|
||||
setPrivateField("type", null);
|
||||
assertNull(cmd.getType());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.framework.extensions.api;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.ExtensionResponse;
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.framework.extensions.manager.ExtensionsManager;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
|
||||
public class UpdateRegisteredExtensionCmdTest {
|
||||
|
||||
private UpdateRegisteredExtensionCmd cmd;
|
||||
private ExtensionsManager extensionsManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
cmd = Mockito.spy(new UpdateRegisteredExtensionCmd());
|
||||
extensionsManager = mock(ExtensionsManager.class);
|
||||
ReflectionTestUtils.setField(cmd, "extensionsManager", extensionsManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extensionIdReturnsNullWhenUnset() {
|
||||
ReflectionTestUtils.setField(cmd, "extensionId", null);
|
||||
assertNull(cmd.getExtensionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extensionIdReturnsValueWhenSet() {
|
||||
Long extensionId = 42L;
|
||||
ReflectionTestUtils.setField(cmd, "extensionId", extensionId);
|
||||
assertEquals(extensionId, cmd.getExtensionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupDetailsReturnsNullWhenUnset() {
|
||||
ReflectionTestUtils.setField(cmd, "cleanupDetails", null);
|
||||
assertNull(cmd.isCleanupDetails());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupDetailsReturnsTrueWhenSet() {
|
||||
ReflectionTestUtils.setField(cmd, "cleanupDetails", true);
|
||||
assertTrue(cmd.isCleanupDetails());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupDetailsReturnsFalseWhenSetToFalse() {
|
||||
ReflectionTestUtils.setField(cmd, "cleanupDetails", false);
|
||||
assertFalse(cmd.isCleanupDetails());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEntityOwnerIdReturnsSystemAccountId() {
|
||||
assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getApiResourceTypeReturnsExtension() {
|
||||
assertEquals(ApiCommandResourceType.Extension, cmd.getApiResourceType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getApiResourceIdReturnsExtensionId() {
|
||||
Long extensionId = 99L;
|
||||
ReflectionTestUtils.setField(cmd, "extensionId", extensionId);
|
||||
assertEquals(extensionId, cmd.getApiResourceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceIdReturnsValueWhenSet() {
|
||||
String resourceId = "resource-123";
|
||||
ReflectionTestUtils.setField(cmd, "resourceId", resourceId);
|
||||
assertEquals(resourceId, cmd.getResourceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceTypeReturnsValueWhenSet() {
|
||||
String resourceType = "PhysicalNetwork";
|
||||
ReflectionTestUtils.setField(cmd, "resourceType", resourceType);
|
||||
assertEquals(resourceType, cmd.getResourceType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detailsReturnsEmptyMapWhenUnset() {
|
||||
ReflectionTestUtils.setField(cmd, "details", null);
|
||||
Map<String, String> details = cmd.getDetails();
|
||||
assertNotNull(details);
|
||||
assertTrue(details.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeSetsExtensionResponseWhenManagerSucceeds() {
|
||||
Extension extension = mock(Extension.class);
|
||||
ExtensionResponse response = mock(ExtensionResponse.class);
|
||||
when(extensionsManager.updateRegisteredExtensionWithResource(cmd)).thenReturn(extension);
|
||||
when(extensionsManager.createExtensionResponse(extension, EnumSet.of(ApiConstants.ExtensionDetails.all)))
|
||||
.thenReturn(response);
|
||||
|
||||
doNothing().when(cmd).setResponseObject(any());
|
||||
|
||||
cmd.execute();
|
||||
|
||||
verify(extensionsManager).updateRegisteredExtensionWithResource(cmd);
|
||||
verify(extensionsManager).createExtensionResponse(extension, EnumSet.of(ApiConstants.ExtensionDetails.all));
|
||||
verify(cmd).setResponseObject(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeThrowsServerApiExceptionWhenManagerFails() {
|
||||
when(extensionsManager.updateRegisteredExtensionWithResource(cmd))
|
||||
.thenThrow(new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update registered extension"));
|
||||
|
||||
try {
|
||||
cmd.execute();
|
||||
fail("Expected ServerApiException");
|
||||
} catch (ServerApiException e) {
|
||||
assertEquals("Failed to update registered extension", e.getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,4 +83,55 @@ public class ExtensionResourceMapDaoImplTest {
|
|||
when(dao.listResourceIdsByExtensionIdAndType(1L, ExtensionResourceMap.ResourceType.Cluster)).thenReturn(expectedIds);
|
||||
assertEquals(expectedIds, dao.listResourceIdsByExtensionIdAndType(1L, ExtensionResourceMap.ResourceType.Cluster));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for new methods: listByResourceIdAndType, listResourceIdsByType
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void listByResourceIdAndTypeReturnsEmptyListWhenNoMatch() {
|
||||
when(dao.listByResourceIdAndType(999L, ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(List.of());
|
||||
assertTrue(dao.listByResourceIdAndType(999L, ExtensionResourceMap.ResourceType.PhysicalNetwork).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listByResourceIdAndTypeReturnsMatchingEntries() {
|
||||
ExtensionResourceMapVO map1 = new ExtensionResourceMapVO();
|
||||
map1.setResourceId(42L);
|
||||
map1.setResourceType(ExtensionResourceMap.ResourceType.PhysicalNetwork);
|
||||
ExtensionResourceMapVO map2 = new ExtensionResourceMapVO();
|
||||
map2.setResourceId(42L);
|
||||
map2.setResourceType(ExtensionResourceMap.ResourceType.PhysicalNetwork);
|
||||
List<ExtensionResourceMapVO> expected = List.of(map1, map2);
|
||||
when(dao.listByResourceIdAndType(42L, ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(expected);
|
||||
List<ExtensionResourceMapVO> result = dao.listByResourceIdAndType(42L, ExtensionResourceMap.ResourceType.PhysicalNetwork);
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listByResourceIdAndTypeDifferentiatesResourceTypes() {
|
||||
ExtensionResourceMapVO clusterMap = new ExtensionResourceMapVO();
|
||||
clusterMap.setResourceType(ExtensionResourceMap.ResourceType.Cluster);
|
||||
when(dao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.Cluster)).thenReturn(List.of(clusterMap));
|
||||
when(dao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(List.of());
|
||||
|
||||
assertEquals(1, dao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.Cluster).size());
|
||||
assertTrue(dao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.PhysicalNetwork).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listResourceIdsByTypeReturnsEmptyListWhenNoMatch() {
|
||||
when(dao.listResourceIdsByType(ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(List.of());
|
||||
assertTrue(dao.listResourceIdsByType(ExtensionResourceMap.ResourceType.PhysicalNetwork).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listResourceIdsByTypeReturnsMatchingIds() {
|
||||
List<Long> expectedIds = List.of(5L, 10L, 15L);
|
||||
when(dao.listResourceIdsByType(ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(expectedIds);
|
||||
List<Long> result = dao.listResourceIdsByType(ExtensionResourceMap.ResourceType.PhysicalNetwork);
|
||||
assertEquals(3, result.size());
|
||||
assertEquals(expectedIds, result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1559,6 +1559,10 @@ public class ApiDBUtils {
|
|||
return s_networkModel.canElementEnableIndividualServices(serviceProvider);
|
||||
}
|
||||
|
||||
public static boolean canElementEnableIndividualServicesByName(String providerName) {
|
||||
return s_networkModel.canElementEnableIndividualServicesByName(providerName);
|
||||
}
|
||||
|
||||
public static Pair<Long, Boolean> getDomainNetworkDetails(long networkId) {
|
||||
NetworkDomainVO map = s_networkDomainDao.getDomainNetworkMapByNetworkId(networkId);
|
||||
|
||||
|
|
|
|||
|
|
@ -548,6 +548,8 @@ public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {
|
|||
ResourceIconManager resourceIconManager;
|
||||
@Inject
|
||||
AsyncJobDao asyncJobDao;
|
||||
@Inject
|
||||
NetworkModel networkModel;
|
||||
|
||||
public static String getPrettyDomainPath(String path) {
|
||||
if (path == null) {
|
||||
|
|
@ -3307,9 +3309,11 @@ public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {
|
|||
}
|
||||
response.setServices(services);
|
||||
|
||||
Provider serviceProvider = Provider.getProvider(result.getProviderName());
|
||||
boolean canEnableIndividualServices = ApiDBUtils.canElementEnableIndividualServices(serviceProvider);
|
||||
response.setCanEnableIndividualServices(canEnableIndividualServices);
|
||||
Provider serviceProvider = networkModel.resolveProvider(result.getProviderName());
|
||||
if (serviceProvider != null) {
|
||||
boolean canEnableIndividualServices = ApiDBUtils.canElementEnableIndividualServices(serviceProvider);
|
||||
response.setCanEnableIndividualServices(canEnableIndividualServices);
|
||||
}
|
||||
|
||||
response.setObjectName("networkserviceprovider");
|
||||
return response;
|
||||
|
|
|
|||
|
|
@ -7268,7 +7268,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
}
|
||||
for (final String prvNameStr : svcPrv.get(serviceStr)) {
|
||||
// check if provider is supported
|
||||
final Network.Provider provider = Network.Provider.getProvider(prvNameStr);
|
||||
final Network.Provider provider = _networkModel.resolveProvider(prvNameStr);
|
||||
if (provider == null) {
|
||||
throw new InvalidParameterValueException("Invalid service provider: " + prvNameStr);
|
||||
}
|
||||
|
|
@ -7954,7 +7954,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
// 1) Vaidate the detail values - have to match the lb provider
|
||||
// name
|
||||
final String providerStr = details.get(detail);
|
||||
if (Network.Provider.getProvider(providerStr) == null) {
|
||||
if (_networkModel.resolveProvider(providerStr) == null) {
|
||||
throw new InvalidParameterValueException("Invalid value " + providerStr + " for the detail " + detail);
|
||||
}
|
||||
if (serviceProviderMap.get(Service.Lb) != null) {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@ import com.cloud.network.element.IpDeployingRequester;
|
|||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.network.element.UserDataServiceProvider;
|
||||
import com.cloud.network.router.VirtualRouter;
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement;
|
||||
import com.cloud.network.rules.FirewallRule.Purpose;
|
||||
import com.cloud.network.rules.FirewallRuleVO;
|
||||
import com.cloud.network.rules.dao.PortForwardingRulesDao;
|
||||
|
|
@ -234,6 +237,11 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
private NetworkService _networkService;
|
||||
@Inject
|
||||
TungstenGuestNetworkIpAddressDao tungstenGuestNetworkIpAddressDao;
|
||||
@Inject
|
||||
ExtensionHelper extensionHelper;
|
||||
@Inject
|
||||
private NetworkExtensionElement networkExtensionElement;
|
||||
|
||||
|
||||
private final HashMap<String, NetworkOfferingVO> _systemNetworks = new HashMap<String, NetworkOfferingVO>(5);
|
||||
|
||||
|
|
@ -262,15 +270,39 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
public NetworkElement getElementImplementingProvider(String providerName) {
|
||||
String elementName = s_providerToNetworkElementMap.get(providerName);
|
||||
NetworkElement element = AdapterBase.getAdapterByName(networkElements, elementName);
|
||||
if (element == null && extensionHelper.isNetworkExtensionProvider(providerName)) {
|
||||
// Provider is an extension-backed external network provider.
|
||||
// Initialize a copy of NetworkExtensionElement
|
||||
if (networkExtensionElement != null) {
|
||||
element = networkExtensionElement.withProviderName(providerName);
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective network capabilities for an extension-backed external
|
||||
* network provider on the given physical network.
|
||||
*
|
||||
* @param physicalNetworkId physical network ID (may be null for offering-level queries)
|
||||
* @param providerName provider / extension name
|
||||
* @return per-provider capabilities, or empty map if not found
|
||||
*/
|
||||
protected Map<Service, Map<Capability, String>> getExternalProviderCapabilities(
|
||||
Long physicalNetworkId, String providerName) {
|
||||
return extensionHelper.getNetworkCapabilitiesForProvider(physicalNetworkId, providerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Service> getElementServices(Provider provider) {
|
||||
NetworkElement element = getElementImplementingProvider(provider.getName());
|
||||
if (element == null) {
|
||||
throw new InvalidParameterValueException("Unable to find the Network Element implementing the Service Provider '" + provider.getName() + "'");
|
||||
}
|
||||
if (extensionHelper.isNetworkExtensionProvider(provider.getName())) {
|
||||
Map<Service, Map<Capability, String>> caps = getExternalProviderCapabilities(null, provider.getName());
|
||||
return new ArrayList<Service>(caps.keySet());
|
||||
}
|
||||
return new ArrayList<Service>(element.getCapabilities().keySet());
|
||||
}
|
||||
|
||||
|
|
@ -283,6 +315,24 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
return element.canEnableIndividualServices();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canElementEnableIndividualServicesByName(String providerName) {
|
||||
if (providerName == null) {
|
||||
return false;
|
||||
}
|
||||
// Try resolve to enum Provider first
|
||||
Provider provider = resolveProvider(providerName);
|
||||
if (provider != null) {
|
||||
try {
|
||||
return canElementEnableIndividualServices(provider);
|
||||
} catch (Exception e) {
|
||||
logger.debug("canElementEnableIndividualServices failed for provider {}: {}", providerName, e.getMessage());
|
||||
}
|
||||
}
|
||||
// Unknown provider: be conservative and return false
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<Purpose> getPublicIpPurposeInRules(PublicIpAddress ip, boolean includeRevoked, boolean includingFirewall) {
|
||||
Set<Purpose> result = new HashSet<Purpose>();
|
||||
List<FirewallRuleVO> rules = null;
|
||||
|
|
@ -435,8 +485,11 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
if (providers == null) {
|
||||
providers = new HashSet<Provider>();
|
||||
}
|
||||
providers.add(Provider.getProvider(nsm.getProvider()));
|
||||
map.put(Service.getService(nsm.getService()), providers);
|
||||
Provider provider = resolveProvider(nsm.getProvider());
|
||||
if (provider != null) {
|
||||
providers.add(provider);
|
||||
map.put(Service.getService(nsm.getService()), providers);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
|
@ -481,16 +534,45 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
Map<Provider, Set<Service>> map = new HashMap<Provider, Set<Service>>();
|
||||
List<NetworkServiceMapVO> nsms = _ntwkSrvcDao.getServicesInNetwork(networkId);
|
||||
for (NetworkServiceMapVO nsm : nsms) {
|
||||
Set<Service> services = map.get(Provider.getProvider(nsm.getProvider()));
|
||||
Provider provider = resolveProvider(nsm.getProvider());
|
||||
if (provider == null) {
|
||||
continue;
|
||||
}
|
||||
Set<Service> services = map.get(provider);
|
||||
if (services == null) {
|
||||
services = new HashSet<Service>();
|
||||
}
|
||||
services.add(Service.getService(nsm.getService()));
|
||||
map.put(Provider.getProvider(nsm.getProvider()), services);
|
||||
map.put(provider, services);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a provider name to a {@link Provider} instance.
|
||||
*
|
||||
* <p>For well-known providers, returns the static constant from
|
||||
* {@link Provider#getProvider(String)}. For dynamic NetworkOrchestrator
|
||||
* extension providers (whose names are not in the static registry), returns
|
||||
* a transient {@link Provider} with the given name so that the caller can
|
||||
* still dispatch to the correct {@link NetworkExtensionElement} via
|
||||
* {@link #getElementImplementingProvider(String)}.</p>
|
||||
*
|
||||
* @param providerName the provider name from {@code ntwk_service_map}
|
||||
* @return a {@link Provider} instance, or {@code null} if not resolvable
|
||||
*/
|
||||
@Override
|
||||
public Provider resolveProvider(String providerName) {
|
||||
Provider provider = Provider.getProvider(providerName);
|
||||
if (provider == null && extensionHelper.isNetworkExtensionProvider(providerName)) {
|
||||
// Dynamic extension-backed provider: create a transient Provider that preserves
|
||||
// the actual extension name. getElementImplementingProvider() handles this name
|
||||
// by detecting it as an extension provider and returning NetworkExtensionElement.
|
||||
provider = Provider.createTransientProvider(providerName);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Provider, ArrayList<PublicIpAddress>> getProviderToIpList(Network network, Map<PublicIpAddress, Set<Service>> ipToServices) {
|
||||
NetworkOffering offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
|
||||
|
|
@ -694,11 +776,21 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
|
||||
// list all services of this networkOffering
|
||||
List<NetworkServiceMapVO> servicesMap = _ntwkSrvcDao.getServicesInNetwork(networkId);
|
||||
|
||||
// Resolve the physical network once for external provider lookups
|
||||
NetworkVO network = _networksDao.findById(networkId);
|
||||
Long physicalNetworkId = network != null ? network.getPhysicalNetworkId() : null;
|
||||
|
||||
for (NetworkServiceMapVO instance : servicesMap) {
|
||||
Service service = Service.getService(instance.getService());
|
||||
NetworkElement element = getElementImplementingProvider(instance.getProvider());
|
||||
if (element != null) {
|
||||
Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
|
||||
Map<Service, Map<Capability, String>> elementCapabilities;
|
||||
if (extensionHelper.isNetworkExtensionProvider(instance.getProvider()) && physicalNetworkId != null) {
|
||||
elementCapabilities = getExternalProviderCapabilities(physicalNetworkId, instance.getProvider());
|
||||
} else {
|
||||
elementCapabilities = element.getCapabilities();
|
||||
}
|
||||
if (elementCapabilities != null) {
|
||||
networkCapabilities.put(service, elementCapabilities.get(service));
|
||||
}
|
||||
|
|
@ -724,10 +816,15 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
|
||||
NetworkElement element = getElementImplementingProvider(provider);
|
||||
if (element != null) {
|
||||
Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
|
||||
;
|
||||
Map<Service, Map<Capability, String>> elementCapabilities;
|
||||
if (extensionHelper.isNetworkExtensionProvider(provider)) {
|
||||
elementCapabilities = getExternalProviderCapabilities(null, provider);
|
||||
} else {
|
||||
elementCapabilities = element.getCapabilities();
|
||||
}
|
||||
|
||||
if (elementCapabilities == null || !elementCapabilities.containsKey(service)) {
|
||||
// TBD: We should be sending providerId and not the offering object itself.
|
||||
throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() +
|
||||
" implementing Provider=" + provider);
|
||||
}
|
||||
|
|
@ -758,12 +855,22 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
// we have to calculate capabilities for all of them
|
||||
String provider = providers.get(0);
|
||||
|
||||
// Check if this is an extension-backed external network provider first.
|
||||
// These providers are not in the static s_providerToNetworkElementMap so
|
||||
// we resolve their capabilities from the extension details directly.
|
||||
if (extensionHelper.isNetworkExtensionProvider(provider)) {
|
||||
Map<Service, Map<Capability, String>> extCaps = getExternalProviderCapabilities(null, provider);
|
||||
if (extCaps != null && extCaps.containsKey(service)) {
|
||||
return extCaps.get(service);
|
||||
}
|
||||
return serviceCapabilities;
|
||||
}
|
||||
|
||||
// FIXME we return the capabilities of the first provider of the service - what if we have multiple providers
|
||||
// for same Service?
|
||||
NetworkElement element = getElementImplementingProvider(provider);
|
||||
if (element != null) {
|
||||
Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
|
||||
;
|
||||
|
||||
if (elementCapabilities == null || !elementCapabilities.containsKey(service)) {
|
||||
// TBD: We should be sending providerId and not the offering object itself.
|
||||
|
|
@ -979,7 +1086,6 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean areServicesSupportedByNetworkOffering(long networkOfferingId, Service... services) {
|
||||
return (_ntwkOfferingSrvcDao.areServicesSupportedByNetworkOffering(networkOfferingId, services));
|
||||
|
|
@ -1161,7 +1267,11 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
if (providers == null) {
|
||||
providers = new HashSet<Provider>();
|
||||
}
|
||||
providers.add(Provider.getProvider(instance.getProvider()));
|
||||
|
||||
final Provider provider = resolveProvider(instance.getProvider());
|
||||
if (provider != null) {
|
||||
providers.add(provider);
|
||||
}
|
||||
serviceProviderMap.put(Service.getService(service), providers);
|
||||
}
|
||||
|
||||
|
|
@ -1201,9 +1311,87 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
}
|
||||
}
|
||||
|
||||
// Also include extension-backed NetworkExtension providers.
|
||||
// resolveProvider() creates a transient Provider (not added to the static list)
|
||||
// for extension names that are not in the built-in registry.
|
||||
try {
|
||||
List<PhysicalNetworkServiceProviderVO> nsps = _pNSPDao.listAll();
|
||||
if (CollectionUtils.isNotEmpty(nsps)) {
|
||||
Set<String> extensionProviderNames = new HashSet<>();
|
||||
List<Extension> extensions = extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator);
|
||||
if (extensions != null) {
|
||||
for (Extension extension : extensions) {
|
||||
if (extension == null || StringUtils.isBlank(extension.getName())) {
|
||||
continue;
|
||||
}
|
||||
extensionProviderNames.add(extension.getName().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
if (!extensionProviderNames.isEmpty()) {
|
||||
// Avoid duplicate provider names across multiple physical networks.
|
||||
Set<String> addedExtProviders = new HashSet<>();
|
||||
for (PhysicalNetworkServiceProviderVO nsp : nsps) {
|
||||
String provName = nsp.getProviderName();
|
||||
if (StringUtils.isBlank(provName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (addedExtProviders.contains(provName) || !extensionProviderNames.contains(provName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter by service if requested: check the NSP's service flags.
|
||||
if (service != null && !isServiceProvidedByNsp(nsp, service)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Resolve or create a transient Provider for the extension name.
|
||||
Provider extProvider = resolveProvider(provName);
|
||||
if (extProvider == null) {
|
||||
continue;
|
||||
}
|
||||
supportedProviders.add(extProvider);
|
||||
addedExtProviders.add(provName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to include extension-backed providers in listSupportedNetworkServiceProviders: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return new ArrayList<Provider>(supportedProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given {@link com.cloud.network.dao.PhysicalNetworkServiceProviderVO}
|
||||
* has its service flag set for {@code service}.
|
||||
*
|
||||
* <p>This is used by {@link #listSupportedNetworkServiceProviders} to filter extension-backed
|
||||
* providers (looked up via {@link com.cloud.network.dao.PhysicalNetworkServiceProviderDao#listAll})
|
||||
* without having to query each extension's capability JSON.</p>
|
||||
*/
|
||||
private boolean isServiceProvidedByNsp(
|
||||
PhysicalNetworkServiceProviderVO nsp, Service service) {
|
||||
if (service == null) {
|
||||
return true;
|
||||
}
|
||||
if (service == Service.Dhcp) return nsp.isDhcpServiceProvided();
|
||||
if (service == Service.Dns) return nsp.isDnsServiceProvided();
|
||||
if (service == Service.Firewall) return nsp.isFirewallServiceProvided();
|
||||
if (service == Service.Gateway) return nsp.isGatewayServiceProvided();
|
||||
if (service == Service.Lb) return nsp.isLbServiceProvided();
|
||||
if (service == Service.PortForwarding) return nsp.isPortForwardingServiceProvided();
|
||||
if (service == Service.SecurityGroup) return nsp.isSecuritygroupServiceProvided();
|
||||
if (service == Service.SourceNat) return nsp.isSourcenatServiceProvided();
|
||||
if (service == Service.StaticNat) return nsp.isStaticnatServiceProvided();
|
||||
if (service == Service.UserData) return nsp.isUserdataServiceProvided();
|
||||
if (service == Service.Vpn) return nsp.isVpnServiceProvided();
|
||||
if (service == Service.NetworkACL) return nsp.isNetworkAclServiceProvided();
|
||||
// Unknown service: fall back to true so extension-backed providers are not filtered out
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider getDefaultUniqueProviderForService(String serviceName) {
|
||||
List<? extends Provider> providers = listSupportedNetworkServiceProviders(serviceName);
|
||||
|
|
@ -1540,13 +1728,19 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
throw new InvalidParameterValueException("Unable to find the Network Element implementing the Service Provider '" + provider.getName() + "'");
|
||||
}
|
||||
|
||||
// For external network providers, get per-provider capabilities
|
||||
final boolean isExternal = extensionHelper.isNetworkExtensionProvider(provider.getName());
|
||||
Map<Service, Map<Capability, String>> providerCaps = isExternal
|
||||
? getExternalProviderCapabilities(null, provider.getName())
|
||||
: element.getCapabilities();
|
||||
|
||||
Set<Service> enabledServices = new HashSet<Service>();
|
||||
enabledServices.addAll(providersMap.get(provider));
|
||||
|
||||
if (enabledServices != null && !enabledServices.isEmpty()) {
|
||||
if (!element.canEnableIndividualServices()) {
|
||||
if (!element.canEnableIndividualServices() && !isExternal) {
|
||||
Set<Service> requiredServices = new HashSet<Service>();
|
||||
requiredServices.addAll(element.getCapabilities().keySet());
|
||||
requiredServices.addAll(providerCaps.keySet());
|
||||
|
||||
if (requiredServices.contains(Network.Service.Gateway)) {
|
||||
requiredServices.remove(Network.Service.Gateway);
|
||||
|
|
@ -1580,7 +1774,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
List<String> serviceList = new ArrayList<String>();
|
||||
for (Service service : enabledServices) {
|
||||
// check if the service is provided by this Provider
|
||||
if (!element.getCapabilities().containsKey(service)) {
|
||||
if (!providerCaps.containsKey(service)) {
|
||||
throw new UnsupportedServiceException(provider.getName() + " Provider cannot provide service " + service.getName());
|
||||
}
|
||||
serviceList.add(service.getName());
|
||||
|
|
@ -1655,18 +1849,32 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean providerSupportsCapability(Set<Provider> providers, Service service, Capability cap) {
|
||||
for (Provider provider : providers) {
|
||||
NetworkElement element = getElementImplementingProvider(provider.getName());
|
||||
if (element != null) {
|
||||
Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
|
||||
boolean isExtProvider = extensionHelper.isNetworkExtensionProvider(provider.getName());
|
||||
Map<Service, Map<Capability, String>> elementCapabilities = isExtProvider
|
||||
? getExternalProviderCapabilities(null, provider.getName())
|
||||
: element.getCapabilities();
|
||||
if (elementCapabilities == null || !elementCapabilities.containsKey(service)) {
|
||||
if (isExtProvider) {
|
||||
// Extension provider with no declared capabilities for this service —
|
||||
// treat as "service supported but capability not constrained"
|
||||
return false;
|
||||
}
|
||||
throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() +
|
||||
" implementing Provider=" + provider.getName());
|
||||
}
|
||||
Map<Capability, String> serviceCapabilities = elementCapabilities.get(service);
|
||||
if (serviceCapabilities == null || serviceCapabilities.isEmpty()) {
|
||||
if (isExtProvider) {
|
||||
// Extension provider declared the service but without specific capability
|
||||
// constraints — treat as "capability not constrained, not explicitly supported"
|
||||
return false;
|
||||
}
|
||||
throw new UnsupportedServiceException("Service " + service.getName() + " doesn't have capabilites for element=" + element.getName() +
|
||||
" implementing Provider=" + provider.getName());
|
||||
}
|
||||
|
|
@ -1686,19 +1894,36 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
for (Provider provider : providers) {
|
||||
NetworkElement element = getElementImplementingProvider(provider.getName());
|
||||
if (element != null) {
|
||||
Map<Service, Map<Capability, String>> elementCapabilities = element.getCapabilities();
|
||||
boolean isExtProvider = extensionHelper.isNetworkExtensionProvider(provider.getName());
|
||||
Map<Service, Map<Capability, String>> elementCapabilities = isExtProvider
|
||||
? getExternalProviderCapabilities(null, provider.getName())
|
||||
: element.getCapabilities();
|
||||
if (elementCapabilities == null || !elementCapabilities.containsKey(service)) {
|
||||
if (isExtProvider) {
|
||||
// Extension provider with no declared capabilities for this service —
|
||||
// treat as supported without constraints; skip the capability check.
|
||||
continue;
|
||||
}
|
||||
throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() +
|
||||
" implementing Provider=" + provider.getName());
|
||||
}
|
||||
Map<Capability, String> serviceCapabilities = elementCapabilities.get(service);
|
||||
if (serviceCapabilities == null || serviceCapabilities.isEmpty()) {
|
||||
if (isExtProvider) {
|
||||
// Extension provider declared the service without capability constraints —
|
||||
// accept any capability value (the extension handles it at runtime).
|
||||
continue;
|
||||
}
|
||||
throw new UnsupportedServiceException("Service " + service.getName() + " doesn't have capabilities for element=" + element.getName() +
|
||||
" implementing Provider=" + provider.getName());
|
||||
}
|
||||
|
||||
String value = serviceCapabilities.get(cap);
|
||||
if (value == null || value.isEmpty()) {
|
||||
if (isExtProvider) {
|
||||
// Capability not explicitly declared for this extension — accept it.
|
||||
continue;
|
||||
}
|
||||
throw new UnsupportedServiceException("Service " + service.getName() + " doesn't have capability " + cap.getName() + " for element=" +
|
||||
element.getName() + " implementing Provider=" + provider.getName());
|
||||
}
|
||||
|
|
@ -1953,7 +2178,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
List<String> providerNames = _ntwkOfferingSrvcDao.getDistinctProviders(ntkwOffId);
|
||||
List<Provider> providers = new ArrayList<Provider>();
|
||||
for (String providerName : providerNames) {
|
||||
providers.add(Network.Provider.getProvider(providerName));
|
||||
providers.add(resolveProvider(providerName));
|
||||
}
|
||||
|
||||
return providers;
|
||||
|
|
@ -2255,7 +2480,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
Map<String, Provider> providers = new HashMap<String, Provider>();
|
||||
for (String providerName : providerNames) {
|
||||
if (!providers.containsKey(providerName)) {
|
||||
providers.put(providerName, Network.Provider.getProvider(providerName));
|
||||
providers.put(providerName, resolveProvider(providerName));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3087,8 +3087,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
|
||||
protected boolean providersConfiguredForExternalNetworking(Collection<String> providers) {
|
||||
for (String providerStr : providers) {
|
||||
Provider provider = Network.Provider.getProvider(providerStr);
|
||||
if (provider.isExternal()) {
|
||||
Provider provider = _networkModel.resolveProvider(providerStr);
|
||||
if (provider != null && provider.isExternal()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -4449,6 +4449,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
addOrRemoveVnets(listOfRanges, network);
|
||||
}
|
||||
_physicalNetworkDao.update(id, network);
|
||||
|
||||
return network;
|
||||
|
||||
}
|
||||
|
|
@ -5121,7 +5122,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
|
||||
Provider provider = null;
|
||||
if (providerName != null) {
|
||||
provider = Network.Provider.getProvider(providerName);
|
||||
provider = _networkModel.resolveProvider(providerName);
|
||||
if (provider == null) {
|
||||
throw new InvalidParameterValueException("Invalid Network Service Provider=" + providerName);
|
||||
}
|
||||
|
|
@ -5158,7 +5159,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
}
|
||||
|
||||
if (providerName != null) {
|
||||
Provider provider = Network.Provider.getProvider(providerName);
|
||||
Provider provider = _networkModel.resolveProvider(providerName);
|
||||
if (provider == null) {
|
||||
throw new InvalidParameterValueException("Invalid Network Service Provider=" + providerName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1468,7 +1468,7 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
|
|||
}
|
||||
|
||||
// Validate Provider
|
||||
Network.Provider provider = Network.Provider.getProvider(cmd.getProvider());
|
||||
Network.Provider provider = networkModel.resolveProvider(cmd.getProvider());
|
||||
if (provider == null) {
|
||||
throw new InvalidParameterValueException("The Provider " + cmd.getProvider() + " does not exist; Unable to create Counter");
|
||||
}
|
||||
|
|
@ -1537,7 +1537,7 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
|
|||
}
|
||||
String providerStr = cmd.getProvider();
|
||||
if (providerStr != null) {
|
||||
Network.Provider provider = Network.Provider.getProvider(providerStr);
|
||||
Network.Provider provider = networkModel.resolveProvider(providerStr);
|
||||
if (provider == null) {
|
||||
throw new InvalidParameterValueException("The Provider " + providerStr + " does not exist; Unable to list Counter");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,9 +69,11 @@ import com.cloud.network.dao.FirewallRulesDcidrsDao;
|
|||
import com.cloud.network.dao.IPAddressDao;
|
||||
import com.cloud.network.dao.IPAddressVO;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkServiceMapDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.network.element.FirewallServiceProvider;
|
||||
import com.cloud.network.element.NetworkACLServiceProvider;
|
||||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.network.element.PortForwardingServiceProvider;
|
||||
import com.cloud.network.element.StaticNatServiceProvider;
|
||||
import com.cloud.network.rules.FirewallManager;
|
||||
|
|
@ -150,6 +152,9 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
|
|||
EntityManager entityManager;
|
||||
@Inject
|
||||
NsxProviderDao nsxProviderDao;
|
||||
@Inject
|
||||
NetworkServiceMapDao networkServiceMapDao;
|
||||
|
||||
List<FirewallServiceProvider> _firewallElements;
|
||||
|
||||
List<PortForwardingServiceProvider> _pfElements;
|
||||
|
|
@ -617,12 +622,25 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
|
|||
String supportedProtocols;
|
||||
String supportedTrafficTypes = null;
|
||||
if (purpose == FirewallRule.Purpose.Firewall) {
|
||||
supportedTrafficTypes = caps.get(Capability.SupportedTrafficDirection).toLowerCase();
|
||||
String supportedTrafficTypesStr = caps.get(Capability.SupportedTrafficDirection);
|
||||
if (supportedTrafficTypesStr == null) {
|
||||
throw new CloudRuntimeException("Supported traffic direction capability is not defined for Firewall service");
|
||||
}
|
||||
supportedTrafficTypes = supportedTrafficTypesStr.toLowerCase();
|
||||
}
|
||||
|
||||
if (purpose == FirewallRule.Purpose.Firewall && trafficType == FirewallRule.TrafficType.Egress) {
|
||||
// throw an exception if cap is not found
|
||||
String supportedProtocolsStr = caps.get(Capability.SupportedEgressProtocols);
|
||||
if (supportedProtocolsStr == null) {
|
||||
throw new CloudRuntimeException("Supported egress protocols capability is not defined for Firewall service");
|
||||
}
|
||||
supportedProtocols = caps.get(Capability.SupportedEgressProtocols).toLowerCase();
|
||||
} else {
|
||||
String supportedProtocolsStr = caps.get(Capability.SupportedProtocols);
|
||||
if (supportedProtocolsStr == null) {
|
||||
throw new CloudRuntimeException("Supported protocols capability is not defined for " + purpose + " service");
|
||||
}
|
||||
supportedProtocols = caps.get(Capability.SupportedProtocols).toLowerCase();
|
||||
}
|
||||
|
||||
|
|
@ -700,18 +718,38 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
|
|||
if (handled)
|
||||
break;
|
||||
}
|
||||
if (!handled) {
|
||||
// Get provider name and get the element by provider name (it could be an external provider)
|
||||
String fwProviderName = networkServiceMapDao.getProviderForServiceInNetwork(network.getId(), Service.Firewall);
|
||||
if (fwProviderName != null) {
|
||||
NetworkElement element = _networkModel.getElementImplementingProvider(fwProviderName);
|
||||
if (element instanceof FirewallServiceProvider) {
|
||||
handled = ((FirewallServiceProvider) element).applyFWRules(network, rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PortForwarding:
|
||||
for (PortForwardingServiceProvider element : _pfElements) {
|
||||
for (PortForwardingServiceProvider element : _pfElements) {
|
||||
Network.Provider provider = element.getProvider();
|
||||
boolean isPfProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.PortForwarding, provider);
|
||||
if (!isPfProvider) {
|
||||
continue;
|
||||
}
|
||||
handled = element.applyPFRules(network, (List<PortForwardingRule>)rules);
|
||||
handled = element.applyPFRules(network, (List<PortForwardingRule>)rules);
|
||||
if (handled)
|
||||
break;
|
||||
}
|
||||
if (!handled) {
|
||||
// Get provider name and get the element by provider name (it could be an external provider)
|
||||
String pfProviderName = networkServiceMapDao.getProviderForServiceInNetwork(network.getId(), Service.PortForwarding);
|
||||
if (pfProviderName != null) {
|
||||
NetworkElement element = _networkModel.getElementImplementingProvider(pfProviderName);
|
||||
if (element instanceof PortForwardingServiceProvider) {
|
||||
handled = ((PortForwardingServiceProvider) element).applyPFRules(network, (List<PortForwardingRule>) rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* case NetworkACL:
|
||||
for (NetworkACLServiceProvider element: _networkAclElements) {
|
||||
|
|
@ -726,7 +764,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
|
|||
}
|
||||
break;*/
|
||||
default:
|
||||
assert (false) : "Unexpected fall through in applying rules to the network elements";
|
||||
assert (false) : "Unexpected fall through in applying rules to the network elements";
|
||||
logger.error("FirewallManager cannot process rules of type " + purpose);
|
||||
throw new CloudRuntimeException("FirewallManager cannot process rules of type " + purpose);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ import com.cloud.network.dao.NetworkServiceMapDao;
|
|||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.network.dao.SslCertVO;
|
||||
import com.cloud.network.element.LoadBalancingServiceProvider;
|
||||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.network.lb.LoadBalancingRule.LbAutoScalePolicy;
|
||||
import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmGroup;
|
||||
import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmProfile;
|
||||
|
|
@ -2049,6 +2050,16 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
|
|||
if (handled)
|
||||
break;
|
||||
}
|
||||
if (!handled) {
|
||||
// Get provider name and get the element by provider name (it could be an external provider)
|
||||
String lbProviderName = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.Lb);
|
||||
if (lbProviderName != null) {
|
||||
NetworkElement element = _networkModel.getElementImplementingProvider(lbProviderName);
|
||||
if (element instanceof LoadBalancingServiceProvider) {
|
||||
handled = ((LoadBalancingServiceProvider) element).applyLBRules(network, rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@ import com.cloud.network.Network;
|
|||
import com.cloud.network.Network.Service;
|
||||
import com.cloud.network.NetworkModel;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkServiceMapDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.network.element.NetworkACLServiceProvider;
|
||||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.network.element.VpcProvider;
|
||||
import com.cloud.network.vpc.NetworkACLItem.State;
|
||||
import com.cloud.network.vpc.dao.NetworkACLDao;
|
||||
|
|
@ -75,6 +77,8 @@ public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLMana
|
|||
private MessageBus _messageBus;
|
||||
@Inject
|
||||
private ResourceTagDao resourceTagDao;
|
||||
@Inject
|
||||
NetworkServiceMapDao networkServiceMapDao;
|
||||
|
||||
private List<NetworkACLServiceProvider> _networkAclElements;
|
||||
|
||||
|
|
@ -442,12 +446,26 @@ public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLMana
|
|||
logger.debug("Applying NetworkACL for network: {} with Network ACL service provider", network);
|
||||
handled = element.applyNetworkACLs(network, rules);
|
||||
if (handled) {
|
||||
// publish message on message bus, so that network elements implementing distributed routing
|
||||
// capability can act on the event
|
||||
_messageBus.publish(_name, "Network_ACL_Replaced", PublishScope.LOCAL, network);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundProvider) {
|
||||
// Get provider name and get the element by provider name (it could be an external provider)
|
||||
String aclProviderName = networkServiceMapDao.getProviderForServiceInNetwork(network.getId(), Service.NetworkACL);
|
||||
if (aclProviderName != null) {
|
||||
foundProvider = true;
|
||||
NetworkElement element = _networkModel.getElementImplementingProvider(aclProviderName);
|
||||
if (element instanceof NetworkACLServiceProvider) {
|
||||
logger.debug("Applying NetworkACL for network: {} with Network ACL service provider: {}", network, aclProviderName);
|
||||
handled = ((NetworkACLServiceProvider) element).applyNetworkACLs(network, rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handled) {
|
||||
// publish message on message bus, so that network elements implementing distributed routing
|
||||
// capability can act on the event
|
||||
_messageBus.publish(_name, "Network_ACL_Replaced", PublishScope.LOCAL, network);
|
||||
}
|
||||
if (!foundProvider) {
|
||||
logger.debug("Unable to find NetworkACL service provider for network: {}", network);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ import org.apache.cloudstack.api.command.user.vpc.RestartVPCCmd;
|
|||
import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
|
|
@ -329,6 +331,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
Site2SiteVpnConnectionDao site2SiteVpnConnectionDao;
|
||||
@Inject
|
||||
Site2SiteCustomerGatewayDao site2SiteCustomerGatewayDao;
|
||||
@Inject
|
||||
ExtensionHelper extensionHelper;
|
||||
|
||||
private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker"));
|
||||
private List<VpcProvider> vpcElements = null;
|
||||
|
|
@ -701,7 +705,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
final Set<Provider> providers = new HashSet<Provider>();
|
||||
for (final String prvNameStr : serviceEntry.getValue()) {
|
||||
// check if provider is supported
|
||||
final Network.Provider provider = Network.Provider.getProvider(prvNameStr);
|
||||
final Network.Provider provider = _ntwkModel.resolveProvider(prvNameStr);
|
||||
if (provider == null) {
|
||||
throw new InvalidParameterValueException("Invalid service provider: " + prvNameStr);
|
||||
}
|
||||
|
|
@ -1248,12 +1252,18 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
|
||||
for (final VpcOfferingServiceMapVO instance : map) {
|
||||
final Service service = Service.getService(instance.getService());
|
||||
if (service == null) {
|
||||
continue;
|
||||
}
|
||||
Set<Provider> providers;
|
||||
providers = serviceProviderMap.get(service);
|
||||
if (providers == null) {
|
||||
providers = new HashSet<Provider>();
|
||||
}
|
||||
providers.add(Provider.getProvider(instance.getProvider()));
|
||||
final Provider provider = _ntwkModel.resolveProvider(instance.getProvider());
|
||||
if (provider != null) {
|
||||
providers.add(provider);
|
||||
}
|
||||
serviceProviderMap.put(service, providers);
|
||||
}
|
||||
|
||||
|
|
@ -1846,6 +1856,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
if (provider == null) {
|
||||
// Default to VPCVirtualRouter
|
||||
provider = Provider.VPCVirtualRouter.getName();
|
||||
} else {
|
||||
final Provider resolvedProvider = _ntwkModel.resolveProvider(provider);
|
||||
if (resolvedProvider == null) {
|
||||
throw new InvalidParameterValueException("Invalid provider " + provider + " configured for service " + service);
|
||||
}
|
||||
provider = resolvedProvider.getName();
|
||||
}
|
||||
|
||||
if (!_ntwkModel.isProviderEnabledInZone(zoneId, provider)) {
|
||||
|
|
@ -2035,9 +2051,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
if (! userIps.isEmpty()) {
|
||||
try {
|
||||
_ipAddrMgr.updateSourceNatIpAddress(requestedIp, userIps);
|
||||
if (isVpcForProvider(Provider.Nsx, vpc) || isVpcForProvider(Provider.Netris, vpc)) {
|
||||
if (isVpcForProvider(Provider.Nsx, vpc) || isVpcForProvider(Provider.Netris, vpc)
|
||||
|| isVpcForProvider(Provider.NetworkExtension, vpc)) {
|
||||
boolean isForNsx = _vpcOffSvcMapDao.isProviderForVpcOffering(Provider.Nsx, vpc.getVpcOfferingId());
|
||||
String providerName = isForNsx ? Provider.Nsx.getName() : Provider.Netris.getName();
|
||||
boolean isForNetris = _vpcOffSvcMapDao.isProviderForVpcOffering(Provider.Netris, vpc.getVpcOfferingId());
|
||||
String providerName = isForNsx ? Provider.Nsx.getName()
|
||||
: (isForNetris ? Provider.Netris.getName() : Provider.NetworkExtension.getName());
|
||||
VpcProvider providerElement = (VpcProvider) _ntwkModel.getElementImplementingProvider(providerName);
|
||||
if (Objects.nonNull(providerElement)) {
|
||||
providerElement.updateVpcSourceNatIp(vpc, requestedIp);
|
||||
|
|
@ -2511,7 +2530,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
// 1) in current release, only vpc provider is supported by Vpc offering
|
||||
final List<Provider> providers = _ntwkModel.getNtwkOffDistinctProviders(guestNtwkOff.getId());
|
||||
for (final Provider provider : providers) {
|
||||
if (!supportedProviders.contains(provider)) {
|
||||
if (!supportedProviders.contains(provider)
|
||||
&& !extensionHelper.isNetworkExtensionProvider(provider.getName())) {
|
||||
throw new InvalidParameterValueException("Provider of type " + provider.getName() + " is not supported for network offerings that can be used in VPC");
|
||||
}
|
||||
}
|
||||
|
|
@ -2618,17 +2638,32 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
}
|
||||
|
||||
public List<VpcProvider> getVpcElements() {
|
||||
// Static providers (VPCVirtualRouter, JuniperContrailVpcRouter) are initialized once.
|
||||
if (vpcElements == null) {
|
||||
vpcElements = new ArrayList<VpcProvider>();
|
||||
vpcElements.add((VpcProvider) _ntwkModel.getElementImplementingProvider(Provider.VPCVirtualRouter.getName()));
|
||||
vpcElements.add((VpcProvider) _ntwkModel.getElementImplementingProvider(Provider.JuniperContrailVpcRouter.getName()));
|
||||
final NetworkElement vpcVirtualRouter = _ntwkModel.getElementImplementingProvider(Provider.VPCVirtualRouter.getName());
|
||||
if (vpcVirtualRouter instanceof VpcProvider) {
|
||||
vpcElements.add((VpcProvider) vpcVirtualRouter);
|
||||
}
|
||||
|
||||
final NetworkElement contrailVpcRouter = _ntwkModel.getElementImplementingProvider(Provider.JuniperContrailVpcRouter.getName());
|
||||
if (contrailVpcRouter instanceof VpcProvider) {
|
||||
vpcElements.add((VpcProvider) contrailVpcRouter);
|
||||
}
|
||||
}
|
||||
|
||||
if (vpcElements == null) {
|
||||
throw new CloudRuntimeException("Failed to initialize vpc elements");
|
||||
// Extension-backed providers are re-fetched every call so that dynamically
|
||||
// registered extensions are picked up without requiring a server restart.
|
||||
final List<VpcProvider> result = new ArrayList<>(vpcElements);
|
||||
for (final Extension extension : extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) {
|
||||
final String providerName = extension.getName();
|
||||
final NetworkElement element = _ntwkModel.getElementImplementingProvider(providerName);
|
||||
if (element instanceof VpcProvider) {
|
||||
result.add((VpcProvider) element);
|
||||
}
|
||||
}
|
||||
|
||||
return vpcElements;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -3937,7 +3972,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
final Map<String, Provider> providers = new HashMap<String, Provider>();
|
||||
for (final String providerName : providerNames) {
|
||||
if (!providers.containsKey(providerName)) {
|
||||
providers.put(providerName, Network.Provider.getProvider(providerName));
|
||||
providers.put(providerName, _ntwkModel.resolveProvider(providerName));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -400,6 +400,16 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canElementEnableIndividualServicesByName(String providerName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider resolveProvider(String providerName) {
|
||||
return Provider.getProvider(providerName);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.cloud.network.NetworkModel#areServicesSupportedInNetwork(long, com.cloud.network.Network.Service[])
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ package com.cloud.network;
|
|||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
|
@ -54,6 +57,9 @@ import com.cloud.network.dao.NetworkDao;
|
|||
import com.cloud.network.dao.NetworkServiceMapDao;
|
||||
import com.cloud.network.dao.NetworkServiceMapVO;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.network.dao.PhysicalNetworkDao;
|
||||
import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
|
||||
import com.cloud.network.dao.PhysicalNetworkServiceProviderVO;
|
||||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.network.element.VpcVirtualRouterElement;
|
||||
import com.cloud.network.vpc.VpcVO;
|
||||
|
|
@ -66,6 +72,9 @@ import com.cloud.utils.net.Ip;
|
|||
import com.cloud.vm.Nic;
|
||||
import com.cloud.vm.NicProfile;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class NetworkModelImplTest {
|
||||
|
|
@ -80,6 +89,17 @@ public class NetworkModelImplTest {
|
|||
private NetworkDao _networksDao;
|
||||
@Inject
|
||||
private NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
|
||||
@Mock
|
||||
private ExtensionHelper extensionHelper;
|
||||
|
||||
@Mock
|
||||
private NetworkExtensionElement networkExtensionElement;
|
||||
|
||||
@Mock
|
||||
private PhysicalNetworkDao physicalNetworkDao;
|
||||
|
||||
@Mock
|
||||
private PhysicalNetworkServiceProviderDao physicalNetworkServiceProviderDao;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
|
|
@ -96,6 +116,11 @@ public class NetworkModelImplTest {
|
|||
networkModel._networkOfferingDao = networkOfferingDao;
|
||||
networkModel._ntwkSrvcDao = networkServiceMapDao;
|
||||
networkModel._ntwkOfferingSrvcDao = networkOfferingServiceMapDao;
|
||||
ReflectionTestUtils.setField(networkModel, "extensionHelper", extensionHelper);
|
||||
ReflectionTestUtils.setField(networkModel, "networkExtensionElement", networkExtensionElement);
|
||||
ReflectionTestUtils.setField(networkModel, "_physicalNetworkDao", physicalNetworkDao);
|
||||
ReflectionTestUtils.setField(networkModel, "_pNSPDao", physicalNetworkServiceProviderDao);
|
||||
Mockito.lenient().when(extensionHelper.isNetworkExtensionProvider(Mockito.anyString())).thenReturn(false);
|
||||
}
|
||||
|
||||
private void prepareMocks(boolean isIp6, Network network, DataCenter zone, VpcVO vpc,
|
||||
|
|
@ -242,8 +267,8 @@ public class NetworkModelImplTest {
|
|||
networkOfferingVO.setForVpc(true);
|
||||
Network network = new NetworkVO();
|
||||
List<NetworkServiceMapVO> networkServiceMapVOs = new ArrayList<>();
|
||||
networkServiceMapVOs.add(new NetworkServiceMapVO(15L, Network.Service.Firewall, Network.Provider.VPCVirtualRouter));
|
||||
networkServiceMapVOs.add(new NetworkServiceMapVO(15L, Network.Service.SourceNat, Network.Provider.VPCVirtualRouter));
|
||||
networkServiceMapVOs.add(new NetworkServiceMapVO(15L, Network.Service.Firewall.getName(), Network.Provider.VPCVirtualRouter.getName()));
|
||||
networkServiceMapVOs.add(new NetworkServiceMapVO(15L, Network.Service.SourceNat.getName(), Network.Provider.VPCVirtualRouter.getName()));
|
||||
NetworkElement element = new VpcVirtualRouterElement();
|
||||
|
||||
ReflectionTestUtils.setField(networkModel, "networkElements", List.of(element));
|
||||
|
|
@ -273,4 +298,120 @@ public class NetworkModelImplTest {
|
|||
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for getElementImplementingProvider with extension provider
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void getElementImplementingProviderReturnsExtensionElementForExtensionProvider() {
|
||||
String providerName = "my-ext-provider";
|
||||
// Provider is not in the static map, so element would be null
|
||||
ReflectionTestUtils.setField(networkModel, "networkElements", new ArrayList<>());
|
||||
when(extensionHelper.isNetworkExtensionProvider(providerName)).thenReturn(true);
|
||||
NetworkExtensionElement mockElement = mock(NetworkExtensionElement.class);
|
||||
when(networkExtensionElement.withProviderName(providerName)).thenReturn(mockElement);
|
||||
|
||||
NetworkElement result = networkModel.getElementImplementingProvider(providerName);
|
||||
// When the element is a NetworkExtensionElement (which is a NetworkElement), result should not be null
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getElementImplementingProviderReturnsNullForUnknownNonExtensionProvider() {
|
||||
String providerName = "unknown-provider";
|
||||
ReflectionTestUtils.setField(networkModel, "networkElements", new ArrayList<>());
|
||||
when(extensionHelper.isNetworkExtensionProvider(providerName)).thenReturn(false);
|
||||
|
||||
NetworkElement result = networkModel.getElementImplementingProvider(providerName);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for resolveProvider
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void resolveProviderReturnsKnownProvider() {
|
||||
Network.Provider result = networkModel.resolveProvider(Network.Provider.VirtualRouter.getName());
|
||||
assertNotNull(result);
|
||||
assertEquals(Network.Provider.VirtualRouter, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveProviderReturnsTransientProviderForExtensionProvider() {
|
||||
String extensionName = "my-ext-network-provider";
|
||||
when(extensionHelper.isNetworkExtensionProvider(extensionName)).thenReturn(true);
|
||||
|
||||
Network.Provider result = networkModel.resolveProvider(extensionName);
|
||||
assertNotNull(result);
|
||||
assertEquals(extensionName, result.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveProviderReturnsNullForUnknownNonExtensionProvider() {
|
||||
String providerName = "totally-unknown";
|
||||
when(extensionHelper.isNetworkExtensionProvider(providerName)).thenReturn(false);
|
||||
|
||||
Network.Provider result = networkModel.resolveProvider(providerName);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for canElementEnableIndividualServicesByName
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void canElementEnableIndividualServicesByNameReturnsFalseForNullProvider() {
|
||||
assertFalse(networkModel.canElementEnableIndividualServicesByName(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canElementEnableIndividualServicesByNameReturnsFalseForUnknownProvider() {
|
||||
when(extensionHelper.isNetworkExtensionProvider("unknown")).thenReturn(false);
|
||||
assertFalse(networkModel.canElementEnableIndividualServicesByName("unknown"));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for getExternalProviderCapabilities
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void getExternalProviderCapabilitiesCallsExtensionHelper() {
|
||||
Map<Network.Service, Map<Network.Capability, String>> caps = new HashMap<>();
|
||||
when(extensionHelper.getNetworkCapabilitiesForProvider(10L, "my-ext")).thenReturn(caps);
|
||||
|
||||
Map<Network.Service, Map<Network.Capability, String>> result =
|
||||
networkModel.getExternalProviderCapabilities(10L, "my-ext");
|
||||
assertEquals(caps, result);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for isServiceProvidedByNsp (via listSupportedNetworkServiceProviders)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void listSupportedNetworkServiceProvidersIncludesExtensionBackedProviders() {
|
||||
PhysicalNetworkServiceProviderVO nsp = mock(PhysicalNetworkServiceProviderVO.class);
|
||||
when(nsp.getProviderName()).thenReturn("my-ext");
|
||||
when(physicalNetworkServiceProviderDao.listAll()).thenReturn(List.of(nsp));
|
||||
|
||||
Extension extension = mock(Extension.class);
|
||||
when(extension.getName()).thenReturn("my-ext");
|
||||
when(extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)).thenReturn(List.of(extension));
|
||||
|
||||
when(extensionHelper.isNetworkExtensionProvider("my-ext")).thenReturn(true);
|
||||
|
||||
// networkElements is empty so no standard providers found
|
||||
ReflectionTestUtils.setField(networkModel, "networkElements", new ArrayList<>());
|
||||
|
||||
// We call with null service to test the inclusion path (parameter is service name String)
|
||||
List<? extends Network.Provider> result = networkModel.listSupportedNetworkServiceProviders(null);
|
||||
|
||||
boolean found = result.stream().anyMatch(p -> "my-ext".equalsIgnoreCase(p.getName()));
|
||||
assertTrue("Extension-backed provider should be included", found);
|
||||
|
||||
Mockito.verify(physicalNetworkServiceProviderDao, Mockito.times(1)).listAll();
|
||||
Mockito.verify(physicalNetworkServiceProviderDao, Mockito.never()).listBy(Mockito.anyLong());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -423,6 +423,8 @@ public class AutoScaleManagerImplTest {
|
|||
when(conditionDao.findById(any())).thenReturn(conditionMock);
|
||||
when(conditionDao.persist(any(ConditionVO.class))).thenReturn(conditionMock);
|
||||
|
||||
when(networkModel.resolveProvider(counterProvider)).thenReturn(Network.Provider.VirtualRouter);
|
||||
|
||||
when(accountManager.finalizeOwner(nullable(Account.class), nullable(String.class), nullable(Long.class), nullable(Long.class))).thenReturn(account);
|
||||
Mockito.doNothing().when(accountManager).checkAccess(Mockito.any(Account.class), Mockito.isNull(), Mockito.anyBoolean(), Mockito.any());
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ import com.cloud.vm.dao.VMInstanceDao;
|
|||
import com.google.common.collect.Maps;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
|
|
@ -151,6 +153,7 @@ public class ConfigDriveNetworkElementTest {
|
|||
@Mock private CallContext callContextMock;
|
||||
@Mock private DomainVO domainVO;
|
||||
@Mock private NetworkOrchestrationService _networkOrchestrationService;
|
||||
@Mock private ExtensionHelper extensionHelper;
|
||||
|
||||
@Spy @InjectMocks
|
||||
private ConfigDriveNetworkElement _configDrivesNetworkElement = new ConfigDriveNetworkElement();
|
||||
|
|
@ -202,6 +205,8 @@ public class ConfigDriveNetworkElementTest {
|
|||
doReturn(_configDrivesNetworkElement.getProvider().getName()).when(_ntwkSrvcDao).getProviderForServiceInNetwork(NETWORK_ID, Network.Service.UserData);
|
||||
|
||||
_networkModel.setNetworkElements(Arrays.asList(_configDrivesNetworkElement));
|
||||
ReflectionTestUtils.setField(_networkModel, "extensionHelper", extensionHelper);
|
||||
Mockito.lenient().when(extensionHelper.isNetworkExtensionProvider(Mockito.anyString())).thenReturn(false);
|
||||
_networkModel.start();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com.cloud.network.firewall;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.NetworkModel;
|
||||
import com.cloud.network.dao.NetworkServiceMapDao;
|
||||
import com.cloud.network.element.FirewallServiceProvider;
|
||||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.network.element.PortForwardingServiceProvider;
|
||||
import com.cloud.network.rules.FirewallRule;
|
||||
import com.cloud.network.rules.PortForwardingRule;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
public class FirewallManagerImplTest {
|
||||
|
||||
interface MockFwElement extends NetworkElement, FirewallServiceProvider {}
|
||||
interface MockPfElement extends NetworkElement, PortForwardingServiceProvider {}
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private FirewallManagerImpl firewallManager;
|
||||
|
||||
@Mock
|
||||
private NetworkModel _networkModel;
|
||||
|
||||
@Mock
|
||||
private NetworkServiceMapDao networkServiceMapDao;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ReflectionTestUtils.setField(firewallManager, "_firewallElements", Collections.emptyList());
|
||||
ReflectionTestUtils.setField(firewallManager, "_pfElements", Collections.emptyList());
|
||||
ReflectionTestUtils.setField(firewallManager, "_staticNatElements", Collections.emptyList());
|
||||
ReflectionTestUtils.setField(firewallManager, "_networkAclElements", Collections.emptyList());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for applyRules with extension-backed Firewall provider
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void applyRulesFirewallHandledByExtensionProvider() throws ResourceUnavailableException {
|
||||
Network network = mock(Network.class);
|
||||
when(network.getId()).thenReturn(1L);
|
||||
|
||||
FirewallRule rule = mock(FirewallRule.class);
|
||||
List<? extends FirewallRule> rules = List.of(rule);
|
||||
|
||||
// No standard firewall elements handle it
|
||||
String extProviderName = "my-ext-fw-provider";
|
||||
when(networkServiceMapDao.getProviderForServiceInNetwork(1L, Network.Service.Firewall))
|
||||
.thenReturn(extProviderName);
|
||||
|
||||
// The element implementing the provider is both NetworkElement and FirewallServiceProvider
|
||||
MockFwElement element = mock(MockFwElement.class);
|
||||
when(element.applyFWRules(eq(network), any())).thenReturn(true);
|
||||
when(_networkModel.getElementImplementingProvider(extProviderName)).thenReturn(element);
|
||||
|
||||
boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.Firewall, rules);
|
||||
assertTrue(result);
|
||||
verify(element).applyFWRules(eq(network), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyRulesFirewallReturnsFalseWhenNoExtensionProviderFound() throws ResourceUnavailableException {
|
||||
Network network = mock(Network.class);
|
||||
when(network.getId()).thenReturn(2L);
|
||||
|
||||
FirewallRule rule = mock(FirewallRule.class);
|
||||
List<? extends FirewallRule> rules = List.of(rule);
|
||||
|
||||
// No standard provider and no extension provider found
|
||||
when(networkServiceMapDao.getProviderForServiceInNetwork(2L, Network.Service.Firewall))
|
||||
.thenReturn(null);
|
||||
|
||||
boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.Firewall, rules);
|
||||
assertFalse(result);
|
||||
verify(_networkModel, never()).getElementImplementingProvider(any());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for applyRules with extension-backed PortForwarding provider
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void applyRulesPortForwardingHandledByExtensionProvider() throws ResourceUnavailableException {
|
||||
Network network = mock(Network.class);
|
||||
when(network.getId()).thenReturn(3L);
|
||||
|
||||
PortForwardingRule rule = mock(PortForwardingRule.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<PortForwardingRule> rules = List.of(rule);
|
||||
|
||||
String extProviderName = "my-ext-pf-provider";
|
||||
when(networkServiceMapDao.getProviderForServiceInNetwork(3L, Network.Service.PortForwarding))
|
||||
.thenReturn(extProviderName);
|
||||
|
||||
MockPfElement element = mock(MockPfElement.class);
|
||||
when(element.applyPFRules(eq(network), any())).thenReturn(true);
|
||||
when(_networkModel.getElementImplementingProvider(extProviderName)).thenReturn(element);
|
||||
|
||||
boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.PortForwarding, rules);
|
||||
assertTrue(result);
|
||||
verify(element).applyPFRules(eq(network), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyRulesPortForwardingReturnsFalseWhenNoExtensionProviderFound() throws ResourceUnavailableException {
|
||||
Network network = mock(Network.class);
|
||||
when(network.getId()).thenReturn(4L);
|
||||
|
||||
PortForwardingRule rule = mock(PortForwardingRule.class);
|
||||
List<? extends FirewallRule> rules = List.of(rule);
|
||||
|
||||
when(networkServiceMapDao.getProviderForServiceInNetwork(4L, Network.Service.PortForwarding))
|
||||
.thenReturn(null);
|
||||
|
||||
boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.PortForwarding, rules);
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for StaticNat (handled by Firewall elements)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void applyRulesStaticNatReturnsFalseWhenNoProviderFound() throws ResourceUnavailableException {
|
||||
Network network = mock(Network.class);
|
||||
when(network.getId()).thenReturn(5L);
|
||||
|
||||
FirewallRule rule = mock(FirewallRule.class);
|
||||
List<? extends FirewallRule> rules = List.of(rule);
|
||||
|
||||
when(networkServiceMapDao.getProviderForServiceInNetwork(5L, Network.Service.Firewall))
|
||||
.thenReturn(null);
|
||||
|
||||
boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.StaticNat, rules);
|
||||
assertFalse(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -68,7 +68,6 @@ import static org.mockito.ArgumentMatchers.anyList;
|
|||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
|
|
@ -92,6 +91,8 @@ public class NetworkACLManagerTest extends TestCase {
|
|||
@Inject
|
||||
NetworkModel _networkModel;
|
||||
@Inject
|
||||
NetworkServiceMapDao networkServiceMapDao;
|
||||
@Inject
|
||||
List<NetworkACLServiceProvider> _networkAclElements;
|
||||
@Inject
|
||||
VpcService _vpcSvc;
|
||||
|
|
@ -169,8 +170,7 @@ public class NetworkACLManagerTest extends TestCase {
|
|||
final List<NetworkVO> networks = new ArrayList<>();
|
||||
networks.add(network);
|
||||
|
||||
NetworkServiceMapDao ntwkSrvcDao = mock(NetworkServiceMapDao.class);
|
||||
when(ntwkSrvcDao.canProviderSupportServiceInNetwork(anyLong(), eq(Network.Service.NetworkACL), nullable(Network.Provider.class))).thenReturn(true);
|
||||
when(networkServiceMapDao.canProviderSupportServiceInNetwork(anyLong(), eq(Network.Service.NetworkACL), nullable(Network.Provider.class))).thenReturn(true);
|
||||
Mockito.when(_networkDao.listByAclId(anyLong())).thenReturn(networks);
|
||||
Mockito.when(_networkDao.findById(anyLong())).thenReturn(network);
|
||||
Mockito.when(networkOfferingDao.isIpv6Supported(anyLong())).thenReturn(false);
|
||||
|
|
@ -277,6 +277,11 @@ public class NetworkACLManagerTest extends TestCase {
|
|||
return Mockito.mock(NetworkModel.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NetworkServiceMapDao networkServiceMapDao() {
|
||||
return Mockito.mock(NetworkServiceMapDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VpcManager vpcManager() {
|
||||
return Mockito.mock(VpcManager.class);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import com.cloud.network.dao.IPAddressDao;
|
|||
import com.cloud.network.dao.IPAddressVO;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.element.NetworkElement;
|
||||
import com.cloud.network.element.VpcProvider;
|
||||
import com.cloud.network.router.CommandSetupHelper;
|
||||
import com.cloud.network.router.NetworkHelper;
|
||||
import com.cloud.network.router.VirtualRouter;
|
||||
|
|
@ -72,6 +73,8 @@ import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd;
|
|||
import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.extension.Extension;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap;
|
||||
import org.apache.cloudstack.network.RoutedIpv4Manager;
|
||||
|
|
@ -90,6 +93,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
|||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
|
@ -99,6 +103,7 @@ import java.util.UUID;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
|
|
@ -247,14 +252,21 @@ public class VpcManagerImplTest {
|
|||
@Test
|
||||
public void getVpcOffSvcProvidersMapForEmptyServiceTest() {
|
||||
long vpcOffId = 1L;
|
||||
VpcOfferingServiceMapVO svcMap = mock(VpcOfferingServiceMapVO.class);
|
||||
Mockito.when(svcMap.getService()).thenReturn(Service.SourceNat.getName());
|
||||
Mockito.when(svcMap.getProvider()).thenReturn(Provider.VPCVirtualRouter.getName());
|
||||
Mockito.when(networkModel.resolveProvider(Provider.VPCVirtualRouter.getName()))
|
||||
.thenReturn(Provider.VPCVirtualRouter);
|
||||
List<VpcOfferingServiceMapVO> list = new ArrayList<VpcOfferingServiceMapVO>();
|
||||
list.add(mock(VpcOfferingServiceMapVO.class));
|
||||
list.add(svcMap);
|
||||
Mockito.when(manager._vpcOffSvcMapDao.listByVpcOffId(vpcOffId)).thenReturn(list);
|
||||
|
||||
Map<Service, Set<Provider>> map = manager.getVpcOffSvcProvidersMap(vpcOffId);
|
||||
|
||||
assertNotNull(map);
|
||||
assertEquals(map.size(), 1);
|
||||
assertTrue(map.containsKey(Service.SourceNat));
|
||||
assertTrue(map.get(Service.SourceNat).contains(Provider.VPCVirtualRouter));
|
||||
}
|
||||
|
||||
protected Map<String, String> createFakeCapabilityInputMap() {
|
||||
|
|
@ -601,4 +613,49 @@ public class VpcManagerImplTest {
|
|||
Assert.assertTrue(manager.isNetworkOnVpcEnabledConserveMode(network));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for getVpcElements with extension-backed NetworkOrchestrator
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void getVpcElementsIncludesExtensionBackedVpcProvider() {
|
||||
manager.setVpcElements(null);
|
||||
|
||||
Mockito.when(networkModel.getElementImplementingProvider(Provider.VPCVirtualRouter.getName())).thenReturn(null);
|
||||
Mockito.when(networkModel.getElementImplementingProvider(Provider.JuniperContrailVpcRouter.getName())).thenReturn(null);
|
||||
|
||||
Extension ext = mock(Extension.class);
|
||||
Mockito.when(ext.getName()).thenReturn("my-vpc-ext");
|
||||
|
||||
ExtensionHelper extHelper = mock(ExtensionHelper.class);
|
||||
Mockito.when(extHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator))
|
||||
.thenReturn(List.of(ext));
|
||||
manager.extensionHelper = extHelper;
|
||||
|
||||
// The element for the extension also implements VpcProvider
|
||||
VpcProvider vpcProviderElement = mock(VpcProvider.class);
|
||||
Mockito.when(networkModel.getElementImplementingProvider("my-vpc-ext")).thenReturn((NetworkElement) vpcProviderElement);
|
||||
|
||||
List<VpcProvider> result = manager.getVpcElements();
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertTrue(result.contains(vpcProviderElement));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVpcElementsReturnsEmptyListWhenNoStaticNorExtensionProviders() {
|
||||
manager.setVpcElements(null);
|
||||
|
||||
Mockito.when(networkModel.getElementImplementingProvider(Provider.VPCVirtualRouter.getName())).thenReturn(null);
|
||||
Mockito.when(networkModel.getElementImplementingProvider(Provider.JuniperContrailVpcRouter.getName())).thenReturn(null);
|
||||
|
||||
ExtensionHelper extHelper = mock(ExtensionHelper.class);
|
||||
Mockito.when(extHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator))
|
||||
.thenReturn(Collections.emptyList());
|
||||
manager.extensionHelper = extHelper;
|
||||
|
||||
List<VpcProvider> result = manager.getVpcElements();
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -411,6 +411,15 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider resolveProvider(String providerName) {
|
||||
return Provider.getProvider(providerName);
|
||||
}
|
||||
@Override
|
||||
public boolean canElementEnableIndividualServicesByName(String providerName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.cloud.network.NetworkModel#areServicesSupportedInNetwork(long, com.cloud.network.Network.Service[])
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2120,6 +2120,7 @@ CREATE TABLE `cloud`.`physical_network_service_providers` (
|
|||
`user_data_service_provided` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Is UserData service provided',
|
||||
`security_group_service_provided` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Is SG service provided',
|
||||
`networkacl_service_provided` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Is Network ACL service provided',
|
||||
`custom_action_service_provided` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Is Custom Action service provided',
|
||||
`removed` datetime COMMENT 'date removed if not null',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `fk_pnetwork_service_providers__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`id`) ON DELETE CASCADE,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -241,6 +241,7 @@
|
|||
"label.action.unmanage.volume": "Unmanage Volume",
|
||||
"label.action.unmanage.volumes": "Unmanage Volumes",
|
||||
"label.action.unregister.extension.resource": "Unregister extension resource",
|
||||
"label.action.update.extension.resource": "Update extension resource details",
|
||||
"label.action.update.host": "Update Host",
|
||||
"label.action.update.security.groups": "Update security groups",
|
||||
"label.action.update.offering.access": "Update offering access",
|
||||
|
|
@ -1095,6 +1096,7 @@
|
|||
"label.external.details.tooltip": "Details that will be passed to the external provisioner while deploying an instance",
|
||||
"label.externalprovisioner": "External provisioner",
|
||||
"label.external.link": "External link",
|
||||
"label.external.network.provider": "External Network Provider",
|
||||
"label.externalid": "External Id",
|
||||
"label.externalloadbalanceripaddress": "External load balancer IP address.",
|
||||
"label.extra": "Extra arguments",
|
||||
|
|
@ -4176,6 +4178,8 @@
|
|||
"message.validate.min": "Please enter a value greater than or equal to {0}.",
|
||||
"message.action.delete.object.storage": "Please confirm that you want to delete this Object Store",
|
||||
"message.action.unregister.extension.resource": "Please confirm that you want to unregister extension with this resource",
|
||||
"message.action.update.extension.resource": "Update the extension resource registration details",
|
||||
"message.success.update.extension.resource": "Successfully updated extension resource registration",
|
||||
"message.bgp.peers.null": "Please note, if no BGP peers are selected, the VR will connect to <br> (1) dedicated BGP peers the owner can access, if the owner has dedicated BGP peers and account setting use.system.bgp.peers is set to false; <br> (2) all BGP peers the owner can access, otherwise.<br>",
|
||||
"message.bucket.delete": "Please confirm that you want to delete this Bucket",
|
||||
"migrate.from": "Migrate from",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default {
|
|||
return fields
|
||||
},
|
||||
details: ['name', 'description', 'id', 'type', 'details', 'path', 'pathready', 'isuserdefined', 'orchestratorrequirespreparevm', 'reservedresourcedetails', 'created'],
|
||||
filters: ['orchestrator'],
|
||||
filters: ['orchestrator', 'networkorchestrator'],
|
||||
tabs: [{
|
||||
name: 'details',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue')))
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ export default {
|
|||
icon: 'edit-outlined',
|
||||
label: 'label.update.physical.network',
|
||||
dataView: true,
|
||||
args: ['vlan', 'tags']
|
||||
args: ['vlan', 'tags', 'externaldetails']
|
||||
},
|
||||
{
|
||||
api: 'addTrafficType',
|
||||
|
|
|
|||
|
|
@ -118,6 +118,14 @@ export default {
|
|||
name: 'network.permissions',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/network/NetworkPermissions.vue'))),
|
||||
show: (record, route, user) => { return 'listNetworkPermissions' in store.getters.apis && record.acltype === 'Account' && !('vpcid' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account) && !record.projectid }
|
||||
}, {
|
||||
name: 'custom.actions',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/extension/RunCustomAction.vue'))),
|
||||
show: (record) => {
|
||||
return 'runCustomAction' in store.getters.apis &&
|
||||
'listCustomActions' in store.getters.apis &&
|
||||
record.service && record.service.some(s => s.name === 'CustomAction')
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
|
|
@ -207,6 +215,19 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
api: 'runCustomAction',
|
||||
icon: 'thunderbolt-outlined',
|
||||
label: 'label.run.custom.action',
|
||||
dataView: true,
|
||||
show: (record) => {
|
||||
return 'runCustomAction' in store.getters.apis &&
|
||||
'listCustomActions' in store.getters.apis &&
|
||||
record.service && record.service.some(s => s.name === 'CustomAction')
|
||||
},
|
||||
popup: true,
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/extension/RunCustomAction.vue')))
|
||||
},
|
||||
{
|
||||
api: 'deleteNetwork',
|
||||
icon: 'delete-outlined',
|
||||
|
|
|
|||
|
|
@ -34,8 +34,25 @@
|
|||
api="listExtensions"
|
||||
:apiParams="extensionsApiParams"
|
||||
resourceType="extension"
|
||||
@change-option="updateResourceTypeByExtension"
|
||||
defaultIcon="appstore-add-outlined" />
|
||||
</a-form-item>
|
||||
<a-form-item name="resourcetype" ref="resourcetype">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.resourcetype')" :tooltip="apiParams.resourcetype.description"/>
|
||||
</template>
|
||||
<a-select
|
||||
v-model:value="form.resourcetype"
|
||||
:placeholder="apiParams.resourcetype.description"
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}" >
|
||||
<a-select-option v-for="opt in resourceTypeOptions" :key="opt" :label="opt">
|
||||
{{ opt }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="name" ref="name">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.name')" :tooltip="apiParams.name.description"/>
|
||||
|
|
@ -152,6 +169,7 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
roleTypes: [],
|
||||
resourceTypeOptions: ['VirtualMachine', 'Network', 'Vpc'],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
|
@ -165,6 +183,7 @@ export default {
|
|||
this.initForm()
|
||||
if (this.extension) {
|
||||
this.form.extensionid = this.extension.id
|
||||
this.updateResourceTypeByExtension(this.extension)
|
||||
}
|
||||
this.roleTypes = this.$fetchCustomActionRoleTypes()
|
||||
},
|
||||
|
|
@ -173,13 +192,30 @@ export default {
|
|||
this.formRef = ref()
|
||||
this.form = reactive({
|
||||
enabled: true,
|
||||
timeout: 5
|
||||
timeout: 5,
|
||||
resourcetype: 'VirtualMachine'
|
||||
})
|
||||
this.rules = reactive({
|
||||
extensionid: [{ required: true, message: `${this.$t('message.error.select')}` }],
|
||||
name: [{ required: true, message: `${this.$t('message.error.name')}` }]
|
||||
name: [{ required: true, message: `${this.$t('message.error.name')}` }],
|
||||
resourcetype: [{ required: true, message: `${this.$t('message.error.select')}` }]
|
||||
})
|
||||
},
|
||||
updateResourceTypeByExtension (selectedExtension) {
|
||||
const type = selectedExtension?.type
|
||||
if (type === 'NetworkOrchestrator') {
|
||||
this.resourceTypeOptions = ['Network', 'Vpc']
|
||||
this.form.resourcetype = 'Network'
|
||||
} else if (type === 'Orchestrator') {
|
||||
this.resourceTypeOptions = ['VirtualMachine']
|
||||
this.form.resourcetype = 'VirtualMachine'
|
||||
} else {
|
||||
this.resourceTypeOptions = ['VirtualMachine', 'Network', 'Vpc']
|
||||
if (!this.form.resourcetype) {
|
||||
this.form.resourcetype = 'VirtualMachine'
|
||||
}
|
||||
}
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
if (this.loading) return
|
||||
|
|
@ -189,6 +225,7 @@ export default {
|
|||
const params = {
|
||||
extensionid: values.extensionid || this.extension.id,
|
||||
name: values.name,
|
||||
resourcetype: values.resourcetype,
|
||||
enabled: values.enabled
|
||||
}
|
||||
const keys = ['description', 'allowedroletypes', 'successmessage', 'errormessage', 'timeout']
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ export default {
|
|||
},
|
||||
fetchExtensionTypes () {
|
||||
this.extensionTypes = []
|
||||
const extensionTypesList = ['Orchestrator']
|
||||
const extensionTypesList = ['Orchestrator', 'NetworkOrchestrator']
|
||||
extensionTypesList.forEach((item) => {
|
||||
this.extensionTypes.push({
|
||||
id: item,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,14 @@
|
|||
{{ text && $toLocaleDate(text) }}
|
||||
</template>
|
||||
<template v-if="column.key === 'actions'">
|
||||
<span style="margin-right: 5px">
|
||||
<tooltip-button
|
||||
v-if="'updateRegisteredExtension' in $store.getters.apis"
|
||||
:tooltip="$t('label.action.update.extension.resource')"
|
||||
type="default"
|
||||
icon="edit-outlined"
|
||||
@onClick="openUpdateModal(record)" />
|
||||
</span>
|
||||
<span style="margin-right: 5px">
|
||||
<a-popconfirm
|
||||
v-if="'unregisterExtension' in $store.getters.apis"
|
||||
|
|
@ -59,6 +67,20 @@
|
|||
:data-map="record.details" />
|
||||
</template>
|
||||
</a-table>
|
||||
<a-modal
|
||||
v-if="updateModalVisible"
|
||||
:visible="updateModalVisible"
|
||||
:width="600"
|
||||
:title="$t('label.action.update.extension.resource')"
|
||||
:closable="true"
|
||||
:footer="null"
|
||||
@cancel="closeUpdateModal">
|
||||
<update-registered-extension
|
||||
:resource="resource"
|
||||
:extension-resource="selectedResource"
|
||||
@refresh-data="handleRefreshData"
|
||||
@close-action="closeUpdateModal" />
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -67,13 +89,16 @@ import { postAPI } from '@/api'
|
|||
import eventBus from '@/config/eventBus'
|
||||
import ObjectListTable from '@/components/view/ObjectListTable.vue'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import UpdateRegisteredExtension from '@/views/extension/UpdateRegisteredExtension'
|
||||
|
||||
export default {
|
||||
name: 'ExtensionResourcesTab',
|
||||
components: {
|
||||
ObjectListTable,
|
||||
TooltipButton
|
||||
TooltipButton,
|
||||
UpdateRegisteredExtension
|
||||
},
|
||||
inject: ['parentFetchData'],
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
|
|
@ -103,7 +128,9 @@ export default {
|
|||
title: this.$t('label.actions')
|
||||
}
|
||||
],
|
||||
unregisterLoading: false
|
||||
unregisterLoading: false,
|
||||
updateModalVisible: false,
|
||||
selectedResource: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -112,13 +139,27 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
openUpdateModal (record) {
|
||||
this.selectedResource = record
|
||||
this.updateModalVisible = true
|
||||
},
|
||||
closeUpdateModal () {
|
||||
this.updateModalVisible = false
|
||||
this.selectedResource = null
|
||||
},
|
||||
handleRefreshData () {
|
||||
if (this.parentFetchData) {
|
||||
this.parentFetchData()
|
||||
}
|
||||
},
|
||||
unregisterExtension (record) {
|
||||
const params = {
|
||||
extensionid: this.resource.id,
|
||||
resourceid: record.id,
|
||||
resourcetype: record.type
|
||||
}
|
||||
postAPI('unregisterExtension', params).then(json => {
|
||||
this.unregisterLoading = true
|
||||
postAPI('unregisterExtension', params).then(() => {
|
||||
eventBus.emit('async-job-complete', null)
|
||||
this.$notification.success({
|
||||
message: this.$t('label.unregister.extension'),
|
||||
|
|
@ -127,7 +168,7 @@ export default {
|
|||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.deleteLoading = false
|
||||
this.unregisterLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export default {
|
|||
},
|
||||
fetchExtensionResourceTypes () {
|
||||
this.resourceTypes = []
|
||||
const resourceTypesList = ['Cluster']
|
||||
const resourceTypesList = ['Cluster', 'PhysicalNetwork']
|
||||
resourceTypesList.forEach((item) => {
|
||||
this.resourceTypes.push({
|
||||
id: item,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div class="form-layout" v-ctrl-enter="handleSubmit">
|
||||
<a-form
|
||||
:ref="formRef"
|
||||
:model="form"
|
||||
:loading="loading"
|
||||
layout="vertical"
|
||||
@finish="handleSubmit">
|
||||
<a-form-item name="details" ref="details">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.details')" :tooltip="$t('message.add.extension.resource.details')"/>
|
||||
</template>
|
||||
<div style="margin-bottom: 10px">{{ $t('message.add.extension.resource.details') }}</div>
|
||||
<details-input
|
||||
v-model:value="form.details" />
|
||||
</a-form-item>
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
||||
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, toRaw } from 'vue'
|
||||
import { postAPI } from '@/api'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
import DetailsInput from '@/components/widgets/DetailsInput'
|
||||
|
||||
export default {
|
||||
name: 'UpdateRegisteredExtension',
|
||||
components: {
|
||||
TooltipLabel,
|
||||
DetailsInput
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
extensionResource: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.initForm()
|
||||
},
|
||||
methods: {
|
||||
initForm () {
|
||||
this.formRef = ref()
|
||||
this.form = reactive({
|
||||
details: this.extensionResource.details ? { ...this.extensionResource.details } : {}
|
||||
})
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
if (this.loading) return
|
||||
this.formRef.value.validate().then(() => {
|
||||
const values = toRaw(this.form)
|
||||
this.loading = true
|
||||
const params = {
|
||||
extensionid: this.resource.id,
|
||||
resourceid: this.extensionResource.id,
|
||||
resourcetype: this.extensionResource.type
|
||||
}
|
||||
if (values.details && Object.keys(values.details).length > 0) {
|
||||
Object.entries(values.details).forEach(([key, value]) => {
|
||||
params['details[0].' + key] = value
|
||||
})
|
||||
} else {
|
||||
params.cleanupdetails = true
|
||||
}
|
||||
postAPI('updateRegisteredExtension', params).then(() => {
|
||||
this.$emit('refresh-data')
|
||||
this.$notification.success({
|
||||
message: this.$t('label.action.update.extension.resource'),
|
||||
description: this.$t('message.success.update.extension.resource')
|
||||
})
|
||||
this.closeAction()
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: this.$t('message.request.failed'),
|
||||
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
|
||||
duration: 0
|
||||
})
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
})
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form-layout {
|
||||
width: 80vw;
|
||||
@media (min-width: 600px) {
|
||||
width: 550px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -706,6 +706,7 @@ export default {
|
|||
}
|
||||
this.loading = true
|
||||
getAPI('listExtensions', {
|
||||
type: 'Orchestrator'
|
||||
}).then(response => {
|
||||
this.extensionsList = response.listextensionsresponse.extension || []
|
||||
}).catch(error => {
|
||||
|
|
|
|||
|
|
@ -302,6 +302,7 @@ export default {
|
|||
fetchExtensionsList () {
|
||||
this.loading = true
|
||||
getAPI('listExtensions', {
|
||||
type: 'Orchestrator'
|
||||
}).then(response => {
|
||||
this.extensionsList = response.listextensionsresponse.extension || []
|
||||
}).catch(error => {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
:tabPosition="device === 'mobile' ? 'top' : 'left'"
|
||||
:animated="false"
|
||||
@change="onTabChange">
|
||||
<!-- Hardcoded NSP tabs -->
|
||||
<a-tab-pane
|
||||
class="custom-tab-pane"
|
||||
v-for="item in hardcodedNsps"
|
||||
|
|
@ -41,8 +42,29 @@
|
|||
:zoneId="resource.zoneid"
|
||||
:tabKey="tabKey"/>
|
||||
</a-tab-pane>
|
||||
<!-- Dynamic extension-based provider tabs (one per registered NetworkOrchestrator extension) -->
|
||||
<a-tab-pane
|
||||
class="custom-tab-pane"
|
||||
v-for="ext in registeredExtensions"
|
||||
:key="ext.name">
|
||||
<template #tab>
|
||||
<span>
|
||||
{{ ext.name }}
|
||||
<status :text="nsps[ext.name] ? nsps[ext.name].state : $t('label.not.added')" style="margin-bottom: 6px; margin-left: 6px" />
|
||||
</span>
|
||||
</template>
|
||||
<provider-item
|
||||
v-if="tabKey === ext.name"
|
||||
:loading="loading"
|
||||
:itemNsp="extensionNspItem(ext.name)"
|
||||
:nsp="nsps[ext.name]"
|
||||
:resourceId="resource.id"
|
||||
:zoneId="resource.zoneid"
|
||||
:tabKey="tabKey"/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
|
||||
<div v-if="showFormAction">
|
||||
<keep-alive v-if="currentAction.component">
|
||||
<a-modal
|
||||
|
|
@ -170,7 +192,8 @@ export default {
|
|||
actionLoading: false,
|
||||
showFormAction: false,
|
||||
currentAction: {},
|
||||
tabKey: 'BaremetalDhcpProvider'
|
||||
tabKey: 'BaremetalDhcpProvider',
|
||||
registeredExtensions: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -848,7 +871,7 @@ export default {
|
|||
listView: true,
|
||||
label: 'label.enable.provider',
|
||||
confirm: 'message.confirm.enable.provider',
|
||||
show: (record) => { return (record && record.id && record.state === 'Disabled') },
|
||||
show: (record) => { return record && record.id && record.state === 'Disabled' },
|
||||
mapping: {
|
||||
state: {
|
||||
value: (record) => { return 'Enabled' }
|
||||
|
|
@ -1148,6 +1171,51 @@ export default {
|
|||
return
|
||||
}
|
||||
this.fetchServiceProvider()
|
||||
this.fetchRegisteredExtensions()
|
||||
},
|
||||
fetchRegisteredExtensions () {
|
||||
// Load NetworkOrchestrator extensions registered to this physical network
|
||||
getAPI('listExtensions', {
|
||||
type: 'NetworkOrchestrator',
|
||||
resourceid: this.resource.id,
|
||||
resourcetype: 'PhysicalNetwork'
|
||||
}).then(json => {
|
||||
this.registeredExtensions = (json.listextensionsresponse && json.listextensionsresponse.extension) || []
|
||||
}).catch(() => {
|
||||
this.registeredExtensions = []
|
||||
})
|
||||
},
|
||||
extensionNspItem (extName) {
|
||||
// Build a ProviderItem-compatible itemNsp descriptor for extension-backed NSPs.
|
||||
// Mirrors the structure of hardcoded entries in hardcodedNsps.
|
||||
return {
|
||||
title: extName,
|
||||
details: ['name', 'state', 'id', 'physicalnetworkid', 'servicelist'],
|
||||
actions: [
|
||||
{
|
||||
api: 'updateNetworkServiceProvider',
|
||||
icon: 'play-circle-outlined',
|
||||
listView: true,
|
||||
label: 'label.enable.provider',
|
||||
confirm: 'message.confirm.enable.provider',
|
||||
show: (record) => record && record.id && record.state === 'Disabled',
|
||||
mapping: {
|
||||
state: { value: () => 'Enabled' }
|
||||
}
|
||||
},
|
||||
{
|
||||
api: 'updateNetworkServiceProvider',
|
||||
icon: 'stop-outlined',
|
||||
listView: true,
|
||||
label: 'label.disable.provider',
|
||||
confirm: 'message.confirm.disable.provider',
|
||||
show: (record) => record && record.id && record.state === 'Enabled',
|
||||
mapping: {
|
||||
state: { value: () => 'Disabled' }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
fetchServiceProvider (name) {
|
||||
this.fetchLoading = true
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
:loading="loading"
|
||||
:columns="listCols"
|
||||
:dataSource="dataSource"
|
||||
:rowKey="record => record.id || record.name || record.nvpdeviceid || record.resourceid"
|
||||
:rowKey="record => record.id || record.name || record.nvpdeviceid || record.resourceid || record.physicalnetworkid"
|
||||
:pagination="false"
|
||||
:scroll="scrollable">
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
|
|
@ -67,6 +67,7 @@
|
|||
<span v-else-if="resource.name==='CiscoVnmc' && title==='listCiscoAsa1000vResources'">
|
||||
{{ $t('label.delete.ciscoasa1000v') }}
|
||||
</span>
|
||||
<!-- External network device UI removed: use extension registration details instead -->
|
||||
</template>
|
||||
<tooltip-button
|
||||
v-if="resource.name==='Ovs'"
|
||||
|
|
@ -86,6 +87,12 @@
|
|||
@onClick="onDelete(record)"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-if="column.key === 'details'">
|
||||
<span v-if="text && typeof text === 'object'">
|
||||
<a-tag v-for="(val, key) in text" :key="key" style="margin-bottom: 2px;">{{ key }}: {{ val }}</a-tag>
|
||||
</span>
|
||||
<span v-else>{{ text }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'lbdevicestate'">
|
||||
<status :text="text ? text : ''" displayText />
|
||||
</template>
|
||||
|
|
@ -269,6 +276,7 @@ export default {
|
|||
name = record.hostname
|
||||
params.resourceid = record.resourceid
|
||||
break
|
||||
// ExternalNetwork provider action removed; use extension registration/unregister instead
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -405,6 +405,9 @@
|
|||
<a-tab-pane :tab="$t('label.vnf.appliances')" key="vnf" v-if="'deployVnfAppliance' in $store.getters.apis">
|
||||
<VnfAppliancesTab :resource="resource" :loading="loading" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('label.custom.actions')" key="customactions" v-if="'runCustomAction' in $store.getters.apis && 'listCustomActions' in $store.getters.apis && resource.service && resource.service.some(s => s.name === 'CustomAction')">
|
||||
<RunCustomAction :resource="resource" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('label.events')" key="events" v-if="'listEvents' in $store.getters.apis">
|
||||
<events-tab :resource="resource" resourceType="Vpc" :loading="loading" />
|
||||
</a-tab-pane>
|
||||
|
|
@ -433,6 +436,7 @@ import AnnotationsTab from '@/components/view/AnnotationsTab'
|
|||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import BgpPeersTab from '@/views/infra/zone/BgpPeersTab.vue'
|
||||
import StaticRoutesTab from './StaticRoutesTab'
|
||||
import RunCustomAction from '@/views/extension/RunCustomAction'
|
||||
|
||||
export default {
|
||||
name: 'VpcTab',
|
||||
|
|
@ -445,6 +449,7 @@ export default {
|
|||
VpcTiersTab,
|
||||
VnfAppliancesTab,
|
||||
StaticRoutesTab,
|
||||
RunCustomAction,
|
||||
EventsTab,
|
||||
AnnotationsTab,
|
||||
ResourceIcon
|
||||
|
|
|
|||
|
|
@ -739,6 +739,13 @@ export default {
|
|||
isSupportedServiceObject (obj) {
|
||||
return (obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object && 'provider' in obj)
|
||||
},
|
||||
isVpcCoreProvider (providerName) {
|
||||
return ['VpcVirtualRouter', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive'].includes(providerName)
|
||||
},
|
||||
isDynamicExtensionProvider (providerName) {
|
||||
const knownProviders = ['VirtualRouter', 'VpcVirtualRouter', 'InternalLbVm', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive', 'Nsx', 'Netris']
|
||||
return !knownProviders.includes(providerName)
|
||||
},
|
||||
fetchDomainData () {
|
||||
const params = {}
|
||||
params.listAll = true
|
||||
|
|
@ -917,11 +924,13 @@ export default {
|
|||
var providers = svc.provider
|
||||
providers.forEach(function (provider, providerIndex) {
|
||||
if (self.forVpc) { // *** vpc ***
|
||||
var enabledProviders = ['VpcVirtualRouter', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive']
|
||||
if (self.lbType === 'internalLb') {
|
||||
enabledProviders.push('InternalLbVm')
|
||||
// Keep the known VPC-safe providers allowlisted and only additionally
|
||||
// enable dynamically discovered extension providers.
|
||||
if (provider.name === 'InternalLbVm') {
|
||||
provider.enabled = self.lbType === 'internalLb' && svc.name === 'Lb'
|
||||
} else {
|
||||
provider.enabled = self.isVpcCoreProvider(provider.name) || self.isDynamicExtensionProvider(provider.name)
|
||||
}
|
||||
provider.enabled = enabledProviders.includes(provider.name)
|
||||
} else { // *** non-vpc ***
|
||||
provider.enabled = !['InternalLbVm', 'VpcVirtualRouter', 'Nsx', 'Netris'].includes(provider.name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@
|
|||
:checkBoxLabel="item.description"
|
||||
:forExternalNetProvider="form.provider === 'NSX' || form.provider === 'Netris'"
|
||||
:defaultCheckBoxValue="form.provider === 'NSX' || form.provider === 'Netris'"
|
||||
:selectOptions="item.provider"
|
||||
:selectOptions="item.provider || []"
|
||||
@handle-checkselectpair-change="handleSupportedServiceChange"/>
|
||||
</a-list-item>
|
||||
</template>
|
||||
|
|
@ -431,6 +431,13 @@ export default {
|
|||
this.zoneLoading = false
|
||||
})
|
||||
},
|
||||
isVpcCoreProvider (providerName) {
|
||||
return ['VpcVirtualRouter', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive'].includes(providerName)
|
||||
},
|
||||
isDynamicExtensionProvider (providerName) {
|
||||
const knownProviders = ['VirtualRouter', 'VpcVirtualRouter', 'InternalLbVm', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive', 'Nsx', 'Netris']
|
||||
return !knownProviders.includes(providerName)
|
||||
},
|
||||
fetchSupportedServiceData () {
|
||||
var services = []
|
||||
if (this.provider === 'NSX') {
|
||||
|
|
@ -520,82 +527,52 @@ export default {
|
|||
provider: [{ name: 'VpcVirtualRouter' }]
|
||||
})
|
||||
} else {
|
||||
services.push({
|
||||
name: 'Dhcp',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'ConfigDrive' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'Dns',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'ConfigDrive' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'Lb',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'InternalLbVm' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'Gateway',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'BigSwitchBcf' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'StaticNat',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'BigSwitchBcf' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'SourceNat',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'BigSwitchBcf' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'NetworkACL',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'BigSwitchBcf' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'PortForwarding',
|
||||
provider: [{ name: 'VpcVirtualRouter' }]
|
||||
})
|
||||
services.push({
|
||||
name: 'UserData',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'ConfigDrive' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'Vpn',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'BigSwitchBcf' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'Connectivity',
|
||||
provider: [
|
||||
{ name: 'BigSwitchBcf' },
|
||||
{ name: 'NiciraNvp' },
|
||||
{ name: 'Ovs' },
|
||||
{ name: 'JuniperContrailVpcRouter' }
|
||||
]
|
||||
this.supportedServices = []
|
||||
this.supportedServiceLoading = true
|
||||
getAPI('listSupportedNetworkServices').then(json => {
|
||||
const vpcServices = ['Dhcp', 'Dns', 'Lb', 'Gateway', 'StaticNat', 'SourceNat', 'NetworkACL', 'PortForwarding', 'UserData', 'Vpn', 'Connectivity', 'CustomAction']
|
||||
services = (json?.listsupportednetworkservicesresponse?.networkservice || [])
|
||||
.filter(service => vpcServices.includes(service.name))
|
||||
.map(service => {
|
||||
const providerMap = {}
|
||||
const providers = [...(service.provider || []), ...(service.name === 'Lb' ? [{ name: 'InternalLbVm' }] : [])]
|
||||
.map(provider => {
|
||||
const providerName = provider.name === 'VirtualRouter' ? 'VpcVirtualRouter' : provider.name
|
||||
const enabled = providerName === 'InternalLbVm'
|
||||
? service.name === 'Lb'
|
||||
: this.isVpcCoreProvider(providerName) || this.isDynamicExtensionProvider(providerName)
|
||||
return {
|
||||
name: providerName,
|
||||
description: providerName,
|
||||
enabled
|
||||
}
|
||||
})
|
||||
.filter(provider => {
|
||||
if (providerMap[provider.name]) {
|
||||
return false
|
||||
}
|
||||
providerMap[provider.name] = true
|
||||
return true
|
||||
})
|
||||
return {
|
||||
...service,
|
||||
description: service.name,
|
||||
provider: providers
|
||||
}
|
||||
})
|
||||
|
||||
this.supportedServices = []
|
||||
if (this.networkmode === 'ROUTED') {
|
||||
services = services.filter(service => !['SourceNat', 'StaticNat', 'Lb', 'PortForwarding', 'Vpn'].includes(service.name))
|
||||
}
|
||||
this.supportedServices = services
|
||||
}).catch(error => {
|
||||
this.supportedServices = []
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.supportedServiceLoading = false
|
||||
})
|
||||
return
|
||||
}
|
||||
this.supportedServices = []
|
||||
if (this.networkmode === 'ROUTED') {
|
||||
|
|
@ -647,7 +624,7 @@ export default {
|
|||
if (service === 'SourceNat') {
|
||||
this.sourceNatServiceChecked = checked
|
||||
}
|
||||
if (checked && provider != null & provider !== undefined) {
|
||||
if (checked && provider != null && provider !== undefined) {
|
||||
this.selectedServiceProviderMap[service] = provider
|
||||
} else {
|
||||
delete this.selectedServiceProviderMap[service]
|
||||
|
|
|
|||
Loading…
Reference in New Issue