From 37d634406de2e73c4bd458f0891bd55208c3958c Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Wed, 6 Sep 2023 15:08:06 -0400 Subject: [PATCH] add create/delete segment and UI integration --- .../main/java/com/cloud/network/Network.java | 2 + .../java/com/cloud/network/dao/NetworkVO.java | 4 + .../agent/api/CreateNsxSegmentCommand.java | 35 ++++++ .../agent/api/DeleteNsxSegmentCommand.java | 9 ++ .../cloudstack/resource/NsxResource.java | 105 +++++++++++++++--- .../apache/cloudstack/service/NsxElement.java | 38 ++++++- .../service/NsxGuestNetworkGuru.java | 103 ++++++++++++++++- .../cloudstack/service/NsxServiceImpl.java | 8 ++ .../cloudstack/utils/NsxApiClientUtils.java | 15 +++ ui/public/locales/en.json | 9 ++ .../views/infra/zone/ZoneWizardLaunchZone.vue | 39 +++++++ .../infra/zone/ZoneWizardNetworkSetupStep.vue | 77 +++++++++++++ .../ZoneWizardPhysicalNetworkSetupStep.vue | 2 +- 13 files changed, 422 insertions(+), 24 deletions(-) create mode 100644 plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxSegmentCommand.java create mode 100644 plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxSegmentCommand.java diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index be0753e3e91..3e9e5e318a3 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -430,6 +430,8 @@ public interface Network extends ControlledEntity, StateObject, I long getDataCenterId(); + long getAccountId(); + long getNetworkOfferingId(); @Override diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java index f24eec4931e..531ac7002be 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java @@ -369,6 +369,10 @@ public class NetworkVO implements Network { return mode; } + public void setAccountId(long accountId) { + this.accountId = accountId; + } + @Override public long getAccountId() { return accountId; diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxSegmentCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxSegmentCommand.java new file mode 100644 index 00000000000..1231765d37d --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxSegmentCommand.java @@ -0,0 +1,35 @@ +// 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.agent.api; + +import com.cloud.network.dao.NetworkVO; + +public class CreateNsxSegmentCommand extends CreateNsxTier1GatewayCommand { + private NetworkVO tierNetwork; + public CreateNsxSegmentCommand(String zoneName, Long zoneId, String accountName, Long accountId, String vpcName, NetworkVO tierNetwork) { + super(zoneName, zoneId, accountName, accountId, vpcName); + this.tierNetwork = tierNetwork; + } + + public NetworkVO getTierNetwork() { + return tierNetwork; + } + + public void setTierNetwork(NetworkVO tierNetwork) { + this.tierNetwork = tierNetwork; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxSegmentCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxSegmentCommand.java new file mode 100644 index 00000000000..03df11101a6 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxSegmentCommand.java @@ -0,0 +1,9 @@ +package org.apache.cloudstack.agent.api; + +import com.cloud.network.dao.NetworkVO; + +public class DeleteNsxSegmentCommand extends CreateNsxSegmentCommand { + public DeleteNsxSegmentCommand(String accountName, NetworkVO network) { + super(null, network.getDataCenterId(), accountName, network.getAccountId(), null, network); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java index cc3bdfd563a..f15ae3cb4c6 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java @@ -16,47 +16,60 @@ // under the License. package org.apache.cloudstack.resource; -import static org.apache.cloudstack.utils.NsxApiClientUtils.PoolAllocation.ROUTING; -import static org.apache.cloudstack.utils.NsxApiClientUtils.HAMode.ACTIVE_STANDBY; -import static org.apache.cloudstack.utils.NsxApiClientUtils.createApiClient; - import com.cloud.agent.IAgentControl; -import com.cloud.agent.api.StartupCommand; -import com.cloud.agent.api.Command; -import com.cloud.agent.api.ReadyCommand; -import com.cloud.agent.api.ReadyAnswer; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; import com.cloud.exception.InvalidParameterValueException; import com.cloud.host.Host; import com.cloud.resource.ServerResource; import com.cloud.utils.exception.CloudRuntimeException; +import com.vmware.nsx.EdgeClusters; +import com.vmware.nsx.model.EdgeCluster; +import com.vmware.nsx_policy.infra.Segments; import com.vmware.nsx_policy.infra.Tier1s; +import com.vmware.nsx_policy.infra.segments.ServiceSegments; import com.vmware.nsx_policy.infra.tier_0s.LocaleServices; import com.vmware.nsx_policy.model.ApiError; import com.vmware.nsx_policy.model.ChildLocaleServices; import com.vmware.nsx_policy.model.LocaleServicesListResult; +import com.vmware.nsx_policy.model.Segment; +import com.vmware.nsx_policy.model.SegmentSubnet; +import com.vmware.nsx_policy.model.ServiceSegmentListResult; import com.vmware.nsx_policy.model.Tier1; import com.vmware.vapi.bindings.Service; import com.vmware.vapi.std.errors.Error; import org.apache.cloudstack.NsxAnswer; import org.apache.cloudstack.StartupNsxCommand; +import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand; import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand; import org.apache.cloudstack.service.NsxApi; +import org.apache.cloudstack.utils.NsxApiClientUtils; import org.apache.log4j.Logger; - import javax.naming.ConfigurationException; import java.util.List; import java.util.Map; import java.util.function.Function; +import static java.util.Objects.isNull; +import static org.apache.cloudstack.utils.NsxApiClientUtils.HAMode.ACTIVE_STANDBY; +import static org.apache.cloudstack.utils.NsxApiClientUtils.FailoverMode.PREEMPTIVE; +import static org.apache.cloudstack.utils.NsxApiClientUtils.PoolAllocation.ROUTING; +import static org.apache.cloudstack.utils.NsxApiClientUtils.createApiClient; + public class NsxResource implements ServerResource { private static final Logger LOGGER = Logger.getLogger(NsxResource.class); private static final String TIER_0_GATEWAY_PATH_PREFIX = "/infra/tier-0s/"; + private static final String TIER_1_GATEWAY_PATH_PREFIX = "/infra/tier-1s/"; private static final String TIER_1_RESOURCE_TYPE = "Tier1"; -// private static final String ROUTING = "ROUTING"; + private static final String SEGMENT_RESOURCE_TYPE = "Segment"; + private String name; protected String hostname; protected String username; @@ -98,7 +111,11 @@ public class NsxResource implements ServerResource { return executeRequest((ReadyCommand) cmd); } else if (cmd instanceof DeleteNsxTier1GatewayCommand) { return executeRequest((DeleteNsxTier1GatewayCommand) cmd); - } else if (cmd instanceof CreateNsxTier1GatewayCommand) { + } else if (cmd instanceof DeleteNsxSegmentCommand) { + return executeRequest((DeleteNsxSegmentCommand) cmd); + } else if (cmd instanceof CreateNsxSegmentCommand) { + return executeRequest((CreateNsxSegmentCommand) cmd); + } else if (cmd instanceof CreateNsxTier1GatewayCommand) { return executeRequest((CreateNsxTier1GatewayCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); @@ -206,15 +223,13 @@ public class NsxResource implements ServerResource { } private Function nsxService = svcClass -> { return nsxApi.getApiClient().createStub(svcClass); }; - private Service getService(Class svcClass) { - return nsxApi.getApiClient().createStub(svcClass); - } private Answer executeRequest(CreateNsxTier1GatewayCommand cmd) { String name = getTier1GatewayName(cmd); Tier1 tier1 = getTier1Gateway(name); if (tier1 != null) { throw new InvalidParameterValueException(String.format("VPC network with name %s exists in NSX zone: %s and account %s", name, cmd.getZoneName(), cmd.getAccountName())); } + List localeServices = getTier0LocalServices(tier0Gateway); String tier0GatewayPath = TIER_0_GATEWAY_PATH_PREFIX + tier0Gateway; @@ -224,6 +239,7 @@ public class NsxResource implements ServerResource { .setResourceType(TIER_1_RESOURCE_TYPE) .setPoolAllocation(ROUTING.name()) .setHaMode(ACTIVE_STANDBY.name()) + .setFailoverMode(PREEMPTIVE.name()) .setId(name) .setDisplayName(name) .setChildren( @@ -231,7 +247,7 @@ public class NsxResource implements ServerResource { .setLocaleServices( new com.vmware.nsx_policy.model.LocaleServices.Builder() .setEdgeClusterPath(localeServices.get(0).getEdgeClusterPath()) - .setId(localeServices.get(0).getId()) + .setParentPath(TIER_1_GATEWAY_PATH_PREFIX + getTier1GatewayName(cmd)) .setResourceType("LocaleServices") .build() ).build())).build(); @@ -254,16 +270,69 @@ public class NsxResource implements ServerResource { return new NsxAnswer(cmd, true, null); } + private Answer executeRequest(CreateNsxSegmentCommand cmd) { + try { + String segmentName = getSegmentName(cmd); + Segments segmentService = (Segments) nsxService.apply(Segments.class); + SegmentSubnet subnet = new SegmentSubnet.Builder() + .setGatewayAddress(cmd.getTierNetwork().getGateway() + "/" + cmd.getTierNetwork().getCidr().split("/")[1]).build(); + Segment segment = new Segment.Builder() + .setResourceType(SEGMENT_RESOURCE_TYPE) + .setId(segmentName) + .setDisplayName(segmentName) + .setConnectivityPath(isNull(cmd.getVpcName()) ? TIER_0_GATEWAY_PATH_PREFIX + tier0Gateway + : TIER_1_GATEWAY_PATH_PREFIX + getTier1GatewayName(cmd)) + .setAdminState(NsxApiClientUtils.AdminState.UP.name()) + .setSubnets(List.of(subnet)) + .build(); + segmentService.patch(segmentName, segment); + } catch (Exception e) { + LOGGER.error(String.format("Failed to create network: %s", cmd.getTierNetwork().getName())); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(DeleteNsxSegmentCommand cmd) { + try { + String segmentName = getSegmentName(cmd); + Segments segmentService = (Segments) nsxService.apply(Segments.class); + segmentService.delete(segmentName); + } catch (Exception e) { + LOGGER.error(String.format("Failed to delete NSX segment: %s", getSegmentName(cmd)) ); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + private List getTier0LocalServices(String tier0Gateway) { try { LocaleServices tier0LocaleServices = (LocaleServices) nsxService.apply(LocaleServices.class); - LocaleServicesListResult result =tier0LocaleServices.list(tier0Gateway, null, false, null, null, null, null); + LocaleServicesListResult result = tier0LocaleServices.list(tier0Gateway, null, false, null, null, null, null); return result.getResults(); } catch (Exception e) { throw new CloudRuntimeException(String.format("Failed to fetch locale services for tier gateway %s due to %s", tier0Gateway, e.getMessage())); } } + private EdgeCluster getEdgeClusterDetails(String edgeClusterName) { + try { + EdgeClusters edgeClusterService = (EdgeClusters) nsxService.apply(EdgeClusters.class); + return edgeClusterService.get(edgeClusterName); + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Failed to fetch details of edge cluster: %s, due to: %s", edgeClusterName, e.getMessage())); + } + } + + private ServiceSegmentListResult listServiceSegments() { + try { + ServiceSegments serviceSegmentSvc = (ServiceSegments) nsxService.apply(ServiceSegments.class); + return serviceSegmentSvc.list(null, null, null, true, null); + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Failed to fetch service segment list due to %s", e.getMessage())); + } + } + private Tier1 getTier1Gateway(String tier1GatewayId) { try { Tier1s tier1service = (Tier1s) nsxService.apply(Tier1s.class); @@ -278,6 +347,10 @@ public class NsxResource implements ServerResource { return cmd.getZoneName() + "-" + cmd.getAccountName() + "-" + cmd.getVpcName(); } + private String getSegmentName(CreateNsxSegmentCommand cmd) { + return cmd.getAccountName() + "-" + cmd.getTierNetwork().getName(); + } + @Override public boolean start() { return true; diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java index e030991d499..69a7e028dd4 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java @@ -36,8 +36,11 @@ import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.network.Network; +import com.cloud.network.NetworkModel; import com.cloud.network.Networks; import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.element.DhcpServiceProvider; @@ -84,11 +87,15 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS @Inject DataCenterDao dataCenterDao; @Inject + NetworkDao networkDao; + @Inject AgentManager agentManager; @Inject ResourceManager resourceManager; @Inject PhysicalNetworkDao physicalNetworkDao; + @Inject + NetworkModel networkModel; private static final Logger LOGGER = Logger.getLogger(NsxElement.class); @@ -97,12 +104,21 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS private static Map> initCapabilities() { Map> capabilities = new HashMap<>(); + Map dhcpCapabilities = Map.of(Network.Capability.DhcpAccrossMultipleSubnets, "true"); capabilities.put(Network.Service.Dhcp, dhcpCapabilities); + Map dnsCapabilities = new HashMap<>(); dnsCapabilities.put(Network.Capability.AllowDnsSuffixModification, "true"); capabilities.put(Network.Service.Dns, dnsCapabilities); - capabilities.put(Network.Service.Connectivity, null); + +// capabilities.put(Network.Service.Connectivity, null); + capabilities.put(Network.Service.StaticNat, null); + + Map sourceNatCapabilities = new HashMap<>(); + sourceNatCapabilities.put(Network.Capability.RedundantRouter, "true"); + sourceNatCapabilities.put(Network.Capability.SupportedSourceNatTypes, "peraccount"); + capabilities.put(Network.Service.SourceNat, sourceNatCapabilities); return capabilities; } @Override @@ -172,12 +188,14 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS @Override public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { - return false; + return canHandle(network, Network.Service.Connectivity); } @Override public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { - return false; + Account account = accountMgr.getAccount(network.getAccountId()); + NetworkVO networkVO = networkDao.findById(network.getId()); + return nsxService.deleteNetwork(account.getAccountName(), networkVO); } @Override @@ -192,7 +210,7 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS @Override public boolean canEnableIndividualServices() { - return false; + return true; } @Override @@ -357,5 +375,17 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS return false; } + protected boolean canHandle(Network network, Network.Service service) { + LOGGER.debug("Checking if Nsx Element can handle service " + service.getName() + " on network " + + network.getDisplayText()); + + if (!networkModel.isProviderForNetwork(getProvider(), network.getId())) { + LOGGER.debug("Nsx Element is not a provider for network " + network.getDisplayText()); + return false; + } + + return true; + } + private final Function zoneFunction = zoneId -> { return dataCenterDao.findById(zoneId); }; } diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java index 1984e188a04..b31b3ca159f 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java @@ -16,24 +16,36 @@ // under the License. package org.apache.cloudstack.service; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + import com.cloud.dc.DataCenter; +import com.cloud.dc.dao.DataCenterDao; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network; import com.cloud.network.NetworkMigrationResponder; import com.cloud.network.NetworkProfile; import com.cloud.network.Networks; import com.cloud.network.PhysicalNetwork; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.guru.GuestNetworkGuru; +import com.cloud.network.vpc.VpcVO; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.user.Account; +import com.cloud.user.dao.AccountDao; import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; import org.apache.log4j.Logger; import javax.inject.Inject; @@ -43,6 +55,12 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr @Inject NetworkOfferingServiceMapDao networkOfferingServiceMapDao; + @Inject + NsxControllerUtils nsxControllerUtils; + @Inject + DataCenterDao zoneDao; + @Inject + AccountDao accountDao; private static final Networks.TrafficType[] TrafficTypes = {Networks.TrafficType.Guest}; @@ -56,12 +74,39 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr PhysicalNetwork physicalNetwork) { return networkType == DataCenter.NetworkType.Advanced && isMyTrafficType(offering.getTrafficType()) && isMyIsolationMethod(physicalNetwork) && networkOfferingServiceMapDao.isProviderForNetworkOffering( - offering.getId(), Network.Provider.Tungsten); + offering.getId(), Network.Provider.Nsx); } @Override public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { - return null; + PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); + DataCenter dc = _dcDao.findById(plan.getDataCenterId()); + + if (!canHandle(offering, dc.getNetworkType(), physnet)) { + LOGGER.debug("Refusing to design this network"); + return null; + } + + NetworkVO network = (NetworkVO) super.design(offering, plan, userSpecified, owner); + + if (network == null) { + return null; + } + if (userSpecified != null) { + if ((userSpecified.getIp6Cidr() == null && userSpecified.getIp6Gateway() != null) || ( + userSpecified.getIp6Cidr() != null && userSpecified.getIp6Gateway() == null)) { + throw new InvalidParameterValueException("cidrv6 and gatewayv6 must be specified together."); + } + + if (userSpecified.getIp6Cidr() != null) { + network.setIp6Cidr(userSpecified.getIp6Cidr()); + network.setIp6Gateway(userSpecified.getIp6Gateway()); + } + } + + network.setBroadcastDomainType(Networks.BroadcastDomainType.NSX); + network.setState(Network.State.Allocated); + return network; } @Override @@ -73,7 +118,38 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr @Override public Network implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) { - return null; + NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(), + network.getBroadcastDomainType(), network.getNetworkOfferingId(), Network.State.Implemented, + network.getDataCenterId(), network.getPhysicalNetworkId(), offering.isRedundantRouter()); + implemented.setAccountId(network.getAccountId()); + + if (network.getGateway() != null) { + implemented.setGateway(network.getGateway()); + } + + if (network.getCidr() != null) { + implemented.setCidr(network.getCidr()); + } + + if (network.getVpcId() != null) { + implemented.setVpcId(network.getVpcId()); + } + + if (network.getName() != null) { + implemented.setName(network.getName()); + } + implemented.setBroadcastUri(Networks.BroadcastDomainType.NSX.toUri("nsx")); + try { + long zoneId = network.getDataCenterId(); + DataCenter zone = zoneDao.findById(zoneId); + if (isNull(zone)) { + throw new CloudRuntimeException(String.format("Failed to find zone with id: %s", zoneId)); + } + createNsxSegment(implemented, zone); + } catch (Exception ex) { + throw new CloudRuntimeException("unable to create NSX network " + network.getUuid() + "due to: " + ex.getMessage()); + } + return implemented; } @Override @@ -112,4 +188,25 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) { } + + private void createNsxSegment(NetworkVO networkVO, DataCenter zone) { + String vpcName = null; + if (nonNull(networkVO.getVpcId())) { + VpcVO vpc = _vpcDao.findById(networkVO.getVpcId()); + if (isNull(vpc)) { + throw new CloudRuntimeException(String.format("Failed to find VPC network with id: %s", networkVO.getVpcId())); + } + vpcName = vpc.getName(); + } + Account account = accountDao.findById(networkVO.getAccountId()); + if (isNull(account)) { + throw new CloudRuntimeException(String.format("Unable to find account with id: %s", networkVO.getAccountId())); + } + CreateNsxSegmentCommand command = new CreateNsxSegmentCommand(zone.getName(), zone.getId(), + account.getAccountName(), networkVO.getAccountId(), vpcName, networkVO); + NsxAnswer answer = nsxControllerUtils.sendNsxCommand(command, zone.getId()); + if (!answer.getResult()) { + throw new CloudRuntimeException("can not create NSX network"); + } + } } diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java index bbad6ab5389..854ec9e793d 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java @@ -16,8 +16,10 @@ // under the License. package org.apache.cloudstack.service; +import com.cloud.network.dao.NetworkVO; import org.apache.cloudstack.NsxAnswer; import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand; import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand; import javax.inject.Inject; @@ -38,4 +40,10 @@ public class NsxServiceImpl implements NsxService { NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteNsxTier1GatewayCommand, zoneId); return result.getResult(); } + + public boolean deleteNetwork(String accountName, NetworkVO network) { + DeleteNsxSegmentCommand deleteNsxSegmentCommand = new DeleteNsxSegmentCommand(accountName, network); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteNsxSegmentCommand, network.getDataCenterId()); + return result.getResult(); + } } diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxApiClientUtils.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxApiClientUtils.java index c34eb385793..baa7a4f3ee9 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxApiClientUtils.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxApiClientUtils.java @@ -40,10 +40,25 @@ public class NsxApiClientUtils { LB_XLARGE } + public enum TYPE { + ROUTED, + NATTED + } + public enum HAMode { ACTIVE_STANDBY, ACTIVE_ACTIVE } + + public enum FailoverMode { + PREEMPTIVE, + NON_PREEMPTIVE + } + + public enum AdminState { + UP, + DOWN + } public static ApiClient createApiClient(String hostname, String port, String username, char[] password) { String controllerUrl = String.format("https://%s:%s", hostname, port); HttpConfiguration.SslConfiguration.Builder sslConfigBuilder = new HttpConfiguration.SslConfiguration.Builder(); diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 7b7bb0810b5..7d1c561c0f3 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1375,6 +1375,14 @@ "label.not.found": "Not found", "label.not.suitable": "Not suitable", "label.notifications": "Notifications", +"label.nsx.provider": "NSX Provider", +"label.nsx.provider.name": "NSX provider name", +"label.nsx.provider.hostname": "NSX provider hostname", +"label.nsx.provider.port": "NSX provider port", +"label.nsx.provider.username": "NSX provider username", +"label.nsx.provider.password": "NSX provider password", +"label.nsx.provider.edgecluster": "NSX provider edge cluster", +"label.nsx.provider.tier0gateway": "NSX provider tier-0 gateway", "label.num.cpu.cores": "# of CPU cores", "label.number": "#Rule", "label.numretries": "Number of retries", @@ -2752,6 +2760,7 @@ "message.host.dedication.released": "Host dedication released.", "message.info.cloudian.console": "Cloudian Management Console should open in another window.", "message.installwizard.cloudstack.helptext.website": " * Project website:\t ", +"message.infra.setup.nsx.description": "This zone must contain an NSX provider because the isolation method is NSX", "message.infra.setup.tungsten.description": "This zone must contain a Tungsten-Fabric provider because the isolation method is TF", "message.installwizard.cloudstack.helptext.document": " * Documentation:\t ", "message.installwizard.cloudstack.helptext.header": "\nYou can find more information about Apache CloudStack™ on the pages listed below.\n", diff --git a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue index ce8769801cf..0272602f8eb 100644 --- a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue +++ b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue @@ -480,6 +480,11 @@ export default { this.stepData.isTungstenZone = true this.stepData.tungstenPhysicalNetworkId = physicalNetworkReturned.id } + if (physicalNetwork.isolationMethod === 'NSX' && + physicalNetwork.traffics.findIndex(traffic => traffic.type === 'public' || traffic.type === 'guest') > -1) { + this.stepData.isNsxZone = true + this.stepData.tungstenPhysicalNetworkId = physicalNetworkReturned.id + } } else { this.stepData.physicalNetworkReturned = this.stepData.physicalNetworkItem['createPhysicalNetwork' + index] } @@ -965,6 +970,8 @@ export default { if (this.stepData.isTungstenZone) { await this.stepCreateTungstenFabricPublicNetwork() + } else if (this.stepData.isNsxZone) { + await this.stepAddNsxController() } else { await this.stepConfigureStorageTraffic() } @@ -1035,6 +1042,28 @@ export default { this.setStepStatus(STATUS_FAILED) } }, + async stepAddNsxController () { + try { + if (!this.stepData.stepMove.includes('addNsxController')) { + const providerParams = {} + providerParams.name = this.prefillContent?.name || '' + providerParams.nsxproviderhostname = this.prefillContent?.nsxHostname || '' + providerParams.nsxproviderport = this.prefillContent?.nsxPort || '' + providerParams.username = this.prefillContent?.username || '' + providerParams.password = this.prefillContent?.password || '' + providerParams.zoneid = this.stepData.zoneReturned.id + providerParams.tier0gateway = this.prefillContent?.tier0Gateway || '' + providerParams.edgecluster = this.prefillContent?.edgeCluster || '' + + await this.addNsxController(providerParams) + this.stepData.stepMove.push('addNsxController') + } + } catch (e) { + this.messageError = e + this.processStatus = STATUS_FAILED + this.setStepStatus(STATUS_FAILED) + } + }, async stepConfigureStorageTraffic () { let targetNetwork = false this.prefillContent.physicalNetworks.forEach(physicalNetwork => { @@ -2177,6 +2206,16 @@ export default { }) }) }, + addNsxController (args) { + return new Promise((resolve, reject) => { + api('addNsxController', {}, 'POST', args).then(json => { + resolve() + }).catch(error => { + const message = error.response.headers['x-description'] + reject(message) + }) + }) + }, configTungstenFabricService (args) { return new Promise((resolve, reject) => { api('configTungstenFabricService', {}, 'POST', args).then(json => { diff --git a/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue b/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue index 0ea76eebe19..62a33d24dc3 100644 --- a/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue +++ b/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue @@ -74,6 +74,18 @@ :isFixError="isFixError" /> + + network.isolationMethod === 'NSX') + isNsx = nsxIdx > -1 + } + return isNsx + }, allSteps () { const steps = [] steps.push({ @@ -201,6 +223,12 @@ export default { formKey: 'tungsten' }) } + if (this.isNsxZone) { + steps.push({ + title: 'label.nsx.provider', + formKey: 'nsx' + }) + } if (this.havingNetscaler) { steps.push({ title: 'label.netScaler', @@ -347,6 +375,53 @@ export default { } ] }, + nsxFields () { + const fields = [ + { + title: 'label.nsx.provider.name', + key: 'name', + placeHolder: 'message.installwizard.tooltip.nsx.provider.name', + required: true + }, + { + title: 'label.nsx.provider.hostname', + key: 'nsxHostname', + placeHolder: 'message.installwizard.tooltip.nsx.provider.hostname', + required: true + }, + { + title: 'label.nsx.provider.port', + key: 'nsxPort', + placeHolder: 'message.installwizard.tooltip.nsx.provider.port', + required: false + }, + { + title: 'label.nsx.provider.username', + key: 'username', + placeHolder: 'message.installwizard.tooltip.nsx.provider.username', + required: true + }, + { + title: 'label.nsx.provider.password', + key: 'password', + placeHolder: 'message.installwizard.tooltip.nsx.provider.password', + required: true + }, + { + title: 'label.nsx.provider.edgecluster', + key: 'edgeCluster', + placeHolder: 'message.installwizard.tooltip.nsx.provider.edgecluster', + required: true + }, + { + title: 'label.nsx.provider.tier0gateway', + key: 'tier0Gateway', + placeHolder: 'message.installwizard.tooltip.nsx.provider.tier0gateway', + required: true + } + ] + return fields + }, guestTrafficFields () { const fields = [ { @@ -416,6 +491,7 @@ export default { }, podSetupDescription: 'message.add.pod.during.zone.creation', tungstenSetupDescription: 'message.infra.setup.tungsten.description', + nsxSetupDescription: 'message.infra.setup.nsx.description', netscalerSetupDescription: 'label.please.specify.netscaler.info', storageTrafficDescription: 'label.zonewizard.traffictype.storage', podFields: [ @@ -459,6 +535,7 @@ export default { created () { this.physicalNetworks = this.prefillContent.physicalNetworks this.steps = this.filteredSteps() + console.log(this.isNsxZone) this.currentStep = this.prefillContent?.networkStep || 0 if (this.stepChild && this.stepChild !== '') { this.currentStep = this.steps.findIndex(item => item.formKey === this.stepChild) diff --git a/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue b/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue index 5491612411f..c0c1e6541b4 100644 --- a/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue +++ b/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue @@ -66,7 +66,7 @@ VSP VCS TF - NSX + NSX