diff --git a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java index d08884d1cbe..25c75001a3c 100644 --- a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java @@ -155,9 +155,7 @@ public class FirewallRuleTO implements InternalIdentity { rule.getIcmpType(), rule.getIcmpCode()); this.trafficType = trafficType; - if (FirewallRule.Purpose.Ipv6Firewall.equals(purpose)) { - this.destCidrList = rule.getDestinationCidrList(); - } + this.destCidrList = rule.getDestinationCidrList(); } public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType, diff --git a/api/src/main/java/com/cloud/bgp/ASNumber.java b/api/src/main/java/com/cloud/bgp/ASNumber.java new file mode 100644 index 00000000000..b0e5394df75 --- /dev/null +++ b/api/src/main/java/com/cloud/bgp/ASNumber.java @@ -0,0 +1,38 @@ +// 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.bgp; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface ASNumber extends InfrastructureEntity, InternalIdentity, Identity { + + Long getAccountId(); + Long getDomainId(); + long getAsNumber(); + long getAsNumberRangeId(); + long getDataCenterId(); + Date getAllocatedTime(); + boolean isAllocated(); + Long getNetworkId(); + Long getVpcId(); + Date getCreated(); + Date getRemoved(); +} diff --git a/api/src/main/java/com/cloud/bgp/ASNumberRange.java b/api/src/main/java/com/cloud/bgp/ASNumberRange.java new file mode 100644 index 00000000000..ae877ee60db --- /dev/null +++ b/api/src/main/java/com/cloud/bgp/ASNumberRange.java @@ -0,0 +1,31 @@ +// 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.bgp; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface ASNumberRange extends InfrastructureEntity, InternalIdentity, Identity { + + long getStartASNumber(); + long getEndASNumber(); + long getDataCenterId(); + Date getCreated(); +} diff --git a/api/src/main/java/com/cloud/bgp/BGPService.java b/api/src/main/java/com/cloud/bgp/BGPService.java new file mode 100644 index 00000000000..935237092dd --- /dev/null +++ b/api/src/main/java/com/cloud/bgp/BGPService.java @@ -0,0 +1,39 @@ +// 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.bgp; + +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.vpc.Vpc; +import com.cloud.utils.Pair; +import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd; + +import java.util.List; + +public interface BGPService { + + ASNumberRange createASNumberRange(long zoneId, long startASNumber, long endASNumber); + List listASNumberRanges(Long zoneId); + Pair, Integer> listASNumbers(ListASNumbersCmd cmd); + boolean allocateASNumber(long zoneId, Long asNumber, Long networkId, Long vpcId); + Pair releaseASNumber(long zoneId, long asNumber, boolean isReleaseNetworkDestroy); + boolean deleteASRange(long id); + + boolean applyBgpPeers(Network network, boolean continueOnError) throws ResourceUnavailableException; + + boolean applyBgpPeers(Vpc vpc, boolean continueOnError) throws ResourceUnavailableException; +} diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 050acdc84bc..5e5309965c1 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -28,7 +28,10 @@ import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.config.Configuration; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; import org.apache.cloudstack.ha.HAConfig; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; import org.apache.cloudstack.quota.QuotaTariff; import org.apache.cloudstack.storage.sharedfs.SharedFS; import org.apache.cloudstack.storage.object.Bucket; @@ -394,6 +397,11 @@ public class EventTypes { public static final String EVENT_VLAN_IP_RANGE_RELEASE = "VLAN.IP.RANGE.RELEASE"; public static final String EVENT_VLAN_IP_RANGE_UPDATE = "VLAN.IP.RANGE.UPDATE"; + // AS Number + public static final String EVENT_AS_RANGE_CREATE = "AS.RANGE.CREATE"; + public static final String EVENT_AS_RANGE_DELETE = "AS.RANGE.DELETE"; + public static final String EVENT_AS_NUMBER_RELEASE = "AS.NUMBER.RELEASE"; + public static final String EVENT_MANAGEMENT_IP_RANGE_CREATE = "MANAGEMENT.IP.RANGE.CREATE"; public static final String EVENT_MANAGEMENT_IP_RANGE_DELETE = "MANAGEMENT.IP.RANGE.DELETE"; public static final String EVENT_MANAGEMENT_IP_RANGE_UPDATE = "MANAGEMENT.IP.RANGE.UPDATE"; @@ -745,6 +753,25 @@ public class EventTypes { public static final String EVENT_QUOTA_TARIFF_DELETE = "QUOTA.TARIFF.DELETE"; public static final String EVENT_QUOTA_TARIFF_UPDATE = "QUOTA.TARIFF.UPDATE"; + // Routing + public static final String EVENT_ZONE_IP4_SUBNET_CREATE = "ZONE.IP4.SUBNET.CREATE"; + public static final String EVENT_ZONE_IP4_SUBNET_UPDATE = "ZONE.IP4.SUBNET.UPDATE"; + public static final String EVENT_ZONE_IP4_SUBNET_DELETE = "ZONE.IP4.SUBNET.DELETE"; + public static final String EVENT_ZONE_IP4_SUBNET_DEDICATE = "ZONE.IP4.SUBNET.DEDICATE"; + public static final String EVENT_ZONE_IP4_SUBNET_RELEASE = "ZONE.IP4.SUBNET.RELEASE"; + public static final String EVENT_IP4_GUEST_SUBNET_CREATE = "IP4.GUEST.SUBNET.CREATE"; + public static final String EVENT_IP4_GUEST_SUBNET_DELETE = "IP4.GUEST.SUBNET.DELETE"; + public static final String EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE = "ROUTING.IPV4.FIREWALL.RULE.CREATE"; + public static final String EVENT_ROUTING_IPV4_FIREWALL_RULE_UPDATE = "ROUTING.IPV4.FIREWALL.RULE.UPDATE"; + public static final String EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE = "ROUTING.IPV4.FIREWALL.RULE.DELETE"; + public static final String EVENT_BGP_PEER_CREATE = "BGP.PEER.CREATE"; + public static final String EVENT_BGP_PEER_UPDATE = "BGP.PEER.UPDATE"; + public static final String EVENT_BGP_PEER_DELETE = "BGP.PEER.DELETE"; + public static final String EVENT_BGP_PEER_DEDICATE = "BGP.PEER.DEDICATE"; + public static final String EVENT_BGP_PEER_RELEASE = "BGP.PEER.RELEASE"; + public static final String EVENT_NETWORK_BGP_PEER_UPDATE = "NETWORK.BGP.PEER.UPDATE"; + public static final String EVENT_VPC_BGP_PEER_UPDATE = "VPC.BGP.PEER.UPDATE"; + // SharedFS public static final String EVENT_SHAREDFS_CREATE = "SHAREDFS.CREATE"; public static final String EVENT_SHAREDFS_START = "SHAREDFS.START"; @@ -1217,6 +1244,23 @@ public class EventTypes { entityEventDetails.put(EVENT_QUOTA_TARIFF_DELETE, QuotaTariff.class); entityEventDetails.put(EVENT_QUOTA_TARIFF_UPDATE, QuotaTariff.class); + // Routing + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_CREATE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_UPDATE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_DELETE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_DEDICATE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_RELEASE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_IP4_GUEST_SUBNET_CREATE, Ipv4GuestSubnetNetworkMap.class); + entityEventDetails.put(EVENT_IP4_GUEST_SUBNET_DELETE, Ipv4GuestSubnetNetworkMap.class); + entityEventDetails.put(EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE, FirewallRule.class); + entityEventDetails.put(EVENT_ROUTING_IPV4_FIREWALL_RULE_UPDATE, FirewallRule.class); + entityEventDetails.put(EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE, FirewallRule.class); + entityEventDetails.put(EVENT_BGP_PEER_CREATE, BgpPeer.class); + entityEventDetails.put(EVENT_BGP_PEER_UPDATE, BgpPeer.class); + entityEventDetails.put(EVENT_BGP_PEER_DELETE, BgpPeer.class); + entityEventDetails.put(EVENT_BGP_PEER_DEDICATE, BgpPeer.class); + entityEventDetails.put(EVENT_BGP_PEER_RELEASE, BgpPeer.class); + // SharedFS entityEventDetails.put(EVENT_SHAREDFS_CREATE, SharedFS.class); entityEventDetails.put(EVENT_SHAREDFS_START, SharedFS.class); diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index 3b13ef7bd9c..d3bc5005cb7 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -103,7 +103,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Service Vpn = new Service("Vpn", Capability.SupportedVpnProtocols, Capability.VpnTypes); public static final Service Dhcp = new Service("Dhcp", Capability.ExtraDhcpOptions); public static final Service Dns = new Service("Dns", Capability.AllowDnsSuffixModification); - public static final Service Gateway = new Service("Gateway"); + public static final Service Gateway = new Service("Gateway", Capability.RedundantRouter); public static final Service Firewall = new Service("Firewall", Capability.SupportedProtocols, Capability.MultipleIps, Capability.TrafficStatistics, Capability.SupportedTrafficDirection, Capability.SupportedEgressProtocols); public static final Service Lb = new Service("Lb", Capability.SupportedLBAlgorithms, Capability.SupportedLBIsolation, Capability.SupportedProtocols, @@ -412,12 +412,16 @@ public interface Network extends ControlledEntity, StateObject, I String getGateway(); + void setGateway(String gateway); + // "cidr" is the Cloudstack managed address space, all CloudStack managed vms get IP address from "cidr", // In general "cidr" also serves as the network CIDR // But in case IP reservation is configured for a Guest network, "networkcidr" is the Effective network CIDR for that network, // "cidr" will still continue to be the effective address space for CloudStack managed vms in that Guest network String getCidr(); + void setCidr(String cidr); + // "networkcidr" is the network CIDR of the guest network which uses IP reservation. // It is the summation of "cidr" and the reservedIPrange(the address space used for non CloudStack purposes). // For networks not configured with IP reservation, "networkcidr" is always null @@ -503,4 +507,6 @@ public interface Network extends ControlledEntity, StateObject, I Integer getPublicMtu(); Integer getPrivateMtu(); + + Integer getNetworkCidrSize(); } diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java index 0d81495489c..d7de9df5325 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -173,6 +173,8 @@ public interface NetworkModel { boolean isProviderSupportServiceInNetwork(long networkId, Service service, Provider provider); + boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services); + boolean isProviderEnabledInPhysicalNetwork(long physicalNetowrkId, String providerName); String getNetworkTag(HypervisorType hType, Network network); diff --git a/api/src/main/java/com/cloud/network/NetworkProfile.java b/api/src/main/java/com/cloud/network/NetworkProfile.java index 1a5c80ea871..83dc247cc9e 100644 --- a/api/src/main/java/com/cloud/network/NetworkProfile.java +++ b/api/src/main/java/com/cloud/network/NetworkProfile.java @@ -41,8 +41,8 @@ public class NetworkProfile implements Network { private final Mode mode; private final BroadcastDomainType broadcastDomainType; private TrafficType trafficType; - private final String gateway; - private final String cidr; + private String gateway; + private String cidr; private final String networkCidr; private final String ip6Gateway; private final String ip6Cidr; @@ -62,6 +62,7 @@ public class NetworkProfile implements Network { private final String guruName; private boolean strechedL2Subnet; private String externalId; + private Integer networkCidrSize; public NetworkProfile(Network network) { id = network.getId(); @@ -98,6 +99,7 @@ public class NetworkProfile implements Network { isRedundant = network.isRedundant(); isRollingRestart = network.isRollingRestart(); externalId = network.getExternalId(); + networkCidrSize = network.getNetworkCidrSize(); } @Override @@ -210,11 +212,21 @@ public class NetworkProfile implements Network { return gateway; } + @Override + public void setGateway(String gateway) { + this.gateway = gateway; + } + @Override public String getCidr() { return cidr; } + @Override + public void setCidr(String cidr) { + this.cidr = cidr; + } + @Override public String getNetworkCidr() { return networkCidr; @@ -367,4 +379,9 @@ public class NetworkProfile implements Network { return null; } + @Override + public Integer getNetworkCidrSize() { + return networkCidrSize; + } + } diff --git a/api/src/main/java/com/cloud/network/element/BgpServiceProvider.java b/api/src/main/java/com/cloud/network/element/BgpServiceProvider.java new file mode 100644 index 00000000000..ee919cb1af7 --- /dev/null +++ b/api/src/main/java/com/cloud/network/element/BgpServiceProvider.java @@ -0,0 +1,31 @@ +// 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.element; + +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.vpc.Vpc; + +import org.apache.cloudstack.network.BgpPeer; + +import java.util.List; + +public interface BgpServiceProvider extends NetworkElement { + + boolean applyBgpPeers(Vpc vpc, Network network, List bgpPeers) throws ResourceUnavailableException; + +} diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java index 3aab57d5d3d..38263f59667 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java @@ -18,6 +18,7 @@ package com.cloud.network.vpc; import java.util.Date; +import com.cloud.offering.NetworkOffering; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -57,7 +58,7 @@ public interface VpcOffering extends InternalIdentity, Identity { boolean isForNsx(); - String getNsxMode(); + NetworkOffering.NetworkMode getNetworkMode(); /** * @return service offering id used by VPC virtual router @@ -79,4 +80,8 @@ public interface VpcOffering extends InternalIdentity, Identity { Date getRemoved(); Date getCreated(); + + NetworkOffering.RoutingMode getRoutingMode(); + + Boolean isSpecifyAsNumber(); } diff --git a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java index 1ce3cf8ab0e..10f1ddcc12d 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java @@ -24,6 +24,7 @@ import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd; import org.apache.cloudstack.api.command.user.vpc.ListVPCOfferingsCmd; +import com.cloud.offering.NetworkOffering; import com.cloud.utils.Pair; import com.cloud.utils.net.NetUtils; @@ -36,8 +37,10 @@ public interface VpcProvisioningService { VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders, Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol, - Long serviceOfferingId, Boolean forNsx, String mode, - List domainIds, List zoneIds, VpcOffering.State state); + Long serviceOfferingId, Boolean forNsx, NetworkOffering.NetworkMode networkMode, + List domainIds, List zoneIds, VpcOffering.State state, + NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber); + Pair,Integer> listVpcOfferings(ListVPCOfferingsCmd cmd); diff --git a/api/src/main/java/com/cloud/network/vpc/VpcService.java b/api/src/main/java/com/cloud/network/vpc/VpcService.java index 0f0d29f4082..af2a9847a62 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java @@ -56,7 +56,8 @@ public interface VpcService { * @throws ResourceAllocationException TODO */ Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain, - String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu) + String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu, Integer cidrSize, + Long asNumber, List bgpPeerIds) throws ResourceAllocationException; /** diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java index cf01fbf30e2..7011aea679e 100644 --- a/api/src/main/java/com/cloud/offering/NetworkOffering.java +++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java @@ -43,11 +43,15 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RelatedNetworkOffering, domainid, zoneid, pvlanType, internetProtocol } - public enum NsxMode { + public enum NetworkMode { NATTED, ROUTED } + enum RoutingMode { + Static, Dynamic + } + public final static String SystemPublicNetwork = "System-Public-Network"; public final static String SystemControlNetwork = "System-Control-Network"; public final static String SystemManagementNetwork = "System-Management-Network"; @@ -102,7 +106,7 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, boolean isForNsx(); - String getNsxMode(); + NetworkMode getNetworkMode(); TrafficType getTrafficType(); @@ -165,4 +169,8 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, String getServicePackage(); Date getCreated(); + + RoutingMode getRoutingMode(); + + Boolean isSpecifyAsNumber(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 21da4025e84..07bb0183f65 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -29,11 +29,17 @@ public class ApiConstants { public static final String ADDRESS = "address"; public static final String ALGORITHM = "algorithm"; public static final String ALIAS = "alias"; + public static final String ALLOCATED_DATE = "allocateddate"; public static final String ALLOCATED_ONLY = "allocatedonly"; + public static final String ALLOCATED_TIME = "allocated"; public static final String ALLOW_USER_FORCE_STOP_VM = "allowuserforcestopvm"; public static final String ANNOTATION = "annotation"; public static final String API_KEY = "apikey"; public static final String ARCHIVED = "archived"; + public static final String AS_NUMBER = "asnumber"; + public static final String AS_NUMBER_ID = "asnumberid"; + public static final String ASN_RANGE = "asnrange"; + public static final String ASN_RANGE_ID = "asnrangeid"; public static final String ASYNC_BACKUP = "asyncbackup"; public static final String AUTO_SELECT = "autoselect"; public static final String USER_API_KEY = "userapikey"; @@ -47,6 +53,8 @@ public class ApiConstants { public static final String BACKUP_OFFERING_NAME = "backupofferingname"; public static final String BACKUP_OFFERING_ID = "backupofferingid"; public static final String BASE64_IMAGE = "base64image"; + public static final String BGP_PEERS = "bgppeers"; + public static final String BGP_PEER_IDS = "bgppeerids"; public static final String BITS = "bits"; public static final String BOOTABLE = "bootable"; public static final String BIND_DN = "binddn"; @@ -88,6 +96,8 @@ public class ApiConstants { public static final String DNS_SEARCH_ORDER = "dnssearchorder"; public static final String CHAIN_INFO = "chaininfo"; public static final String CIDR = "cidr"; + public static final String CIDR_SIZE = "cidrsize"; + public static final String IP6_CIDR = "ip6cidr"; public static final String CIDR_LIST = "cidrlist"; public static final String DEST_CIDR_LIST = "destcidrlist"; @@ -171,6 +181,7 @@ public class ApiConstants { public static final String DURATION = "duration"; public static final String ELIGIBLE = "eligible"; public static final String EMAIL = "email"; + public static final String END_ASN = "endasn"; public static final String END_DATE = "enddate"; public static final String END_IP = "endip"; public static final String END_IPV6 = "endipv6"; @@ -315,7 +326,7 @@ public class ApiConstants { public static final String MIGRATIONS = "migrations"; public static final String MEMORY = "memory"; public static final String MODE = "mode"; - public static final String NSX_MODE = "nsxmode"; + public static final String NETWORK_MODE = "networkmode"; public static final String NSX_ENABLED = "isnsxenabled"; public static final String NAME = "name"; public static final String METHOD_NAME = "methodname"; @@ -356,6 +367,7 @@ public class ApiConstants { public static final String PARENT = "parent"; public static final String PARENT_ID = "parentid"; public static final String PARENT_DOMAIN_ID = "parentdomainid"; + public static final String PARENT_SUBNET = "parentsubnet"; public static final String PARENT_TEMPLATE_ID = "parenttemplateid"; public static final String PASSWORD = "password"; public static final String CURRENT_PASSWORD = "currentpassword"; @@ -441,6 +453,7 @@ public class ApiConstants { public static final String SNAPSHOT_QUIESCEVM = "quiescevm"; public static final String SOURCE_ZONE_ID = "sourcezoneid"; public static final String SSL_VERIFICATION = "sslverification"; + public static final String START_ASN = "startasn"; public static final String START_DATE = "startdate"; public static final String START_ID = "startid"; public static final String START_IP = "startip"; @@ -519,6 +532,7 @@ public class ApiConstants { public static final String ISOLATED_PVLAN = "isolatedpvlan"; public static final String ISOLATED_PVLAN_TYPE = "isolatedpvlantype"; public static final String ISOLATION_URI = "isolationuri"; + public static final String IS_ALLOCATED = "isallocated"; public static final String IS_DEDICATED = "isdedicated"; public static final String TAKEN = "taken"; public static final String VM_AVAILABLE = "vmavailable"; @@ -547,6 +561,7 @@ public class ApiConstants { public static final String NETWORK_ID = "networkid"; public static final String NETWORK_FILTER = "networkfilter"; public static final String NIC_ID = "nicid"; + public static final String SPECIFY_AS_NUMBER = "specifyasnumber"; public static final String SPECIFY_VLAN = "specifyvlan"; public static final String IS_DEFAULT = "isdefault"; public static final String IS_SYSTEM = "issystem"; @@ -688,6 +703,8 @@ public class ApiConstants { public static final String ASSOCIATED_NETWORK = "associatednetwork"; public static final String ASSOCIATED_NETWORK_ID = "associatednetworkid"; public static final String ASSOCIATED_NETWORK_NAME = "associatednetworkname"; + public static final String ASSOCIATED_VPC_ID = "associatedvpcid"; + public static final String ASSOCIATED_VPC_NAME = "associatedvpcname"; public static final String SOURCE_NAT_SUPPORTED = "sourcenatsupported"; public static final String RESOURCE_STATE = "resourcestate"; public static final String PROJECT_INVITE_REQUIRED = "projectinviterequired"; @@ -703,6 +720,8 @@ public class ApiConstants { public static final String LIST_ONLY_REMOVED = "listonlyremoved"; public static final String LIST_SYSTEM_VMS = "listsystemvms"; public static final String IP_RANGES = "ipranges"; + public static final String IPV4_ROUTING = "ip4routing"; + public static final String IPV4_ROUTES = "ip4routes"; public static final String IPV6_ROUTING = "ip6routing"; public static final String IPV6_ROUTES = "ip6routes"; public static final String SPECIFY_IP_RANGES = "specifyipranges"; @@ -964,6 +983,7 @@ public class ApiConstants { public static final String NUMBER = "number"; public static final String IS_DYNAMICALLY_SCALABLE = "isdynamicallyscalable"; public static final String ROUTING = "isrouting"; + public static final String ROUTING_MODE = "routingmode"; public static final String MAX_CONNECTIONS = "maxconnections"; public static final String SERVICE_STATE = "servicestate"; diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java index b206cd011c1..457afdc8847 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java @@ -31,6 +31,7 @@ import java.util.regex.Pattern; import javax.inject.Inject; +import com.cloud.bgp.BGPService; import org.apache.cloudstack.acl.ProjectRoleService; import org.apache.cloudstack.acl.RoleService; import org.apache.cloudstack.acl.RoleType; @@ -38,6 +39,7 @@ import org.apache.cloudstack.affinity.AffinityGroupService; import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService; import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService; import org.apache.cloudstack.query.QueryService; @@ -217,7 +219,11 @@ public abstract class BaseCmd { public VnfTemplateManager vnfTemplateManager; @Inject public BucketApiService _bucketService; + @Inject + public BGPService bgpService; + @Inject + public RoutedIpv4Manager routedIpv4Manager; public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException; diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 46a810c6f3b..ea0d946ee41 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -22,8 +22,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.cloudstack.api.response.BackupRepositoryResponse; -import org.apache.cloudstack.backup.BackupRepository; +import com.cloud.bgp.ASNumber; +import com.cloud.bgp.ASNumberRange; + import org.apache.cloudstack.storage.object.Bucket; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -33,11 +34,14 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ASNumberResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.AutoScalePolicyResponse; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; import org.apache.cloudstack.api.response.BackupOfferingResponse; +import org.apache.cloudstack.api.response.BackupRepositoryResponse; import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.BucketResponse; @@ -142,6 +146,7 @@ import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.BackupOffering; +import org.apache.cloudstack.backup.BackupRepository; import org.apache.cloudstack.backup.BackupSchedule; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.config.ConfigurationGroup; @@ -556,6 +561,10 @@ public interface ResponseGenerator { BucketResponse createBucketResponse(Bucket bucket); + ASNRangeResponse createASNumberRangeResponse(ASNumberRange asnRange); + + ASNumberResponse createASNumberResponse(ASNumber asn); + BackupRepositoryResponse createBackupRepositoryResponse(BackupRepository repository); SharedFSResponse createSharedFSResponse(ResponseView view, SharedFS sharedFS); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmd.java new file mode 100644 index 00000000000..beacba850c3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmd.java @@ -0,0 +1,83 @@ +// 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.api.command.admin.bgp; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +@APICommand(name = "createASNRange", + description = "Creates a range of Autonomous Systems for BGP Dynamic Routing", + responseObject = ASNRangeResponse.class, + entityType = {ASNumberRange.class}, + since = "4.20.0", + authorized = {RoleType.Admin}) +public class CreateASNRangeCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID", required = true) + private Long zoneId; + + @Parameter(name = ApiConstants.START_ASN, type = CommandType.LONG, required=true, description = "the start AS Number") + private Long startASNumber; + + @Parameter(name = ApiConstants.END_ASN, type = CommandType.LONG, required=true, description = "the end AS Number") + private Long endASNumber; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + ASNumberRange asnRange = bgpService.createASNumberRange(zoneId, startASNumber, endASNumber); + ASNRangeResponse response = _responseGenerator.createASNumberRangeResponse(asnRange); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Cannot create AS Number Range %s-%s for zone %s: %s", startASNumber, endASNumber, zoneId, e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + } + + public Long getZoneId() { + return zoneId; + } + + public Long getStartASNumber() { + return startASNumber; + } + + public Long getEndASNumber() { + return endASNumber; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmd.java new file mode 100644 index 00000000000..33e139315bf --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmd.java @@ -0,0 +1,79 @@ +// 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.api.command.admin.bgp; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "deleteASNRange", + description = "deletes a range of Autonomous Systems for BGP Dynamic Routing", + responseObject = SuccessResponse.class, + since = "4.20.0", + authorized = {RoleType.Admin}) +public class DeleteASNRangeCmd extends BaseCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + //////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = ASNRangeResponse.class, + required = true, + description = "ID of the AS range") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + if (bgpService.deleteASRange(getId())) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove AS range: " + getId()); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmd.java new file mode 100644 index 00000000000..82e54581102 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmd.java @@ -0,0 +1,79 @@ +// 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.api.command.admin.bgp; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listASNRanges", + description = "List Autonomous Systems Number Ranges", + responseObject = ASNRangeResponse.class, + entityType = {ASNumberRange.class}, + since = "4.20.0", + authorized = {RoleType.Admin}) +public class ListASNRangesCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID") + private Long zoneId; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + List ranges = bgpService.listASNumberRanges(zoneId); + ListResponse response = new ListResponse<>(); + List responses = new ArrayList<>(); + for (ASNumberRange asnRange : ranges) { + responses.add(_responseGenerator.createASNumberRangeResponse(asnRange)); + } + response.setResponses(responses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Error listing AS Number Ranges: %s", e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + } + + public Long getZoneId() { + return zoneId; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmd.java new file mode 100644 index 00000000000..687f60dc6da --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmd.java @@ -0,0 +1,83 @@ +// 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.api.command.admin.bgp; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +@APICommand(name = "releaseASNumber", + description = "Releases an AS Number back to the pool", + since = "4.20.0", + authorized = {RoleType.Admin}, + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class ReleaseASNumberCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID", required = true) + private Long zoneId; + + @Parameter(name= ApiConstants.AS_NUMBER, type=CommandType.LONG, description="the AS Number to be released", + required = true) + private Long asNumber; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + Pair resultPair = bgpService.releaseASNumber(zoneId, asNumber, false); + Boolean result = resultPair.first(); + if (!result) { + String details = resultPair.second(); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Cannot release AS Number %s: %s", asNumber, details)); + } + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setDisplayText(String.format("AS Number %s is released successfully", asNumber)); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Error releasing AS Number %s: %s", asNumber, e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + } + + public Long getZoneId() { + return zoneId; + } + + public Long getAsNumber() { + return asNumber; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java new file mode 100644 index 00000000000..a482cb1d4f2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java @@ -0,0 +1,108 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; + +@APICommand(name = "createIpv4SubnetForGuestNetwork", + description = "Creates a IPv4 subnet for guest networks.", + responseObject = Ipv4SubnetForGuestNetworkResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class CreateIpv4SubnetForGuestNetworkCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.PARENT_ID, + type = CommandType.UUID, + entityType = DataCenterIpv4SubnetResponse.class, + required = true, + description = "The zone Ipv4 subnet which the IPv4 subnet belongs to.") + private Long parentId; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + description = "The CIDR of this Ipv4 subnet.") + private String subnet; + + @Parameter(name = ApiConstants.CIDR_SIZE, + type = CommandType.INTEGER, + description = "the CIDR size of IPv4 network. This is mutually exclusive with subnet.") + private Integer cidrSize; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getParentId() { + return parentId; + } + + public String getSubnet() { + return subnet; + } + + public Integer getCidrSize() { + return cidrSize; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IP4_GUEST_SUBNET_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating guest IPv4 subnet " + getSubnet() + " in zone subnet=" + getParentId(); + } + + @Override + public void execute() { + Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.createIpv4SubnetForGuestNetwork(this); + if (result != null) { + Ipv4SubnetForGuestNetworkResponse response = routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create zone guest IPv4 subnet."); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmd.java new file mode 100644 index 00000000000..5f48cf9c632 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmd.java @@ -0,0 +1,125 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "createIpv4SubnetForZone", + description = "Creates a IPv4 subnet for a zone.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class CreateIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + required = true, + description = "UUID of the zone which the IPv4 subnet belongs to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long zoneId; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + required = true, + description = "The CIDR of the IPv4 subnet.") + private String subnet; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the IPv4 subnet") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the IPv4 subnet") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the IPv4 subnet") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getZoneId() { + return zoneId; + } + + public String getSubnet() { + return subnet; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating guest IPv4 subnet " + getSubnet() + " for zone=" + getZoneId(); + } + + @Override + public void execute() { + DataCenterIpv4GuestSubnet result = routedIpv4Manager.createDataCenterIpv4GuestSubnet(this); + if (result != null) { + DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create zone guest IPv4 subnet."); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java index cd9770877ed..d8b57f79528 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java @@ -24,10 +24,13 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.admin.AdminCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; import org.apache.cloudstack.api.response.NetworkResponse; import com.cloud.network.Network; +import java.util.List; + @APICommand(name = "createNetwork", description = "Creates a network", responseObject = NetworkResponse.class, responseView = ResponseView.Full, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCmd { @@ -49,6 +52,14 @@ public class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCm validations = {ApiArgValidator.NotNullOrEmpty}) private String routerIpv6; + @Parameter(name = ApiConstants.BGP_PEER_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "Ids of the Bgp Peer for the network", + since = "4.20.0") + private List bgpPeerIds; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -78,4 +89,8 @@ public class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCm public String getRouterIpv6() { return routerIpv6; } + + public List getBgpPeerIds() { + return bgpPeerIds; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java index 9117bcfc193..af3db374a7c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java @@ -146,12 +146,6 @@ public class CreateNetworkOfferingCmd extends BaseCmd { since = "4.20.0") private Boolean forNsx; - @Parameter(name = ApiConstants.NSX_MODE, - type = CommandType.STRING, - description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", - since = "4.20.0") - private String nsxMode; - @Parameter(name = ApiConstants.NSX_SUPPORT_LB, type = CommandType.BOOLEAN, description = "true if network offering for NSX network offering supports Load balancer service.", @@ -164,6 +158,12 @@ public class CreateNetworkOfferingCmd extends BaseCmd { since = "4.20.0") private Boolean nsxSupportsInternalLbService; + @Parameter(name = ApiConstants.NETWORK_MODE, + type = CommandType.STRING, + description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", + since = "4.20.0") + private String networkMode; + @Parameter(name = ApiConstants.FOR_TUNGSTEN, type = CommandType.BOOLEAN, description = "true if network offering is meant to be used for Tungsten-Fabric, false otherwise.") @@ -211,6 +211,16 @@ public class CreateNetworkOfferingCmd extends BaseCmd { since = "4.16") private Boolean enable; + @Parameter(name = ApiConstants.SPECIFY_AS_NUMBER, type = CommandType.BOOLEAN, since = "4.20.0", + description = "true if network offering supports choosing AS number") + private Boolean specifyAsNumber; + + @Parameter(name = ApiConstants.ROUTING_MODE, + type = CommandType.STRING, + since = "4.20.0", + description = "the routing mode for the network offering. Supported types are: Static or Dynamic.") + private String routingMode; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -302,8 +312,8 @@ public class CreateNetworkOfferingCmd extends BaseCmd { return BooleanUtils.isTrue(forNsx); } - public String getNsxMode() { - return nsxMode; + public String getNetworkMode() { + return networkMode; } public boolean getNsxSupportsLbService() { @@ -462,6 +472,14 @@ public class CreateNetworkOfferingCmd extends BaseCmd { return false; } + public boolean getSpecifyAsNumber() { + return BooleanUtils.toBoolean(specifyAsNumber); + } + + public String getRoutingMode() { + return routingMode; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java new file mode 100644 index 00000000000..2df032c559c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java @@ -0,0 +1,111 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "dedicateIpv4SubnetForZone", + description = "Dedicates an existing IPv4 subnet for a zone to an account or a domain.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DedicateIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DataCenterIpv4SubnetResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the IPv4 subnet") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the IPv4 subnet") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the IPv4 subnet") + private Long domainId; + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_DEDICATE; + } + + @Override + public String getEventDescription() { + return "Dedicating zone IPv4 subnet " + getId(); + } + + @Override + public void execute() { + try { + DataCenterIpv4GuestSubnet result = routedIpv4Manager.dedicateDataCenterIpv4GuestSubnet(this); + if (result != null) { + DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.java new file mode 100644 index 00000000000..28a646f9d03 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.java @@ -0,0 +1,88 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deleteIpv4SubnetForGuestNetwork", + description = "Deletes an existing IPv4 subnet for guest network.", + responseObject = SuccessResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DeleteIpv4SubnetForGuestNetworkCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Ipv4SubnetForGuestNetworkResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IP4_GUEST_SUBNET_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting guest IPv4 subnet " + getId(); + } + + @Override + public void execute() { + try { + boolean result = routedIpv4Manager.deleteIpv4SubnetForGuestNetwork(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmd.java new file mode 100644 index 00000000000..222bc1bad98 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmd.java @@ -0,0 +1,88 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deleteIpv4SubnetForZone", + description = "Deletes an existing IPv4 subnet for a zone.", + responseObject = SuccessResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DeleteIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DataCenterIpv4SubnetResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting zone IPv4 subnet " + getId(); + } + + @Override + public void execute() { + try { + boolean result = routedIpv4Manager.deleteDataCenterIpv4GuestSubnet(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmd.java new file mode 100644 index 00000000000..9761f6e89eb --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmd.java @@ -0,0 +1,123 @@ +// 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.api.command.admin.network; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; + +@APICommand(name = "listIpv4SubnetsForGuestNetwork", + description = "Lists IPv4 subnets for guest networks.", + responseObject = Ipv4SubnetForGuestNetworkResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ListIpv4SubnetsForGuestNetworkCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = Ipv4SubnetForGuestNetworkResponse.class, + description = "UUID of the IPv4 subnet for guest network.") + private Long id; + + @Parameter(name = ApiConstants.PARENT_ID, + type = CommandType.UUID, + entityType = DataCenterIpv4SubnetResponse.class, + description = "UUID of zone Ipv4 subnet which the IPv4 subnet belongs to.") + private Long parentId; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + description = "The CIDR of the Ipv4 subnet.") + private String subnet; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "UUID of zone to which the IPv4 subnet belongs to.") + private Long zoneId; + + @Parameter(name = ApiConstants.NETWORK_ID, + type = CommandType.UUID, + entityType = NetworkResponse.class, + description = "UUID of network to which the IPv4 subnet is associated to.") + private Long networkId; + + @Parameter(name = ApiConstants.VPC_ID, + type = CommandType.UUID, + entityType = VpcResponse.class, + description = "UUID of VPC to which the IPv4 subnet is associated to.") + private Long vpcId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getParentId() { + return parentId; + } + + public Long getZoneId() { + return zoneId; + } + + public String getSubnet() { + return subnet; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getVpcId() { + return vpcId; + } + + @Override + public void execute() { + List subnets = routedIpv4Manager.listIpv4GuestSubnetsForGuestNetwork(this); + ListResponse response = new ListResponse<>(); + List subnetResponses = new ArrayList<>(); + for (Ipv4GuestSubnetNetworkMap subnet : subnets) { + Ipv4SubnetForGuestNetworkResponse subnetResponse = routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(subnet); + subnetResponses.add(subnetResponse); + } + + response.setResponses(subnetResponses, subnets.size()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmd.java new file mode 100644 index 00000000000..2c2182250ed --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmd.java @@ -0,0 +1,120 @@ +// 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.api.command.admin.network; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +@APICommand(name = "listIpv4SubnetsForZone", + description = "Lists IPv4 subnets for zone.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ListIpv4SubnetsForZoneCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = DataCenterIpv4SubnetResponse.class, + description = "UUID of the IPv4 subnet.") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "UUID of zone to which the IPv4 subnet belongs to.") + private Long zoneId; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + description = "CIDR of the IPv4 subnet.") + private String subnet; + + @Parameter(name = ApiConstants.ACCOUNT, + type = CommandType.STRING, + description = "the account which the IPv4 subnet is dedicated to. Must be used with the domainId parameter.") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, + type = CommandType.UUID, + entityType = ProjectResponse.class, + description = "project who which the IPv4 subnet is dedicated to") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "the domain ID which the IPv4 subnet is dedicated to.") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + public String getSubnet() { + return subnet; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + @Override + public void execute() { + List subnets = routedIpv4Manager.listDataCenterIpv4GuestSubnets(this); + ListResponse response = new ListResponse<>(); + List subnetResponses = new ArrayList<>(); + for (DataCenterIpv4GuestSubnet subnet : subnets) { + DataCenterIpv4SubnetResponse subnetResponse = routedIpv4Manager.createDataCenterIpv4SubnetResponse(subnet); + subnetResponses.add(subnetResponse); + } + + response.setResponses(subnetResponses, subnets.size()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.java new file mode 100644 index 00000000000..3e151b9b58f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.java @@ -0,0 +1,88 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "releaseIpv4SubnetForZone", + description = "Releases an existing dedicated IPv4 subnet for a zone.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ReleaseDedicatedIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DataCenterIpv4SubnetResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_RELEASE; + } + + @Override + public String getEventDescription() { + return "Releasing a dedicated zone IPv4 subnet " + getId(); + } + + @Override + public void execute() { + try { + DataCenterIpv4GuestSubnet result = routedIpv4Manager.releaseDedicatedDataCenterIpv4GuestSubnet(this); + if (result != null) { + DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmd.java new file mode 100644 index 00000000000..da7a23f50d9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmd.java @@ -0,0 +1,98 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "updateIpv4SubnetForZone", + description = "Updates an existing IPv4 subnet for a zone.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class UpdateIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DataCenterIpv4SubnetResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + required = true, + description = "The new CIDR of the IPv4 subnet.") + private String subnet; + + public Long getId() { + return id; + } + + public String getSubnet() { + return subnet; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating zone IPv4 subnet " + getId(); + } + + @Override + public void execute() { + try { + DataCenterIpv4GuestSubnet result = routedIpv4Manager.updateDataCenterIpv4GuestSubnet(this); + if (result != null) { + DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.java new file mode 100644 index 00000000000..1d6bffca342 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.java @@ -0,0 +1,109 @@ +// 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.api.command.admin.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +import java.util.List; + +@APICommand(name = "changeBgpPeersForNetwork", + description = "Change the BGP peers for a network.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ChangeBgpPeersForNetworkCmd extends BaseAsyncCmd implements AdminCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_ID, + type = CommandType.UUID, + entityType = NetworkResponse.class, + required = true, + description = "UUID of the network which the Bgp Peers are associated to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long networkId; + + @Parameter(name = ApiConstants.BGP_PEER_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "Ids of the Bgp Peer. If it is empty, all BGP peers will be unlinked.") + private List bgpPeerIds; + + public Long getNetworkId() { + return networkId; + } + + public List getBgpPeerIds() { + return bgpPeerIds; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_BGP_PEER_UPDATE; + } + + @Override + public String getEventDescription() { + return "Changing Bgp Peers for network " + getNetworkId(); + } + + @Override + public void execute() { + try { + Network result = routedIpv4Manager.changeBgpPeersForNetwork(this); + if (result != null) { + NetworkResponse response = _responseGenerator.createNetworkResponse(getResponseView(), result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change BGP Peers for network"); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmd.java new file mode 100644 index 00000000000..0c89f3f1d43 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmd.java @@ -0,0 +1,109 @@ +// 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.api.command.admin.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.VpcResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.vpc.Vpc; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +import java.util.List; + +@APICommand(name = "changeBgpPeersForVpc", + description = "Change the BGP peers for a VPC.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ChangeBgpPeersForVpcCmd extends BaseAsyncCmd implements AdminCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.VPC_ID, + type = CommandType.UUID, + entityType = VpcResponse.class, + required = true, + description = "UUID of the VPC which the Bgp Peers are associated to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long vpcId; + + @Parameter(name = ApiConstants.BGP_PEER_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "Ids of the Bgp Peer. If it is empty, all BGP peers will be unlinked.") + private List bgpPeerIds; + + public Long getVpcId() { + return vpcId; + } + + public List getBgpPeerIds() { + return bgpPeerIds; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VPC_BGP_PEER_UPDATE; + } + + @Override + public String getEventDescription() { + return "Changing Bgp Peers for VPC " + getVpcId(); + } + + @Override + public void execute() { + try { + Vpc result = routedIpv4Manager.changeBgpPeersForVpc(this); + if (result != null) { + VpcResponse response = _responseGenerator.createVpcResponse(getResponseView(), result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change BGP Peers for vpc"); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmd.java new file mode 100644 index 00000000000..80642124938 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmd.java @@ -0,0 +1,168 @@ +// 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.api.command.admin.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.commons.collections.MapUtils; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +import java.util.Collection; +import java.util.Map; + +@APICommand(name = "createBgpPeer", + description = "Creates a Bgp Peer for a zone.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = true, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class CreateBgpPeerCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + required = true, + description = "UUID of the zone which the Bgp Peer belongs to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long zoneId; + + @Parameter(name = ApiConstants.IP_ADDRESS, + type = CommandType.STRING, + description = "The IPv4 address of the Bgp Peer.") + private String ip4Address; + + @Parameter(name = ApiConstants.IP6_ADDRESS, + type = CommandType.STRING, + description = "The IPv6 address of the Bgp Peer.") + private String ip6Address; + + @Parameter(name = ApiConstants.AS_NUMBER, + type = CommandType.LONG, + required = true, + description = "The AS number of the Bgp Peer.") + private Long asNumber; + + @Parameter(name = ApiConstants.PASSWORD, + type = CommandType.STRING, + description = "The password of the Bgp Peer.") + private String password; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the Bgp Peer") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the Bgp Peer") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the Bgp Peer") + private Long domainId; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, + description = "BGP peer details in key/value pairs.") + protected Map details; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getZoneId() { + return zoneId; + } + + public String getIp4Address() { + return ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public String getPassword() { + return password; + } + + public Long getAsNumber() { + return asNumber; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + public Map getDetails() { + if (MapUtils.isEmpty(details)) { + return null; + } + Collection paramsCollection = this.details.values(); + return (Map) (paramsCollection.toArray())[0]; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating Bgp Peer " + getAsNumber() + " for zone=" + getZoneId(); + } + + @Override + public void execute() { + BgpPeer result = routedIpv4Manager.createBgpPeer(this); + if (result != null) { + BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Bgp Peer."); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmd.java new file mode 100644 index 00000000000..ec3d0ea1162 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmd.java @@ -0,0 +1,111 @@ +// 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.api.command.admin.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.network.BgpPeer; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "dedicateBgpPeer", + description = "Dedicates an existing Bgp Peer to an account or a domain.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DedicateBgpPeerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BgpPeerResponse.class, required = true, description = "Id of the Bgp Peer") + private Long id; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the Bgp Peer") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the Bgp Peer") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the Bgp Peer") + private Long domainId; + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_DEDICATE; + } + + @Override + public String getEventDescription() { + return "Dedicating Bgp Peer " + getId(); + } + + @Override + public void execute() { + try { + BgpPeer result = routedIpv4Manager.dedicateBgpPeer(this); + if (result != null) { + BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate Bgp Peer:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmd.java new file mode 100644 index 00000000000..a01711efa44 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmd.java @@ -0,0 +1,88 @@ +// 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.api.command.admin.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deleteBgpPeer", + description = "Deletes an existing Bgp Peer.", + responseObject = SuccessResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DeleteBgpPeerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BgpPeerResponse.class, required = true, description = "Id of the Bgp Peer") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting Bgp Peer " + getId(); + } + + @Override + public void execute() { + try { + boolean result = routedIpv4Manager.deleteBgpPeer(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Bgp Peer:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmd.java new file mode 100644 index 00000000000..ea15f0970e8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmd.java @@ -0,0 +1,130 @@ +// 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.api.command.admin.network.bgp; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.network.BgpPeer; + +@APICommand(name = "listBgpPeers", + description = "Lists Bgp Peers.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ListBgpPeersCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "UUID of the Bgp Peer.") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "UUID of zone to which the Bgp Peer belongs to.") + private Long zoneId; + + @Parameter(name = ApiConstants.AS_NUMBER, + type = CommandType.LONG, + description = "AS number of the Bgp Peer.") + private Long asNumber; + + @Parameter(name = ApiConstants.ACCOUNT, + type = CommandType.STRING, + description = "the account which the Bgp Peer is dedicated to. Must be used with the domainId parameter.") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, + type = CommandType.UUID, + entityType = ProjectResponse.class, + description = "project who which the Bgp Peer is dedicated to") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "the domain ID which the Bgp Peer is dedicated to.") + private Long domainId; + + @Parameter(name = ApiConstants.IS_DEDICATED, + type = CommandType.BOOLEAN, + description = "Lists only dedicated or non-dedicated Bgp Peers. If not set, lists all dedicated and non-dedicated BGP peers the domain/account can access.") + private Boolean isDedicated; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getAsNumber() { + return asNumber; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + public Boolean getDedicated() { + return isDedicated; + } + + @Override + public void execute() { + List subnets = routedIpv4Manager.listBgpPeers(this); + ListResponse response = new ListResponse<>(); + List subnetResponses = new ArrayList<>(); + for (BgpPeer subnet : subnets) { + BgpPeerResponse subnetResponse = routedIpv4Manager.createBgpPeerResponse(subnet); + subnetResponse.setObjectName("bgppeer"); + subnetResponses.add(subnetResponse); + } + + response.setResponses(subnetResponses, subnets.size()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmd.java new file mode 100644 index 00000000000..92610c233ef --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmd.java @@ -0,0 +1,88 @@ +// 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.api.command.admin.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.network.BgpPeer; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "releaseBgpPeer", + description = "Releases an existing dedicated Bgp Peer.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ReleaseDedicatedBgpPeerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BgpPeerResponse.class, required = true, description = "Id of the Bgp Peer") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_RELEASE; + } + + @Override + public String getEventDescription() { + return "Releasing a dedicated Bgp Peer " + getId(); + } + + @Override + public void execute() { + try { + BgpPeer result = routedIpv4Manager.releaseDedicatedBgpPeer(this); + if (result != null) { + BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release Bgp Peer:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmd.java new file mode 100644 index 00000000000..ae44330ea03 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmd.java @@ -0,0 +1,149 @@ +// 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.api.command.admin.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.network.BgpPeer; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.collections.MapUtils; + +import java.util.Collection; +import java.util.Map; + +@APICommand(name = "updateBgpPeer", + description = "Updates an existing Bgp Peer.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = true, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class UpdateBgpPeerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BgpPeerResponse.class, required = true, description = "Id of the Bgp Peer") + private Long id; + + @Parameter(name = ApiConstants.IP_ADDRESS, + type = CommandType.STRING, + description = "The IPv4 address of the Bgp Peer.") + private String ip4Address; + + @Parameter(name = ApiConstants.IP6_ADDRESS, + type = CommandType.STRING, + description = "The IPv6 address of the Bgp Peer.") + private String ip6Address; + + @Parameter(name = ApiConstants.AS_NUMBER, + type = CommandType.LONG, + description = "The AS number of the Bgp Peer.") + private Long asNumber; + + @Parameter(name = ApiConstants.PASSWORD, + type = CommandType.STRING, + description = "The password of the Bgp Peer.") + private String password; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, + description = "BGP peer details in key/value pairs.") + 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 are removed for this resource; if false or not set, no action)") + private Boolean cleanupDetails; + + public Long getId() { + return id; + } + + public String getIp4Address() { + return ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public Long getAsNumber() { + return asNumber; + } + + public String getPassword() { + return password; + } + + public Map getDetails() { + if (MapUtils.isEmpty(details)) { + return null; + } + Collection paramsCollection = this.details.values(); + return (Map) (paramsCollection.toArray())[0]; + } + + public boolean isCleanupDetails(){ + return cleanupDetails == null ? false : cleanupDetails.booleanValue(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating Bgp Peer " + getId(); + } + + @Override + public void execute() { + try { + BgpPeer result = routedIpv4Manager.updateBgpPeer(this); + if (result != null) { + BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Bgp Peer:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdmin.java index bd00876ed36..9dc31f8cefe 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdmin.java @@ -17,13 +17,31 @@ package org.apache.cloudstack.api.command.admin.vpc; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.admin.AdminCmd; import org.apache.cloudstack.api.command.user.vpc.CreateVPCCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; import org.apache.cloudstack.api.response.VpcResponse; import com.cloud.network.vpc.Vpc; +import java.util.List; + @APICommand(name = "createVPC", description = "Creates a VPC", responseObject = VpcResponse.class, responseView = ResponseView.Full, entityType = {Vpc.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class CreateVPCCmdByAdmin extends CreateVPCCmd implements AdminCmd {} +public class CreateVPCCmdByAdmin extends CreateVPCCmd implements AdminCmd { + @Parameter(name = ApiConstants.BGP_PEER_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "Ids of the Bgp Peer for the VPC", + since = "4.20.0") + private List bgpPeerIds; + + + public List getBgpPeerIds() { + return bgpPeerIds; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java index dd5c815238e..73b4f5df196 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -118,12 +118,6 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { since = "4.20.0") private Boolean forNsx; - @Parameter(name = ApiConstants.NSX_MODE, - type = CommandType.STRING, - description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", - since = "4.20.0") - private String nsxMode; - @Parameter(name = ApiConstants.NSX_SUPPORT_LB, type = CommandType.BOOLEAN, description = "true if network offering for NSX VPC offering supports Load balancer service.", @@ -136,6 +130,22 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { since = "4.16") private Boolean enable; + @Parameter(name = ApiConstants.NETWORK_MODE, + type = CommandType.STRING, + description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", + since = "4.20.0") + private String networkMode; + + @Parameter(name = ApiConstants.SPECIFY_AS_NUMBER, type = CommandType.BOOLEAN, since = "4.20.0", + description = "true if the VPC offering supports choosing AS number") + private Boolean specifyAsNumber; + + @Parameter(name = ApiConstants.ROUTING_MODE, + type = CommandType.STRING, + since = "4.20.0", + description = "the routing mode for the VPC offering. Supported types are: Static or Dynamic.") + private String routingMode; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -173,8 +183,8 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { return BooleanUtils.isTrue(forNsx); } - public String getNsxMode() { - return nsxMode; + public String getNetworkMode() { + return networkMode; } public boolean getNsxSupportsLbService() { @@ -265,6 +275,14 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { return false; } + public Boolean getSpecifyAsNumber() { + return BooleanUtils.toBoolean(specifyAsNumber); + } + + public String getRoutingMode() { + return routingMode; + } + @Override public void create() throws ResourceAllocationException { VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(this); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmd.java new file mode 100644 index 00000000000..b835f7225df --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmd.java @@ -0,0 +1,134 @@ +// 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.api.command.user.bgp; + +import com.cloud.bgp.ASNumber; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.utils.Pair; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ASNumberResponse; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listASNumbers", + description = "List Autonomous Systems Numbers", + responseObject = ASNumberResponse.class, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class ListASNumbersCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID") + private Long zoneId; + + @Parameter(name = ApiConstants.ASN_RANGE_ID, type = BaseCmd.CommandType.UUID, entityType = ASNRangeResponse.class, + description = "the AS Number range ID") + private Long asNumberRangeId; + + @Parameter(name = ApiConstants.AS_NUMBER, type = CommandType.INTEGER, entityType = ASNumberResponse.class, + description = "AS number") + private Integer asNumber; + + @Parameter(name = ApiConstants.IS_ALLOCATED, type = CommandType.BOOLEAN, + description = "to indicate if the AS number is allocated to any network") + private Boolean allocated; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, + description = "the network id") + private Long networkId; + + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, + description = "the vpc id") + private Long vpcId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, entityType = AccountResponse.class, + description = "account name") + private String account; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, + description = "domain id") + private Long domainId; + + public Long getZoneId() { + return zoneId; + } + + public Long getAsNumberRangeId() { + return asNumberRangeId; + } + + public Boolean getAllocated() { + return allocated; + } + + public Integer getAsNumber() { return asNumber; } + + public Long getNetworkId() { + return networkId; + } + + public String getAccount() { + return account; + } + + public Long getDomainId() { + return domainId; + } + + public Long getVpcId() { + return vpcId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + Pair, Integer> pair = bgpService.listASNumbers(this); + List asNumbers = pair.first(); + ListResponse response = new ListResponse<>(); + List responses = new ArrayList<>(); + for (ASNumber asn : asNumbers) { + responses.add(_responseGenerator.createASNumberResponse(asn)); + } + response.setResponses(responses, pair.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Error listing AS Numbers, due to: %s", e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java index 4e3cf4621ef..18af5b2973e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java @@ -43,7 +43,7 @@ import com.cloud.user.Account; import com.cloud.utils.net.NetUtils; @APICommand(name = "createIpv6FirewallRule", - description = "Creates an Ipv6 firewall rule in the given network (the network has to belong to VPC)", + description = "Creates an Ipv6 firewall rule in the given network (the network must not belong to VPC)", responseObject = FirewallRuleResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java index 2395339a477..aca3d3ca1b4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java @@ -191,6 +191,14 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { since = "4.19") private String sourceNatIP; + @Parameter(name = ApiConstants.CIDR_SIZE, type = CommandType.INTEGER, + description = "the CIDR size of IPv4 network. For regular users, this is required for isolated networks with ROUTED mode.", + since = "4.20.0") + private Integer cidrSize; + + @Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, since = "4.20.0", description="the AS Number of the network") + private Long asNumber; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -364,6 +372,10 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { return NetUtils.standardizeIp6Cidr(ip6Cidr); } + public Integer getCidrSize() { + return cidrSize; + } + public Long getAclId() { return aclId; } @@ -391,6 +403,10 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { return ip6Dns2; } + public Long getAsNumber() { + return asNumber; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkOfferingsCmd.java index 33f452008d9..bdc89d804cd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkOfferingsCmd.java @@ -110,6 +110,12 @@ public class ListNetworkOfferingsCmd extends BaseListCmd { @Parameter(name = ApiConstants.FOR_VPC, type = CommandType.BOOLEAN, description = "the network offering can be used" + " only for network creation inside the VPC") private Boolean forVpc; + @Parameter(name = ApiConstants.ROUTING_MODE, + type = CommandType.STRING, + description = "the routing mode for the network offering. Supported types are: Static or Dynamic.", + since = "4.20.0") + private String routingMode; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -186,6 +192,8 @@ public class ListNetworkOfferingsCmd extends BaseListCmd { return forVpc; } + public String getRoutingMode() { return routingMode; } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java new file mode 100644 index 00000000000..7146d1ae1d1 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java @@ -0,0 +1,271 @@ +// 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.api.command.user.network.routing; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.exception.NetworkRuleConflictException; +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.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; +import com.cloud.utils.net.NetUtils; + +@APICommand(name = "createRoutingFirewallRule", + description = "Creates a routing firewall rule in the given network in ROUTED mode", + since = "4.20.0", + responseObject = FirewallRuleResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateRoutingFirewallRuleCmd extends BaseAsyncCreateCmd { + + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP/ALL or valid protocol number") + private String protocol; + + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of firewall rule") + private Integer publicStartPort; + + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of firewall rule") + private Integer publicEndPort; + + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, + description = "the source CIDR list to allow traffic from. Multiple entries must be separated by a single comma character (,).") + protected List sourceCidrList; + + @Parameter(name = ApiConstants.DEST_CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, + description = "the destination CIDR list to allow traffic to. Multiple entries must be separated by a single comma character (,).") + protected List destinationCidrlist; + + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the ICMP message being sent") + private Integer icmpType; + + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this ICMP message") + private Integer icmpCode; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, + description = "The network of the VM the firewall rule will be created for", required = true) + private Long networkId; + + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, + description = "the traffic type for the Routing firewall rule, can be ingress or egress, defaulted to ingress if not specified") + private String trafficType; + + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, + description = "an optional field, whether to the display the rule to the end user or not", authorized = {RoleType.Admin}) + private Boolean display; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + @Override + public boolean isDisplay() { + if (display != null) { + return display; + } else { + return true; + } + } + + public String getProtocol() { + String p = protocol == null ? "" : protocol.trim(); + if (StringUtils.isNumeric(p)) { + int protoNumber = Integer.parseInt(p); + switch (protoNumber) { + case 1: + p = NetUtils.ICMP_PROTO; + break; + case 6: + p = NetUtils.TCP_PROTO; + break; + case 17: + p = NetUtils.UDP_PROTO; + break; + default: + throw new InvalidParameterValueException(String.format("Protocol %d not supported", protoNumber)); + + } + } + return p; + } + + public List getSourceCidrList() { + if (sourceCidrList != null) { + return sourceCidrList; + } else { + List oneCidrList = new ArrayList(); + oneCidrList.add(NetUtils.ALL_IP4_CIDRS); + return oneCidrList; + } + } + + public List getDestinationCidrList() { + if (destinationCidrlist != null) { + return destinationCidrlist; + } else { + List oneCidrList = new ArrayList(); + oneCidrList.add(NetUtils.ALL_IP4_CIDRS); + return oneCidrList; + } + } + + public FirewallRule.TrafficType getTrafficType() { + if (trafficType == null) { + return FirewallRule.TrafficType.Ingress; + } + for (FirewallRule.TrafficType type : FirewallRule.TrafficType.values()) { + if (type.toString().equalsIgnoreCase(trafficType)) { + return type; + } + } + throw new InvalidParameterValueException("Invalid traffic type " + trafficType); + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + public Integer getSourcePortStart() { + return publicStartPort; + } + + public Integer getSourcePortEnd() { + if (publicEndPort == null) { + if (publicStartPort != null) { + return publicStartPort; + } + } else { + return publicEndPort; + } + + return null; + } + + public Long getNetworkId() { + return networkId; + } + + @Override + public long getEntityOwnerId() { + Network network = _networkService.getNetwork(networkId); + if (network != null) { + return network.getAccountId(); + } + Account owner = CallContext.current().getCallingAccount(); + return owner.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating ipv4 firewall rule for routed network"; + } + + public Integer getIcmpCode() { + if (icmpCode != null) { + return icmpCode; + } else if (getProtocol().equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + } + return null; + } + + public Integer getIcmpType() { + if (icmpType != null) { + return icmpType; + } else if (getProtocol().equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + + } + return null; + } + + @Override + public void create() { + try { + FirewallRule result = routedIpv4Manager.createRoutingFirewallRule(this); + setEntityId(result.getId()); + setEntityUuid(result.getUuid()); + } catch (NetworkRuleConflictException e) { + logger.trace("Network Rule Conflict: ", e); + throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, e.getMessage(), e); + } + } + + @Override + public void execute() throws ResourceUnavailableException { + boolean success = false; + FirewallRule rule = _firewallService.getFirewallRule(getEntityId()); + try { + CallContext.current().setEventDetails("Rule ID: " + getEntityId()); + success = routedIpv4Manager.applyRoutingFirewallRule(rule.getId()); + + // State is different after the rule is applied, so get new object here + rule = _firewallService.getFirewallRule(getEntityId()); + FirewallResponse ruleResponse = new FirewallResponse(); + if (rule != null) { + ruleResponse = _responseGenerator.createFirewallResponse(rule); + setResponseObject(ruleResponse); + } + ruleResponse.setResponseName(getCommandName()); + } catch (Exception ex) { + logger.error("Got exception when create Routing firewall rules: " + ex); + } finally { + if (!success || rule == null) { + routedIpv4Manager.revokeRoutingFirewallRule(getEntityId()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Routing firewall rule"); + } + } + } + + @Override + public Long getApiResourceId() { + return getNetworkId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Network; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmd.java new file mode 100644 index 00000000000..16696f5f71b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmd.java @@ -0,0 +1,109 @@ +// 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.api.command.user.network.routing; + +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.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; + +@APICommand(name = "deleteRoutingFirewallRule", + description = "Deletes a routing firewall rule", + since = "4.20.0", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteRoutingFirewallRuleCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the Routing firewall rule") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public String getEventType() { + return EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE; + } + + @Override + public String getEventDescription() { + return String.format("Deleting ipv4 routing firewall rule ID=%s", id); + } + + @Override + public long getEntityOwnerId() { + FirewallRule rule = _firewallService.getFirewallRule(id); + if (rule != null) { + return rule.getAccountId(); + } + Account caller = CallContext.current().getCallingAccount(); + return caller.getAccountId(); + } + + @Override + public void execute() throws ResourceUnavailableException { + CallContext.current().setEventDetails("Routing firewall rule ID: " + id); + boolean result = routedIpv4Manager.revokeRoutingFirewallRule(id); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Routing firewall rule"); + } + } + + @Override + public Long getApiResourceId() { + FirewallRule rule = _firewallService.getFirewallRule(id); + if (rule != null) { + return rule.getNetworkId(); + } + return null; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Network; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmd.java new file mode 100644 index 00000000000..3fdf3b0f5b4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmd.java @@ -0,0 +1,115 @@ +// 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.api.command.user.network.routing; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.user.firewall.IListFirewallRulesCmd; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.Pair; + +@APICommand(name = "listRoutingFirewallRules", + description = "Lists all Routing firewall rules", + since = "4.20.0", + responseObject = FirewallRuleResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListRoutingFirewallRulesCmd extends BaseListTaggedResourcesCmd implements IListFirewallRulesCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, + description = "Lists Routing firewall rule with the specified ID") + private Long id; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list Routing firewall rules by network ID") + private Long networkId; + + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "list Routing firewall rules by traffic type - ingress or egress") + private String trafficType; + + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", authorized = {RoleType.Admin}) + private Boolean display; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public Long getNetworkId() { + return networkId; + } + + @Override + public Long getId() { + return id; + } + + @Override + public FirewallRule.TrafficType getTrafficType() { + if (trafficType != null) { + return FirewallRule.TrafficType.valueOf(trafficType); + } + return null; + } + + @Override + public Long getIpAddressId() { + return null; + } + + @Override + public Boolean getDisplay() { + if (display != null) { + return display; + } + return super.getDisplay(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + Pair, Integer> result = routedIpv4Manager.listRoutingFirewallRules(this); + ListResponse response = new ListResponse<>(); + List ruleResponses = new ArrayList<>(); + + for (FirewallRule rule : result.first()) { + FirewallResponse ruleData = _responseGenerator.createFirewallResponse(rule); + ruleResponses.add(ruleData); + } + response.setResponses(ruleResponses, result.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmd.java new file mode 100644 index 00000000000..c6f6034b1ba --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmd.java @@ -0,0 +1,125 @@ +// 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.api.command.user.network.routing; + +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.BaseAsyncCustomIdCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; + +@APICommand(name = "updateRoutingFirewallRule", + description = "Updates Routing firewall rule with specified ID", + since = "4.20.0", + responseObject = FirewallRuleResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class UpdateRoutingFirewallRuleCmd extends BaseAsyncCustomIdCmd { + + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the Routing firewall rule") + private Long id; + + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the Routing firewall rule to the end user or not", + authorized = {RoleType.Admin}) + private Boolean display; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + @Override + public boolean isDisplay() { + if (display != null) { + return display; + } else { + return true; + } + } + + public Long getId() { + return id; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + FirewallRule rule = _firewallService.getFirewallRule(id); + if (rule != null) { + return rule.getAccountId(); + } + Account caller = CallContext.current().getCallingAccount(); + return caller.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating ipv4 routing firewall rule"; + } + + @Override + public void execute() throws ResourceUnavailableException { + CallContext.current().setEventDetails("Rule Id: " + getId()); + FirewallRule rule = routedIpv4Manager.updateRoutingFirewallRule(this); + FirewallResponse ruleResponse = _responseGenerator.createFirewallResponse(rule); + setResponseObject(ruleResponse); + ruleResponse.setResponseName(getCommandName()); + } + + @Override + public void checkUuid() { + if (this.getCustomId() != null) { + _uuidMgr.checkUuid(this.getCustomId(), FirewallRule.class); + } + } + + @Override + public Long getApiResourceId() { + FirewallRule rule = _firewallService.getFirewallRule(id); + if (rule != null) { + return rule.getNetworkId(); + } + return null; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Network; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java index 89a65f8c27c..2f62d0d7210 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java @@ -75,10 +75,15 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { private String displayText; - @Parameter(name = ApiConstants.CIDR, type = CommandType.STRING, required = true, description = "the cidr of the VPC. All VPC " + - "guest networks' cidrs should be within this CIDR") + @Parameter(name = ApiConstants.CIDR, type = CommandType.STRING, + description = "the cidr of the VPC. All VPC guest networks' cidrs should be within this CIDR") private String cidr; + @Parameter(name = ApiConstants.CIDR_SIZE, type = CommandType.INTEGER, + description = "the CIDR size of VPC. For regular users, this is required for VPC with ROUTED mode.", + since = "4.20.0") + private Integer cidrSize; + @Parameter(name = ApiConstants.VPC_OFF_ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, required = true, description = "the ID of the VPC offering") private Long vpcOffering; @@ -117,6 +122,9 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { since = "4.19") private String sourceNatIP; + @Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, since = "4.20.0", description="the AS Number of the VPC tiers") + private Long asNumber; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -141,6 +149,10 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { return cidr; } + public Integer getCidrSize() { + return cidrSize; + } + public String getDisplayText() { return StringUtils.isEmpty(displayText) ? vpcName : displayText; } @@ -189,6 +201,10 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { return sourceNatIP; } + public Long getAsNumber() { + return asNumber; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ASNRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ASNRangeResponse.java new file mode 100644 index 00000000000..86dab54ca6b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ASNRangeResponse.java @@ -0,0 +1,93 @@ +// 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.api.response; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; + +@EntityReference(value = ASNumberRange.class) +public class ASNRangeResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the AS Number Range") + private String id; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "Zone ID") + private String zoneId; + + @SerializedName(ApiConstants.START_ASN) + @Param(description = "Start AS Number") + private Long startASNumber; + + @SerializedName(ApiConstants.END_ASN) + @Param(description = "End AS Number") + private Long endASNumber; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Created date") + private Date created; + + public ASNRangeResponse() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public Long getStartASNumber() { + return startASNumber; + } + + public void setStartASNumber(Long startASNumber) { + this.startASNumber = startASNumber; + } + + public Long getEndASNumber() { + return endASNumber; + } + + public void setEndASNumber(Long endASNumber) { + this.endASNumber = endASNumber; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ASNumberResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ASNumberResponse.java new file mode 100644 index 00000000000..45884250984 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ASNumberResponse.java @@ -0,0 +1,237 @@ +// 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.api.response; + +import com.cloud.bgp.ASNumber; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; + +@EntityReference(value = ASNumber.class) +public class ASNumberResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the AS Number") + private String id; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "Account ID") + private String accountId; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account name") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "Domain ID") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name") + private String domainName; + + @SerializedName(ApiConstants.AS_NUMBER) + @Param(description = "AS Number") + private Long asNumber; + + @SerializedName(ApiConstants.ASN_RANGE_ID) + @Param(description = "AS Number ID") + private String asNumberRangeId; + + @SerializedName(ApiConstants.ASN_RANGE) + @Param(description = "AS Number Range") + private String asNumberRange; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "Zone ID") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "the zone name of the AS Number range") + private String zoneName; + + @SerializedName("allocated") + @Param(description = "Allocated Date") + private Date allocated; + + @SerializedName(ApiConstants.ALLOCATION_STATE) + @Param(description = "Allocation state") + private String allocationState; + + @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) + @Param(description = "Network ID") + private String associatedNetworkId; + + @SerializedName(ApiConstants.ASSOCIATED_NETWORK_NAME) + @Param(description = "Network Name") + private String associatedNetworkName; + + @SerializedName((ApiConstants.VPC_ID)) + @Param(description = "VPC ID") + private String vpcId; + + @SerializedName(ApiConstants.VPC_NAME) + @Param(description = "VPC Name") + private String vpcName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Created Date") + private Date created; + + public ASNumberResponse() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getDomainId() { + return domainId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public String getDomainName() { + return domainName; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public Long getAsNumber() { + return asNumber; + } + + public void setAsNumber(Long asNumber) { + this.asNumber = asNumber; + } + + public String getAsNumberRangeId() { + return asNumberRangeId; + } + + public void setAsNumberRangeId(String asNumberRangeId) { + this.asNumberRangeId = asNumberRangeId; + } + + public String getAsNumberRange() { + return asNumberRange; + } + + public void setAsNumberRange(String asNumberRange) { + this.asNumberRange = asNumberRange; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public Date getAllocated() { + return allocated; + } + + public void setAllocated(Date allocatedDate) { + this.allocated = allocatedDate; + } + + public String getAllocationState() { + return allocationState; + } + + public void setAllocationState(String allocated) { + allocationState = allocated; + } + + public String getAssociatedNetworkId() { + return associatedNetworkId; + } + + public void setAssociatedNetworkId(String associatedNetworkId) { + this.associatedNetworkId = associatedNetworkId; + } + + public String getAssociatedNetworkName() { + return associatedNetworkName; + } + + public void setAssociatedNetworkName(String associatedNetworkName) { + this.associatedNetworkName = associatedNetworkName; + } + + public String getVpcId() { + return vpcId; + } + + public void setVpcId(String vpcId) { + this.vpcId = vpcId; + } + + public String getVpcName() { + return vpcName; + } + + public void setVpcName(String vpcName) { + this.vpcName = vpcName; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BgpPeerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BgpPeerResponse.java new file mode 100644 index 00000000000..344e65c6bad --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/BgpPeerResponse.java @@ -0,0 +1,200 @@ +// 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.api.response; + +import java.util.Date; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.network.BgpPeer; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = BgpPeer.class) +public class BgpPeerResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "id of the bgp peer") + private String id; + + @SerializedName(ApiConstants.IP_ADDRESS) + @Param(description = "IPv4 address of bgp peer") + private String ip4Address; + + @SerializedName(ApiConstants.IP6_ADDRESS) + @Param(description = "IPv6 address of bgp peer") + private String ip6Address; + + @SerializedName(ApiConstants.AS_NUMBER) + @Param(description = "AS number of bgp peer") + private Long asNumber; + + @SerializedName(ApiConstants.PASSWORD) + @Param(description = "password of bgp peer") + private String password; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "id of zone to which the bgp peer belongs to." ) + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "name of zone to which the bgp peer belongs to." ) + private String zoneName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "date when this bgp peer was created." ) + private Date created; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account of the bgp peer") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the bgp peer") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the bgp peer") + private String domainName; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project id of the bgp peer") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the bgp peer") + private String projectName; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "additional key/value details of the bgp peer") + private Map details; + + public void setId(String id) { + this.id = id; + } + + public void setIp4Address(String ip4Address) { + this.ip4Address = ip4Address; + } + + public void setIp6Address(String ip6Address) { + this.ip6Address = ip6Address; + } + + public void setAsNumber(Long asNumber) { + this.asNumber = asNumber; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public void setDetails(Map details) { + this.details = details; + } + + public String getId() { + return id; + } + + public String getIp4Address() { + return ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public Long getAsNumber() { + return asNumber; + } + + public String getPassword() { + return password; + } + + public String getZoneId() { + return zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public Date getCreated() { + return created; + } + + public String getAccountName() { + return accountName; + } + + public String getDomainId() { + return domainId; + } + + public String getDomainName() { + return domainName; + } + + public String getProjectId() { + return projectId; + } + + public String getProjectName() { + return projectName; + } + + public Map getDetails() { + return details; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponse.java new file mode 100644 index 00000000000..a1a87794a88 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponse.java @@ -0,0 +1,151 @@ +// 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.api.response; + +import java.util.Date; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = DataCenterIpv4GuestSubnet.class) +public class DataCenterIpv4SubnetResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "id of the guest IPv4 subnet") + private String id; + + @SerializedName(ApiConstants.SUBNET) + @Param(description = "guest IPv4 subnet") + private String subnet; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "id of zone to which the IPv4 subnet belongs to." ) + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "name of zone to which the IPv4 subnet belongs to." ) + private String zoneName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "date when this IPv4 subnet was created." ) + private Date created; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account of the IPv4 subnet") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the IPv4 subnet") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the IPv4 subnet") + private String domainName; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project id of the IPv4 subnet") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the IPv4 subnet") + private String projectName; + + public void setId(String id) { + this.id = id; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public String getId() { + return id; + } + + public String getSubnet() { + return subnet; + } + + public String getZoneId() { + return zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public Date getCreated() { + return created; + } + + public String getAccountName() { + return accountName; + } + + public String getDomainId() { + return domainId; + } + + public String getDomainName() { + return domainName; + } + + public String getProjectId() { + return projectId; + } + + public String getProjectName() { + return projectName; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Ipv4RouteResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4RouteResponse.java new file mode 100644 index 00000000000..136c87971b7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4RouteResponse.java @@ -0,0 +1,59 @@ +// 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.api.response; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class Ipv4RouteResponse extends BaseResponse { + + @SerializedName(ApiConstants.SUBNET) + @Param(description = "the guest Ipv4 cidr for route") + private String subnet; + + @SerializedName(ApiConstants.GATEWAY) + @Param(description = "the outbound Ipv4 gateway") + private String gateway; + + public Ipv4RouteResponse() { + } + + public Ipv4RouteResponse(String subnet, String gateway) { + this.subnet = subnet; + this.gateway = gateway; + } + + public String getSubnet() { + return subnet; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + public String getGateway() { + return gateway; + } + + public void setGateway(String gateway) { + this.gateway = gateway; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponse.java new file mode 100644 index 00000000000..1430bcd059c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponse.java @@ -0,0 +1,199 @@ +// 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.api.response; + +import java.util.Date; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = Ipv4GuestSubnetNetworkMap.class) +public class Ipv4SubnetForGuestNetworkResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "id of the IPv4 subnet for guest network") + private String id; + + @SerializedName(ApiConstants.PARENT_ID) + @Param(description = "id of the data center IPv4 subnet") + private String parentId; + + @SerializedName(ApiConstants.PARENT_SUBNET) + @Param(description = "subnet of the data center IPv4 subnet") + private String parentSubnet; + + @SerializedName(ApiConstants.SUBNET) + @Param(description = "subnet of the IPv4 network") + private String subnet; + + @SerializedName(ApiConstants.STATE) + @Param(description = "state of subnet of the IPv4 network") + private String state; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "id of zone to which the IPv4 subnet belongs to." ) + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "id of zone to which the IPv4 subnet belongs to." ) + private String zoneName; + + @SerializedName(ApiConstants.NETWORK_ID) + @Param(description = "id of network which the IPv4 subnet is associated with." ) + private String networkId; + + @SerializedName(ApiConstants.NETWORK_NAME) + @Param(description = "name of network which the IPv4 subnet is associated with." ) + private String networkName; + + @SerializedName(ApiConstants.VPC_ID) + @Param(description = "Id of the VPC which the IPv4 subnet is associated with.") + private String vpcId; + + @SerializedName(ApiConstants.VPC_NAME) + @Param(description = "Name of the VPC which the IPv4 subnet is associated with.") + private String vpcName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "date when this IPv4 subnet was created." ) + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "date when this IPv4 subnet was removed." ) + private Date removed; + + @SerializedName(ApiConstants.ALLOCATED_TIME) + @Param(description = "date when this IPv4 subnet was allocated." ) + private Date allocatedTime; + + public void setId(String id) { + this.id = id; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public void setParentSubnet(String parentSubnet) { + this.parentSubnet = parentSubnet; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + public void setState(String state) { + this.state = state; + } + + public void setNetworkId(String networkId) { + this.networkId = networkId; + } + + public void setNetworkName(String networkName) { + this.networkName = networkName; + } + + public void setVpcId(String vpcId) { + this.vpcId = vpcId; + } + + public void setVpcName(String vpcName) { + this.vpcName = vpcName; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public void setAllocatedTime(Date allocatedTime) { + this.allocatedTime = allocatedTime; + } + + public String getId() { + return id; + } + + public String getParentId() { + return parentId; + } + + public String getParentSubnet() { + return parentSubnet; + } + + public String getSubnet() { + return subnet; + } + + public String getState() { + return state; + } + + public String getZoneId() { + return zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public String getNetworkId() { + return networkId; + } + + public String getNetworkName() { + return networkName; + } + + public String getVpcId() { + return vpcId; + } + + public String getVpcName() { + return vpcName; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } + + public Date getAllocatedTime() { + return allocatedTime; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java index b73163a5d05..81a8129ecb7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java @@ -107,9 +107,9 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if network offering can be used by Tungsten-Fabric networks only") private Boolean forTungsten; - @SerializedName(ApiConstants.NSX_MODE) - @Param(description = "Mode in which the network will operate. This parameter is only relevant for NSX offerings") - private String nsxMode; + @SerializedName(ApiConstants.NETWORK_MODE) + @Param(description = "Mode in which the network will operate. The valid values are NATTED and ROUTED") + private String networkMode; @SerializedName(ApiConstants.IS_PERSISTENT) @Param(description = "true if network offering supports persistent networks, false otherwise") @@ -159,6 +159,14 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "the internet protocol of the network offering") private String internetProtocol; + @SerializedName(ApiConstants.SPECIFY_AS_NUMBER) + @Param(description = "true if network offering supports choosing AS numbers") + private Boolean specifyAsNumber; + + @SerializedName(ApiConstants.ROUTING_MODE) + @Param(description = "the routing mode for the network offering, supported types are Static or Dynamic.") + private String routingMode; + public void setId(String id) { this.id = id; } @@ -235,8 +243,8 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { this.forTungsten = forTungsten; } - public void setNsxMode(String nsxMode) { - this.nsxMode = nsxMode; + public void setNetworkMode(String networkMode) { + this.networkMode = networkMode; } public void setIsPersistent(Boolean isPersistent) { @@ -306,4 +314,20 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { public void setInternetProtocol(String internetProtocol) { this.internetProtocol = internetProtocol; } + + public Boolean getSpecifyAsNumber() { + return specifyAsNumber; + } + + public void setSpecifyAsNumber(Boolean specifyAsNumber) { + this.specifyAsNumber = specifyAsNumber; + } + + public String getRoutingMode() { + return routingMode; + } + + public void setRoutingMode(String routingMode) { + this.routingMode = routingMode; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java index 1049740bf55..a80317c83cd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.response; import java.util.Date; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -135,6 +136,14 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "The vlan of the network. This parameter is visible to ROOT admins only") private String vlan; + @SerializedName(ApiConstants.AS_NUMBER_ID) + @Param(description = "UUID of AS NUMBER", since = "4.20.0") + private String asNumberId; + + @SerializedName(ApiConstants.AS_NUMBER) + @Param(description = "AS NUMBER", since = "4.20.0") + private Long asNumber; + @SerializedName(ApiConstants.ACL_TYPE) @Param(description = "acl type - access type to the network") private String aclType; @@ -292,7 +301,7 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement private String internetProtocol; @SerializedName(ApiConstants.IPV6_ROUTING) - @Param(description = "The routing mode of network offering", since = "4.17.0") + @Param(description = "The Ipv6 routing type of network offering", since = "4.17.0") private String ipv6Routing; @SerializedName(ApiConstants.IPV6_ROUTES) @@ -315,6 +324,18 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "the second IPv6 DNS for the network", since = "4.18.0") private String ipv6Dns2; + @SerializedName(ApiConstants.IPV4_ROUTING) + @Param(description = "The IPv4 routing type of network", since = "4.20.0") + private String ipv4Routing; + + @SerializedName(ApiConstants.IPV4_ROUTES) + @Param(description = "The routes for the network to ease adding route in upstream router", since = "4.20.0") + private Set ipv4Routes; + + @SerializedName(ApiConstants.BGP_PEERS) + @Param(description = "The BGP peers for the network", since = "4.20.0") + private Set bgpPeers; + public NetworkResponse() {} public Boolean getDisplayNetwork() { @@ -415,6 +436,14 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement this.vlan = vlan; } + public void setAsNumber(long asNumber) { + this.asNumber = asNumber; + } + + public void setAsNumberId(String asNumberId) { + this.asNumberId = asNumberId; + } + public void setIsSystem(Boolean isSystem) { this.isSystem = isSystem; } @@ -624,6 +653,18 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement this.internetProtocol = internetProtocol; } + public void setIpv4Routing(String ipv4Routing) { + this.ipv4Routing = ipv4Routing; + } + + public void setIpv4Routes(Set ipv4Routes) { + this.ipv4Routes = ipv4Routes; + } + + public void addIpv4Route(Ipv4RouteResponse ipv4Route) { + this.ipv4Routes.add(ipv4Route); + } + public void setIpv6Routing(String ipv6Routing) { this.ipv6Routing = ipv6Routing; } @@ -636,6 +677,17 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement this.ipv6Routes.add(ipv6Route); } + public void setBgpPeers(Set bgpPeers) { + this.bgpPeers = bgpPeers; + } + + public void addBgpPeer(BgpPeerResponse bgpPeer) { + if (this.bgpPeers == null) { + this.setBgpPeers(new LinkedHashSet<>()); + } + this.bgpPeers.add(bgpPeer); + } + public Integer getPublicMtu() { return publicMtu; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java index ce00827f06d..b11764da7d9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java @@ -70,9 +70,9 @@ public class VpcOfferingResponse extends BaseResponse { @Param(description = "true if vpc offering can be used by NSX networks only") private Boolean forNsx; - @SerializedName(ApiConstants.NSX_MODE) - @Param(description = "Mode in which the network will operate. This parameter is only relevant for NSX offerings") - private String nsxMode; + @SerializedName(ApiConstants.NETWORK_MODE) + @Param(description = "Mode in which the network will operate. The valid values are NATTED and ROUTED") + private String networkMode; @SerializedName(ApiConstants.DOMAIN_ID) @Param(description = "the domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") @@ -94,6 +94,14 @@ public class VpcOfferingResponse extends BaseResponse { @Param(description = "the internet protocol of the vpc offering") private String internetProtocol; + @SerializedName(ApiConstants.SPECIFY_AS_NUMBER) + @Param(description = "true if network offering supports choosing AS numbers") + private Boolean specifyAsNumber; + + @SerializedName(ApiConstants.ROUTING_MODE) + @Param(description = "the routing mode for the network offering, supported types are Static or Dynamic.") + private String routingMode; + public void setId(String id) { this.id = id; } @@ -150,8 +158,8 @@ public class VpcOfferingResponse extends BaseResponse { this.forNsx = forNsx; } - public void setNsxMode(String nsxMode) { - this.nsxMode = nsxMode; + public void setNetworkMode(String networkMode) { + this.networkMode = networkMode; } public String getZoneId() { @@ -177,4 +185,20 @@ public class VpcOfferingResponse extends BaseResponse { public void setInternetProtocol(String internetProtocol) { this.internetProtocol = internetProtocol; } + + public Boolean getSpecifyAsNumber() { + return specifyAsNumber; + } + + public void setSpecifyAsNumber(Boolean specifyAsNumber) { + this.specifyAsNumber = specifyAsNumber; + } + + public String getRoutingMode() { + return routingMode; + } + + public void setRoutingMode(String routingMode) { + this.routingMode = routingMode; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java index 6f91da7d2d7..56479506686 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.response; import java.util.Date; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -160,6 +161,26 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll @Param(description = "the second IPv6 DNS for the VPC", since = "4.18.0") private String ipv6Dns2; + @SerializedName(ApiConstants.IPV4_ROUTING) + @Param(description = "The IPv4 routing mode of VPC", since = "4.20.0") + private String ipv4Routing; + + @SerializedName(ApiConstants.IPV4_ROUTES) + @Param(description = "The routes for the VPC to ease adding route in upstream router", since = "4.20.0") + private Set ipv4Routes; + + @SerializedName(ApiConstants.AS_NUMBER_ID) + @Param(description = "UUID of AS NUMBER", since = "4.20.0") + private String asNumberId; + + @SerializedName(ApiConstants.AS_NUMBER) + @Param(description = "AS NUMBER", since = "4.20.0") + private Long asNumber; + + @SerializedName(ApiConstants.BGP_PEERS) + @Param(description = "The BGP peers for the VPC", since = "4.20.0") + private Set bgpPeers; + public void setId(final String id) { this.id = id; } @@ -279,6 +300,18 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll this.icon = icon; } + public void setIpv4Routing(String ipv4Routing) { + this.ipv4Routing = ipv4Routing; + } + + public void setIpv4Routes(Set ipv4Routes) { + this.ipv4Routes = ipv4Routes; + } + + public void addIpv4Route(Ipv4RouteResponse ipv4Route) { + this.ipv4Routes.add(ipv4Route); + } + public void setIpv6Routes(Set ipv6Routes) { this.ipv6Routes = ipv6Routes; } @@ -306,4 +339,23 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll public void setIpv6Dns2(String ipv6Dns2) { this.ipv6Dns2 = ipv6Dns2; } + + public void setAsNumber(long asNumber) { + this.asNumber = asNumber; + } + + public void setAsNumberId(String asNumberId) { + this.asNumberId = asNumberId; + } + + public void setBgpPeers(Set bgpPeers) { + this.bgpPeers = bgpPeers; + } + + public void addBgpPeer(BgpPeerResponse bgpPeer) { + if (this.bgpPeers == null) { + this.setBgpPeers(new LinkedHashSet<>()); + } + this.bgpPeers.add(bgpPeer); + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java index ff43fb697b5..8a4c41f2957 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java @@ -149,6 +149,10 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @Param(description = "true, if zone is NSX enabled", since = "4.20.0") private boolean nsxEnabled = false; + @SerializedName(ApiConstants.ASN_RANGE) + @Param(description = "AS Number Range") + private String asnRange; + public ZoneResponse() { tags = new LinkedHashSet(); } @@ -392,4 +396,12 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso public void setNsxEnabled(boolean nsxEnabled) { this.nsxEnabled = nsxEnabled; } + + public void setAsnRange(String asnRange) { + this.asnRange = asnRange; + } + + public String getAsnRange() { + return asnRange; + } } diff --git a/api/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnet.java b/api/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnet.java new file mode 100644 index 00000000000..90d55cc5751 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnet.java @@ -0,0 +1,36 @@ +// 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.datacenter; + +import java.util.Date; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface DataCenterIpv4GuestSubnet extends InfrastructureEntity, InternalIdentity, Identity { + Long getDataCenterId(); + + String getSubnet(); + + Long getDomainId(); + + Long getAccountId(); + + Date getCreated(); +} diff --git a/api/src/main/java/org/apache/cloudstack/network/BgpPeer.java b/api/src/main/java/org/apache/cloudstack/network/BgpPeer.java new file mode 100644 index 00000000000..e1d7eca0a03 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/network/BgpPeer.java @@ -0,0 +1,50 @@ +// 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.network; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface BgpPeer extends Identity, InternalIdentity { + + Long getDomainId(); + + Long getAccountId(); + + enum State { + Active, Add, Revoke + } + + enum Detail { + EBGP_MultiHop + } + + long getDataCenterId(); + + String getIp4Address(); + + String getIp6Address(); + + Long getAsNumber(); + + String getPassword(); + + Date getCreated(); +} diff --git a/api/src/main/java/org/apache/cloudstack/network/BgpPeerTO.java b/api/src/main/java/org/apache/cloudstack/network/BgpPeerTO.java new file mode 100644 index 00000000000..b0503314616 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/network/BgpPeerTO.java @@ -0,0 +1,91 @@ +// 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.network; + +import java.util.Map; + +public class BgpPeerTO { + Long peerId; + Long peerAsNumber; + String ip4Address; + String ip6Address; + String peerPassword; + Long networkId; + Long networkAsNumber; + String guestIp4Cidr; + String guestIp6Cidr; + + Map details; + + public BgpPeerTO(Long peerId, String ip4Address, String ip6Address, Long peerAsNumber, String peerPassword, + Long networkId, Long networkAsNumber, String guestIp4Cidr, String guestIp6Cidr, Map details) { + this.peerId = peerId; + this.ip4Address = ip4Address; + this.ip6Address = ip6Address; + this.peerAsNumber = peerAsNumber; + this.peerPassword = peerPassword; + this.networkId = networkId; + this.networkAsNumber = networkAsNumber; + this.guestIp4Cidr = guestIp4Cidr; + this.guestIp6Cidr = guestIp6Cidr; + this.details = details; + } + + public BgpPeerTO(Long networkId) { + this.networkId = networkId; + } + + public Long getPeerId() { + return peerId; + } + + public String getIp4Address() { + return ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public Long getPeerAsNumber() { + return peerAsNumber; + } + + public String getPeerPassword() { + return peerPassword; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getNetworkAsNumber() { + return networkAsNumber; + } + + public String getGuestIp4Cidr() { + return guestIp4Cidr; + } + + public String getGuestIp6Cidr() { + return guestIp6Cidr; + } + + public Map getDetails() { + return details; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMap.java b/api/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMap.java new file mode 100644 index 00000000000..569ed22c164 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMap.java @@ -0,0 +1,47 @@ +// 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.network; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface Ipv4GuestSubnetNetworkMap extends Identity, InternalIdentity { + Date getAllocated(); + + Date getCreated(); + + enum State { + Allocating, // The subnet will be assigned to a network + Allocated, // The subnet is in use. + Releasing, // The subnet is being released. + Free // The subnet is ready to be allocated. + } + + Long getParentId(); + + String getSubnet(); + + Long getVpcId(); + + Long getNetworkId(); + + State getState(); + +} diff --git a/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java b/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java new file mode 100644 index 00000000000..2f704e9f47d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java @@ -0,0 +1,199 @@ +// 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.network; + +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcOffering; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.component.PluggableService; + +import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.DedicateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteIpv4SubnetForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.ListIpv4SubnetsForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.ListIpv4SubnetsForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.ReleaseDedicatedIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.UpdateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ChangeBgpPeersForNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ChangeBgpPeersForVpcCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.CreateBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.DedicateBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.DeleteBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ListBgpPeersCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ReleaseDedicatedBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.UpdateBgpPeerCmd; +import org.apache.cloudstack.api.command.user.network.routing.CreateRoutingFirewallRuleCmd; +import org.apache.cloudstack.api.command.user.network.routing.ListRoutingFirewallRulesCmd; +import org.apache.cloudstack.api.command.user.network.routing.UpdateRoutingFirewallRuleCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; + +import java.util.List; + +public interface RoutedIpv4Manager extends PluggableService, Configurable { + + ConfigKey RoutedNetworkIPv4MaxCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class, + "routed.network.ipv4.max.cidr.size", "30", "The maximum value of the cidr size for isolated networks in ROUTED mode", + true, ConfigKey.Scope.Account); + + ConfigKey RoutedNetworkIPv4MinCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class, + "routed.network.ipv4.min.cidr.size", "24", "The minimum value of the cidr size for isolated networks in ROUTED mode", + true, ConfigKey.Scope.Account); + + ConfigKey RoutedVpcIPv4MaxCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class, + "routed.ipv4.vpc.max.cidr.size", "28", "The maximum value of the cidr size for VPC in ROUTED mode", + true, ConfigKey.Scope.Account); + + ConfigKey RoutedVpcIPv4MinCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class, + "routed.ipv4.vpc.min.cidr.size", "22", "The minimum value of the cidr size for VPC in ROUTED mode", + true, ConfigKey.Scope.Account); + + ConfigKey RoutedIPv4NetworkCidrAutoAllocationEnabled = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Boolean.class, + "routed.ipv4.network.cidr.auto.allocation.enabled", + "true", + "Indicates whether the auto-allocation of network CIDR for routed network is enabled or not.", + true, + ConfigKey.Scope.Account); + + ConfigKey UseSystemBgpPeers = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Boolean.class, + "use.system.bgp.peers", + "true", + "If true, when account has dedicated bgp peers(s), the guest networks with dynamic routing will use both system and dedicated bgp peers. If false, only dedicated bgp peers will be used.", + true, + ConfigKey.Scope.Account); + + // Methods for DataCenterIpv4GuestSubnet APIs + DataCenterIpv4GuestSubnet createDataCenterIpv4GuestSubnet(CreateIpv4SubnetForZoneCmd createIpv4SubnetForZoneCmd); + + DataCenterIpv4SubnetResponse createDataCenterIpv4SubnetResponse(DataCenterIpv4GuestSubnet result); + + boolean deleteDataCenterIpv4GuestSubnet(DeleteIpv4SubnetForZoneCmd deleteIpv4SubnetForZoneCmd); + + DataCenterIpv4GuestSubnet updateDataCenterIpv4GuestSubnet(UpdateIpv4SubnetForZoneCmd updateIpv4SubnetForZoneCmd); + + List listDataCenterIpv4GuestSubnets(ListIpv4SubnetsForZoneCmd listIpv4SubnetsForZoneCmd); + + DataCenterIpv4GuestSubnet dedicateDataCenterIpv4GuestSubnet(DedicateIpv4SubnetForZoneCmd dedicateIpv4SubnetForZoneCmd); + + DataCenterIpv4GuestSubnet releaseDedicatedDataCenterIpv4GuestSubnet(ReleaseDedicatedIpv4SubnetForZoneCmd releaseDedicatedIpv4SubnetForZoneCmd); + + // Methods for Ipv4SubnetForGuestNetwork APIs + Ipv4GuestSubnetNetworkMap createIpv4SubnetForGuestNetwork(CreateIpv4SubnetForGuestNetworkCmd createIpv4SubnetForGuestNetworkCmd); + + boolean deleteIpv4SubnetForGuestNetwork(DeleteIpv4SubnetForGuestNetworkCmd deleteIpv4SubnetForGuestNetworkCmd); + + void releaseIpv4SubnetForGuestNetwork(long networkId); + + void releaseIpv4SubnetForVpc(long vpcId); + + List listIpv4GuestSubnetsForGuestNetwork(ListIpv4SubnetsForGuestNetworkCmd listIpv4SubnetsForGuestNetworkCmd); + + Ipv4SubnetForGuestNetworkResponse createIpv4SubnetForGuestNetworkResponse(Ipv4GuestSubnetNetworkMap subnet); + + // Methods for internal calls + void getOrCreateIpv4SubnetForGuestNetwork(Network network, String networkCidr); + + Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetwork(Long domainId, Long accountId, Long zoneId, Integer networkCidrSize); + + void getOrCreateIpv4SubnetForVpc(Vpc vpc, String networkCidr); + + Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForVpc(Vpc vpc, Integer vpcCidrSize); + + void assignIpv4SubnetToNetwork(Network network); + + void assignIpv4SubnetToVpc(Vpc vpc); + + // Methods for Routing firewall rules + FirewallRule createRoutingFirewallRule(CreateRoutingFirewallRuleCmd createRoutingFirewallRuleCmd) throws NetworkRuleConflictException; + + Pair, Integer> listRoutingFirewallRules(ListRoutingFirewallRulesCmd listRoutingFirewallRulesCmd); + + FirewallRule updateRoutingFirewallRule(UpdateRoutingFirewallRuleCmd updateRoutingFirewallRuleCmd); + + boolean revokeRoutingFirewallRule(Long id); + + boolean applyRoutingFirewallRule(long id); + + boolean isVirtualRouterGateway(Network network); + + boolean isVirtualRouterGateway(NetworkOffering networkOffering); + + boolean isRoutedNetwork(Network network); + + boolean isDynamicRoutedNetwork(Network network); + + boolean isDynamicRoutedNetwork(NetworkOffering networkOffering); + + boolean isRoutedVpc(Vpc vpc); + + boolean isVpcVirtualRouterGateway(VpcOffering vpcOffering); + + BgpPeer createBgpPeer(CreateBgpPeerCmd createBgpPeerCmd); + + BgpPeerResponse createBgpPeerResponse(BgpPeer result); + + boolean deleteBgpPeer(DeleteBgpPeerCmd deleteBgpPeerCmd); + + BgpPeer updateBgpPeer(UpdateBgpPeerCmd updateBgpPeerCmd); + + BgpPeer dedicateBgpPeer(DedicateBgpPeerCmd dedicateBgpPeerCmd); + + BgpPeer releaseDedicatedBgpPeer(ReleaseDedicatedBgpPeerCmd releaseDedicatedBgpPeerCmd); + + List listBgpPeers(ListBgpPeersCmd listBgpPeersCmd); + + Network changeBgpPeersForNetwork(ChangeBgpPeersForNetworkCmd changeBgpPeersForNetworkCmd); + + Network removeBgpPeersFromNetwork(Network network); + + void validateBgpPeers(Account owner, Long zoneId, List bgpPeerIds); + + void persistBgpPeersForGuestNetwork(long networkId, List bgpPeerIds); + + void releaseBgpPeersForGuestNetwork(long networkId); + + boolean isDynamicRoutedVpc(Vpc vpc); + + boolean isDynamicRoutedVpc(VpcOffering vpcOff); + + void persistBgpPeersForVpc(long vpcId, List bgpPeerIds); + + void releaseBgpPeersForVpc(long vpcId); + + Vpc changeBgpPeersForVpc(ChangeBgpPeersForVpcCmd changeBgpPeersForVpcCmd); + + List getBgpPeerIdsForAccount(Account owner, long zoneIdd); + + void removeIpv4SubnetsForZoneByAccountId(long accountId); + + void removeIpv4SubnetsForZoneByDomainId(long domainId); + + void removeBgpPeersByAccountId(long accountId); + + void removeBgpPeersByDomainId(long domainId); +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmdTest.java new file mode 100644 index 00000000000..603cda2040d --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmdTest.java @@ -0,0 +1,69 @@ +// 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.api.command.admin.bgp; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.bgp.BGPService; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class CreateASNRangeCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testCreateASNRangeCmd() { + Long zoneId = 1L; + Long startASNumber = 110000L; + Long endASNumber = 120000L; + + CreateASNRangeCmd cmd = new CreateASNRangeCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "startASNumber", startASNumber); + ReflectionTestUtils.setField(cmd, "endASNumber", endASNumber); + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(startASNumber, cmd.getStartASNumber()); + Assert.assertEquals(endASNumber, cmd.getEndASNumber()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + + ASNumberRange asnRange = Mockito.mock(ASNumberRange.class); + Mockito.when(bgpService.createASNumberRange(zoneId, startASNumber, endASNumber)).thenReturn(asnRange); + + ASNRangeResponse response = Mockito.mock(ASNRangeResponse.class); + Mockito.when(_responseGenerator.createASNumberRangeResponse(asnRange)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmdTest.java new file mode 100644 index 00000000000..2abcf736c5b --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmdTest.java @@ -0,0 +1,55 @@ +// 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.api.command.admin.bgp; + +import com.cloud.bgp.BGPService; + +import org.apache.cloudstack.api.response.SuccessResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteASNRangeCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + + @Test + public void testDeleteASNRangeCmd() { + Long id = 200L; + + DeleteASNRangeCmd cmd = new DeleteASNRangeCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + + Assert.assertEquals(id, cmd.getId()); + + Mockito.when(bgpService.deleteASRange(id)).thenReturn(true); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof SuccessResponse); + + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmdTest.java new file mode 100644 index 00000000000..7f49c61a693 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmdTest.java @@ -0,0 +1,75 @@ +// 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.api.command.admin.bgp; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.bgp.BGPService; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListASNRangesCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testListASNRangesCmdTest() { + Long zoneId = 1L; + + ListASNRangesCmd cmd = new ListASNRangesCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + + List ranges = new ArrayList<>(); + ASNumberRange asnRange = Mockito.mock(ASNumberRange.class); + ranges.add(asnRange); + + ASNRangeResponse asnRangeResponse = Mockito.mock(ASNRangeResponse.class); + Mockito.when(_responseGenerator.createASNumberRangeResponse(asnRange)).thenReturn(asnRangeResponse); + + Mockito.when(bgpService.listASNumberRanges(zoneId)).thenReturn(ranges); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof ListResponse); + ListResponse listResponse = (ListResponse) response; + Assert.assertEquals(1L, (long) listResponse.getCount()); + Assert.assertTrue(listResponse.getResponses().get(0) instanceof ASNRangeResponse); + ASNRangeResponse firstResponse = (ASNRangeResponse) listResponse.getResponses().get(0); + Assert.assertEquals(asnRangeResponse, firstResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmdTest.java new file mode 100644 index 00000000000..1b80e5bff7f --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmdTest.java @@ -0,0 +1,61 @@ +// 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.api.command.admin.bgp; + +import com.cloud.bgp.BGPService; +import com.cloud.utils.Pair; + +import org.apache.cloudstack.api.response.SuccessResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ReleaseASNumberCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + + @Test + public void testReleaseASNumberCmd() { + Long zoneId = 1L; + Long asNumber = 10000L; + + ReleaseASNumberCmd cmd = new ReleaseASNumberCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "asNumber", asNumber); + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(asNumber, cmd.getAsNumber()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + + Pair resultPair = Mockito.mock(Pair.class); + Mockito.when(resultPair.first()).thenReturn(true); + Mockito.when(bgpService.releaseASNumber(zoneId, asNumber, false)).thenReturn(resultPair); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java new file mode 100644 index 00000000000..e1393e31699 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java @@ -0,0 +1,69 @@ +// 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.api.command.admin.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class CreateIpv4SubnetForGuestNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateIpv4SubnetForGuestNetworkCmd() { + Long parentId = 1L; + String subnet = "192.168.1.0/24"; + Integer cidrSize = 26; + + CreateIpv4SubnetForGuestNetworkCmd cmd = new CreateIpv4SubnetForGuestNetworkCmd(); + ReflectionTestUtils.setField(cmd, "parentId", parentId); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + ReflectionTestUtils.setField(cmd, "cidrSize", cidrSize); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(parentId, cmd.getParentId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(cidrSize, cmd.getCidrSize()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_IP4_GUEST_SUBNET_CREATE, cmd.getEventType()); + Assert.assertEquals(String.format("Creating guest IPv4 subnet %s in zone subnet=%s", subnet, parentId), cmd.getEventDescription()); + + Ipv4GuestSubnetNetworkMap ipv4GuestSubnetNetworkMap = Mockito.mock(Ipv4GuestSubnetNetworkMap.class); + Mockito.when(routedIpv4Manager.createIpv4SubnetForGuestNetwork(cmd)).thenReturn(ipv4GuestSubnetNetworkMap); + + Ipv4SubnetForGuestNetworkResponse response = Mockito.mock(Ipv4SubnetForGuestNetworkResponse.class); + Mockito.when(routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(ipv4GuestSubnetNetworkMap)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.java new file mode 100644 index 00000000000..51c1eb986c4 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.java @@ -0,0 +1,75 @@ +// 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.api.command.admin.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class CreateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateIpv4SubnetForZoneCmd() { + Long zoneId = 1L; + String subnet = "192.168.1.0/24"; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + CreateIpv4SubnetForZoneCmd cmd = new CreateIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + ReflectionTestUtils.setField(cmd,"domainId", domainId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_CREATE, cmd.getEventType()); + Assert.assertEquals(String.format("Creating guest IPv4 subnet %s for zone=%s", subnet, zoneId), cmd.getEventDescription()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.java new file mode 100644 index 00000000000..7db77098b23 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.java @@ -0,0 +1,72 @@ +// 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.api.command.admin.network; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class DedicateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDedicateIpv4SubnetForZoneCmd() { + Long id = 1L; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + DedicateIpv4SubnetForZoneCmd cmd = new DedicateIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + ReflectionTestUtils.setField(cmd,"domainId", domainId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_DEDICATE, cmd.getEventType()); + Assert.assertEquals(String.format("Dedicating zone IPv4 subnet %s", id), cmd.getEventDescription()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + Mockito.when(routedIpv4Manager.dedicateDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.java new file mode 100644 index 00000000000..a4af5ddf748 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.java @@ -0,0 +1,58 @@ +// 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.api.command.admin.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteIpv4SubnetForGuestNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteIpv4SubnetForGuestNetworkCmd() { + Long id = 1L; + + DeleteIpv4SubnetForGuestNetworkCmd cmd = new DeleteIpv4SubnetForGuestNetworkCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_IP4_GUEST_SUBNET_DELETE, cmd.getEventType()); + Assert.assertEquals(String.format("Deleting guest IPv4 subnet %s", id), cmd.getEventDescription()); + + Mockito.when(routedIpv4Manager.deleteIpv4SubnetForGuestNetwork(cmd)).thenReturn(true); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.java new file mode 100644 index 00000000000..7af173f09d9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.java @@ -0,0 +1,58 @@ +// 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.api.command.admin.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteIpv4SubnetForZoneCmd() { + Long id = 1L; + + DeleteIpv4SubnetForZoneCmd cmd = new DeleteIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_DELETE, cmd.getEventType()); + Assert.assertEquals(String.format("Deleting zone IPv4 subnet %s", id), cmd.getEventDescription()); + + Mockito.when(routedIpv4Manager.deleteDataCenterIpv4GuestSubnet(cmd)).thenReturn(true); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmdTest.java new file mode 100644 index 00000000000..cbfe34f774a --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmdTest.java @@ -0,0 +1,83 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListIpv4SubnetsForGuestNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testListIpv4SubnetsForGuestNetworkCmd() { + Long id = 1L; + Long zoneId = 2L; + Long parentId = 2L; + String subnet = "192.168.1.0/24"; + Long networkId = 10L; + Long vpcId = 11L; + + ListIpv4SubnetsForGuestNetworkCmd cmd = new ListIpv4SubnetsForGuestNetworkCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + ReflectionTestUtils.setField(cmd, "parentId", parentId); + ReflectionTestUtils.setField(cmd,"networkId", networkId); + ReflectionTestUtils.setField(cmd,"vpcId", vpcId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(networkId, cmd.getNetworkId()); + Assert.assertEquals(vpcId, cmd.getVpcId()); + Assert.assertEquals(parentId, cmd.getParentId()); + + Assert.assertEquals(0L, cmd.getEntityOwnerId()); + + Ipv4GuestSubnetNetworkMap ipv4GuestSubnetNetworkMap = Mockito.mock(Ipv4GuestSubnetNetworkMap.class); + List ipv4GuestSubnetNetworkMaps = Arrays.asList(ipv4GuestSubnetNetworkMap); + Mockito.when(routedIpv4Manager.listIpv4GuestSubnetsForGuestNetwork(cmd)).thenReturn(ipv4GuestSubnetNetworkMaps); + + Ipv4SubnetForGuestNetworkResponse response = Mockito.mock(Ipv4SubnetForGuestNetworkResponse.class); + Mockito.when(routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(ipv4GuestSubnetNetworkMap)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof ListResponse); + ListResponse listResponse = (ListResponse) cmd.getResponseObject(); + Assert.assertEquals(1, (int) listResponse.getCount()); + Assert.assertEquals(response, listResponse.getResponses().get(0)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmdTest.java new file mode 100644 index 00000000000..2c7a246f70f --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmdTest.java @@ -0,0 +1,83 @@ +// 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.api.command.admin.network; + +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListIpv4SubnetsForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testListIpv4SubnetsForZoneCmd() { + Long id = 1L; + Long zoneId = 2L; + String subnet = "192.168.1.0/24"; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + ListIpv4SubnetsForZoneCmd cmd = new ListIpv4SubnetsForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + ReflectionTestUtils.setField(cmd,"domainId", domainId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(0L, cmd.getEntityOwnerId()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + List zoneSubnets = Arrays.asList(zoneSubnet); + Mockito.when(routedIpv4Manager.listDataCenterIpv4GuestSubnets(cmd)).thenReturn(zoneSubnets); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof ListResponse); + ListResponse listResponse = (ListResponse) cmd.getResponseObject(); + Assert.assertEquals(1, (int) listResponse.getCount()); + Assert.assertEquals(response, listResponse.getResponses().get(0)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.java new file mode 100644 index 00000000000..9ce9a4f9464 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.java @@ -0,0 +1,62 @@ +// 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.api.command.admin.network; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ReleaseDedicatedIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testReleaseDedicatedIpv4SubnetForZoneCmd() { + Long id = 1L; + + ReleaseDedicatedIpv4SubnetForZoneCmd cmd = new ReleaseDedicatedIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_RELEASE, cmd.getEventType()); + Assert.assertEquals(String.format("Releasing a dedicated zone IPv4 subnet %s", id), cmd.getEventDescription()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + Mockito.when(routedIpv4Manager.releaseDedicatedDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.java new file mode 100644 index 00000000000..cdb9cce22d8 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.java @@ -0,0 +1,66 @@ +// 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.api.command.admin.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testUpdateIpv4SubnetForZoneCmd() { + Long id = 1L; + String subnet = "192.168.1.0/24"; + + UpdateIpv4SubnetForZoneCmd cmd = new UpdateIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_UPDATE, cmd.getEventType()); + Assert.assertEquals(String.format("Updating zone IPv4 subnet %s", id), cmd.getEventDescription()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + Mockito.when(routedIpv4Manager.updateDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.java new file mode 100644 index 00000000000..28ddad17afe --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.java @@ -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.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; + +import com.cloud.network.Network; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ChangeBgpPeersForNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testChangeBgpPeersForNetworkCmd() { + Long networkId = 10L; + List bgpPeerIds = Arrays.asList(20L, 21L); + + ChangeBgpPeersForNetworkCmd cmd = new ChangeBgpPeersForNetworkCmd(); + ReflectionTestUtils.setField(cmd, "networkId", networkId); + ReflectionTestUtils.setField(cmd, "bgpPeerIds", bgpPeerIds); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + + Assert.assertEquals(networkId, cmd.getNetworkId()); + Assert.assertEquals(bgpPeerIds, cmd.getBgpPeerIds()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_NETWORK_BGP_PEER_UPDATE, cmd.getEventType()); + Assert.assertEquals(String.format("Changing Bgp Peers for network %s", networkId), cmd.getEventDescription()); + + Network network = Mockito.mock(Network.class); + Mockito.when(routedIpv4Manager.changeBgpPeersForNetwork(cmd)).thenReturn(network); + + NetworkResponse response = Mockito.mock(NetworkResponse.class); + Mockito.when(_responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Full, network)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.java new file mode 100644 index 00000000000..96eb1f020de --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.java @@ -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.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; + +import com.cloud.network.vpc.Vpc; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ChangeBgpPeersForVpcCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testChangeBgpPeersForVpcCmd() { + Long VpcId = 10L; + List bgpPeerIds = Arrays.asList(20L, 21L); + + ChangeBgpPeersForVpcCmd cmd = new ChangeBgpPeersForVpcCmd(); + ReflectionTestUtils.setField(cmd, "vpcId", VpcId); + ReflectionTestUtils.setField(cmd, "bgpPeerIds", bgpPeerIds); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + + Assert.assertEquals(VpcId, cmd.getVpcId()); + Assert.assertEquals(bgpPeerIds, cmd.getBgpPeerIds()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_VPC_BGP_PEER_UPDATE, cmd.getEventType()); + Assert.assertEquals(String.format("Changing Bgp Peers for VPC %s", VpcId), cmd.getEventDescription()); + + Vpc Vpc = Mockito.mock(Vpc.class); + Mockito.when(routedIpv4Manager.changeBgpPeersForVpc(cmd)).thenReturn(Vpc); + + VpcResponse response = Mockito.mock(VpcResponse.class); + Mockito.when(_responseGenerator.createVpcResponse(ResponseObject.ResponseView.Full, Vpc)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java new file mode 100644 index 00000000000..0d802bf3619 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java @@ -0,0 +1,85 @@ +// 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.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class CreateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateBgpPeerCmd() { + Long zoneId = 1L; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + String ip4Address = "ip4-address"; + String ip6Address = "ip6-address"; + Long peerAsNumber = 15000L; + String peerPassword = "peer-password"; + + CreateBgpPeerCmd cmd = new CreateBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "ip4Address", ip4Address); + ReflectionTestUtils.setField(cmd, "ip6Address", ip6Address); + ReflectionTestUtils.setField(cmd, "asNumber", peerAsNumber); + ReflectionTestUtils.setField(cmd, "password", peerPassword); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + ReflectionTestUtils.setField(cmd,"domainId", domainId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(ip4Address, cmd.getIp4Address()); + Assert.assertEquals(ip6Address, cmd.getIp6Address()); + Assert.assertEquals(peerAsNumber, cmd.getAsNumber()); + Assert.assertEquals(peerPassword, cmd.getPassword()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_CREATE, cmd.getEventType()); + Assert.assertEquals(String.format("Creating Bgp Peer %s for zone=%s", peerAsNumber, zoneId), cmd.getEventDescription()); + + BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); + Mockito.when(routedIpv4Manager.createBgpPeer(cmd)).thenReturn(bgpPeer); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.java new file mode 100644 index 00000000000..f3ae007da28 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.java @@ -0,0 +1,72 @@ +// 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.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class DedicateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDedicateBgpPeerCmd() { + Long id = 1L; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + DedicateBgpPeerCmd cmd = new DedicateBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + ReflectionTestUtils.setField(cmd,"domainId", domainId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_DEDICATE, cmd.getEventType()); + Assert.assertEquals(String.format("Dedicating Bgp Peer %s", id), cmd.getEventDescription()); + + BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); + Mockito.when(routedIpv4Manager.dedicateBgpPeer(cmd)).thenReturn(bgpPeer); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.java new file mode 100644 index 00000000000..5e747188fda --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.java @@ -0,0 +1,58 @@ +// 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.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteBgpPeerCmd() { + Long id = 1L; + + DeleteBgpPeerCmd cmd = new DeleteBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_DELETE, cmd.getEventType()); + Assert.assertEquals(String.format("Deleting Bgp Peer %s", id), cmd.getEventDescription()); + + Mockito.when(routedIpv4Manager.deleteBgpPeer(cmd)).thenReturn(true); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmdTest.java new file mode 100644 index 00000000000..cb2027951ad --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmdTest.java @@ -0,0 +1,96 @@ +// 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.api.command.admin.network.bgp; + +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListBgpPeersCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testIsDedicated() { + ListBgpPeersCmd cmd = new ListBgpPeersCmd(); + + Assert.assertNull(cmd.getDedicated()); + + ReflectionTestUtils.setField(cmd, "isDedicated", Boolean.TRUE); + Assert.assertTrue(cmd.getDedicated()); + + ReflectionTestUtils.setField(cmd, "isDedicated", Boolean.FALSE); + Assert.assertFalse(cmd.getDedicated()); + } + + @Test + public void testListBgpPeersCmd() { + Long id = 1L; + Long zoneId = 2L; + Long peerAsNumber = 15000L; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + ListBgpPeersCmd cmd = new ListBgpPeersCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "asNumber", peerAsNumber); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + ReflectionTestUtils.setField(cmd,"domainId", domainId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(peerAsNumber, cmd.getAsNumber()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(0L, cmd.getEntityOwnerId()); + + BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); + List bgpPeers = Arrays.asList(bgpPeer); + Mockito.when(routedIpv4Manager.listBgpPeers(cmd)).thenReturn(bgpPeers); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof ListResponse); + ListResponse listResponse = (ListResponse) cmd.getResponseObject(); + Assert.assertEquals(1, (int) listResponse.getCount()); + Assert.assertEquals(response, listResponse.getResponses().get(0)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.java new file mode 100644 index 00000000000..8c55c4a7347 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.java @@ -0,0 +1,62 @@ +// 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.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ReleaseDedicatedBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testReleaseDedicatedBgpPeerCmd() { + Long id = 1L; + + ReleaseDedicatedBgpPeerCmd cmd = new ReleaseDedicatedBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_RELEASE, cmd.getEventType()); + Assert.assertEquals(String.format("Releasing a dedicated Bgp Peer %s", id), cmd.getEventDescription()); + + BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); + Mockito.when(routedIpv4Manager.releaseDedicatedBgpPeer(cmd)).thenReturn(bgpPeer); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.java new file mode 100644 index 00000000000..003944c6147 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.java @@ -0,0 +1,87 @@ +// 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.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testUpdateBgpPeerCmd() { + Long id = 1L; + String ip4Address = "ip4-address"; + String ip6Address = "ip6-address"; + Long peerAsNumber = 15000L; + String peerPassword = "peer-password"; + + UpdateBgpPeerCmd cmd = new UpdateBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "ip4Address", ip4Address); + ReflectionTestUtils.setField(cmd, "ip6Address", ip6Address); + ReflectionTestUtils.setField(cmd, "asNumber", peerAsNumber); + ReflectionTestUtils.setField(cmd, "password", peerPassword); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(ip4Address, cmd.getIp4Address()); + Assert.assertEquals(ip6Address, cmd.getIp6Address()); + Assert.assertEquals(peerAsNumber, cmd.getAsNumber()); + Assert.assertEquals(peerPassword, cmd.getPassword()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_UPDATE, cmd.getEventType()); + Assert.assertEquals(String.format("Updating Bgp Peer %s", id), cmd.getEventDescription()); + + BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); + Mockito.when(routedIpv4Manager.updateBgpPeer(cmd)).thenReturn(bgpPeer); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } + + @Test + public void testUpdateBgpPeerCleanupDetails() { + UpdateBgpPeerCmd cmd = new UpdateBgpPeerCmd(); + Assert.assertFalse(cmd.isCleanupDetails()); + + ReflectionTestUtils.setField(cmd, "cleanupDetails", Boolean.TRUE); + Assert.assertTrue(cmd.isCleanupDetails()); + + ReflectionTestUtils.setField(cmd, "cleanupDetails", Boolean.FALSE); + Assert.assertFalse(cmd.isCleanupDetails()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java new file mode 100644 index 00000000000..c4e21bb948b --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java @@ -0,0 +1,55 @@ +// 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.api.command.admin.vpc; + +import com.cloud.network.vpc.VpcService; +import com.cloud.user.AccountService; +import com.cloud.utils.db.EntityManager; +import junit.framework.TestCase; +import org.apache.cloudstack.api.ResponseGenerator; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class CreateVPCCmdByAdminTest extends TestCase { + + @Mock + public VpcService _vpcService; + @Mock + public EntityManager _entityMgr; + @Mock + public AccountService _accountService; + private ResponseGenerator responseGenerator; + @InjectMocks + CreateVPCCmdByAdmin cmd = new CreateVPCCmdByAdmin(); + + @Test + public void testBgpPeerIds() { + List bgpPeerIds = Mockito.mock(List.class); + ReflectionTestUtils.setField(cmd, "bgpPeerIds", bgpPeerIds); + Assert.assertEquals(bgpPeerIds, cmd.getBgpPeerIds()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmdTest.java new file mode 100644 index 00000000000..9d7f4ef7cf1 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmdTest.java @@ -0,0 +1,97 @@ +// 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.api.command.user.bgp; + +import com.cloud.bgp.ASNumber; +import com.cloud.bgp.BGPService; + +import com.cloud.utils.Pair; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ASNumberResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListASNumbersCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testListASNumbersCmdTest() { + Long zoneId = 1L; + Long asNumberRangeId = 2L; + Integer asNumber = 10; + Long networkId = 11L; + Long vpcId = 12L; + String account = "account"; + Long domainId = 13L; + + ListASNumbersCmd cmd = new ListASNumbersCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "asNumberRangeId", asNumberRangeId); + ReflectionTestUtils.setField(cmd, "asNumber", asNumber); + ReflectionTestUtils.setField(cmd, "networkId", networkId); + ReflectionTestUtils.setField(cmd, "vpcId", vpcId); + ReflectionTestUtils.setField(cmd, "account", account); + ReflectionTestUtils.setField(cmd, "domainId", domainId); + ReflectionTestUtils.setField(cmd, "allocated", Boolean.TRUE); + + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(asNumberRangeId, cmd.getAsNumberRangeId()); + Assert.assertEquals(asNumber, cmd.getAsNumber()); + Assert.assertEquals(networkId, cmd.getNetworkId()); + Assert.assertEquals(vpcId, cmd.getVpcId()); + Assert.assertEquals(account, cmd.getAccount()); + Assert.assertEquals(domainId, cmd.getDomainId()); + Assert.assertTrue(cmd.getAllocated()); + + List asNumbers = new ArrayList<>(); + ASNumber asn = Mockito.mock(ASNumber.class); + asNumbers.add(asn); + Pair, Integer> pair = new Pair<>(asNumbers, 1); + + ASNumberResponse asNumberResponse = Mockito.mock(ASNumberResponse.class); + Mockito.when(_responseGenerator.createASNumberResponse(asn)).thenReturn(asNumberResponse); + + Mockito.when(bgpService.listASNumbers(cmd)).thenReturn(pair); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof ListResponse); + ListResponse listResponse = (ListResponse) response; + Assert.assertEquals(1L, (long) listResponse.getCount()); + Assert.assertTrue(listResponse.getResponses().get(0) instanceof ASNumberResponse); + ASNumberResponse firstResponse = (ASNumberResponse) listResponse.getResponses().get(0); + Assert.assertEquals(asNumberResponse, firstResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmdTest.java new file mode 100644 index 00000000000..11c41f4c92d --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmdTest.java @@ -0,0 +1,251 @@ +// 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.api.command.user.network.routing; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.firewall.FirewallService; +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.net.NetUtils; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +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; + +@RunWith(MockitoJUnitRunner.class) +public class CreateRoutingFirewallRuleCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + FirewallService _firewallService = Mockito.spy(FirewallService.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testIsDisplay() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + assertTrue(cmd.isDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.TRUE); + assertTrue(cmd.isDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.FALSE); + assertFalse(cmd.isDisplay()); + } + + @Test + public void testGetProtocolValid() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + assertEquals("", cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "1"); + assertEquals(NetUtils.ICMP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "icmp"); + assertEquals(NetUtils.ICMP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "6"); + assertEquals(NetUtils.TCP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "tcp"); + assertEquals(NetUtils.TCP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "17"); + assertEquals(NetUtils.UDP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "udp"); + assertEquals(NetUtils.UDP_PROTO, cmd.getProtocol()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetProtocolInValid() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + ReflectionTestUtils.setField(cmd, "protocol", "100"); + cmd.getProtocol(); + } + + @Test + public void testGetSourceCidrListNull() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + List result = cmd.getSourceCidrList(); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(NetUtils.ALL_IP4_CIDRS, result.get(0)); + } + + @Test + public void testGetSourceCidrList() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + List cidrList = Arrays.asList("192.168.0.0/24", "10.0.0.0/8"); + cmd.sourceCidrList = cidrList; + List result = cmd.getSourceCidrList(); + assertNotNull(result); + assertEquals(cidrList, result); + } + + @Test + public void testGetDestinationCidrListNull() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + List result = cmd.getDestinationCidrList(); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(NetUtils.ALL_IP4_CIDRS, result.get(0)); + } + + @Test + public void testGetDestinationCidrList() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + List cidrList = Arrays.asList("192.168.0.0/24", "10.0.0.0/8"); + cmd.destinationCidrlist = cidrList; + List result = cmd.getDestinationCidrList(); + assertNotNull(result); + assertEquals(cidrList, result); + } + + @Test + public void testGetTrafficTypeValid() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + assertEquals(FirewallRule.TrafficType.Ingress, cmd.getTrafficType()); + + ReflectionTestUtils.setField(cmd, "trafficType", "ingress"); + assertEquals(FirewallRule.TrafficType.Ingress, cmd.getTrafficType()); + + ReflectionTestUtils.setField(cmd, "trafficType", "egress"); + assertEquals(FirewallRule.TrafficType.Egress, cmd.getTrafficType()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetTrafficTypeInValid() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + ReflectionTestUtils.setField(cmd, "trafficType", "invalid"); + cmd.getTrafficType(); + } + + @Test + public void testSourcePortStartEnd() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + assertNull(cmd.getSourcePortStart()); + assertNull(cmd.getSourcePortEnd()); + + ReflectionTestUtils.setField(cmd, "publicStartPort", 1111); + assertEquals(1111, (int) cmd.getSourcePortStart()); + assertEquals(1111, (int) cmd.getSourcePortEnd()); + + ReflectionTestUtils.setField(cmd, "publicEndPort", 2222); + assertEquals(1111, (int) cmd.getSourcePortStart()); + assertEquals(2222, (int) cmd.getSourcePortEnd()); + } + + @Test + public void testNetworkId() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + ReflectionTestUtils.setField(cmd, "networkId", 1111L); + assertEquals(1111L, (long) cmd.getNetworkId()); + + assertEquals(1111L, (long) cmd.getApiResourceId()); + assertEquals(ApiCommandResourceType.Network, cmd.getApiResourceType()); + assertEquals(EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE, cmd.getEventType()); + assertEquals("Creating ipv4 firewall rule for routed network", cmd.getEventDescription()); + } + + @Test + public void testIcmpCodeAndType() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "protocol", "tcp"); + assertNull(cmd.getIcmpType()); + assertNull(cmd.getIcmpCode()); + + ReflectionTestUtils.setField(cmd, "protocol", "icmp"); + assertEquals(-1, (int) cmd.getIcmpType()); + assertEquals(-1, (int) cmd.getIcmpCode()); + + ReflectionTestUtils.setField(cmd, "icmpType", 1111); + ReflectionTestUtils.setField(cmd, "icmpCode", 2222); + assertEquals(1111, (int) cmd.getIcmpType()); + assertEquals(2222, (int) cmd.getIcmpCode()); + } + + @Test + public void testCreate() throws Exception { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + + Long id = 1L; + String uuid = "uuid"; + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(firewallRule.getId()).thenReturn(id); + Mockito.when(firewallRule.getUuid()).thenReturn(uuid); + Mockito.when(routedIpv4Manager.createRoutingFirewallRule(cmd)).thenReturn(firewallRule); + + try { + cmd.create(); + } catch (Exception ignored) { + } + + assertEquals(id, cmd.getEntityId()); + assertEquals(uuid, cmd.getEntityUuid()); + } + + @Test + public void testExecute() throws Exception { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); + ReflectionTestUtils.setField(cmd, "_responseGenerator", _responseGenerator); + + Long id = 1L; + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(firewallRule.getId()).thenReturn(id); + Mockito.when(_firewallService.getFirewallRule(id)).thenReturn(firewallRule); + Mockito.when(routedIpv4Manager.applyRoutingFirewallRule(id)).thenReturn(true); + + FirewallResponse ruleResponse = Mockito.mock(FirewallResponse.class); + Mockito.when(_responseGenerator.createFirewallResponse(firewallRule)).thenReturn(ruleResponse); + + try { + ReflectionTestUtils.setField(cmd, "id", id); + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(ruleResponse, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.java new file mode 100644 index 00000000000..2b55d4c6a58 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.java @@ -0,0 +1,83 @@ +// 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.api.command.user.network.routing; + +import com.cloud.event.EventTypes; +import com.cloud.network.firewall.FirewallService; +import com.cloud.network.rules.FirewallRule; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteRoutingFirewallRuleCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + FirewallService _firewallService = Mockito.spy(FirewallService.class); + + @Test + public void testProperties() { + DeleteRoutingFirewallRuleCmd cmd = new DeleteRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); + + long id = 1L; + long accountId = 2L; + long networkId = 3L; + + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(firewallRule.getAccountId()).thenReturn(accountId); + Mockito.when(firewallRule.getNetworkId()).thenReturn(networkId); + Mockito.when(_firewallService.getFirewallRule(id)).thenReturn(firewallRule); + + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, (long) cmd.getId()); + assertEquals(accountId, cmd.getEntityOwnerId()); + assertEquals(networkId, (long) cmd.getApiResourceId()); + assertEquals(ApiCommandResourceType.Network, cmd.getApiResourceType()); + assertEquals(EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE, cmd.getEventType()); + assertEquals(String.format("Deleting ipv4 routing firewall rule ID=%s", id), cmd.getEventDescription()); + } + + + @Test + public void testExecute() throws Exception { + DeleteRoutingFirewallRuleCmd cmd = new DeleteRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + + Long id = 1L; + Mockito.when(routedIpv4Manager.revokeRoutingFirewallRule(id)).thenReturn(true); + + try { + ReflectionTestUtils.setField(cmd, "id", id); + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmdTest.java new file mode 100644 index 00000000000..53ac45917cb --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmdTest.java @@ -0,0 +1,115 @@ +// 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.api.command.user.network.routing; + +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.Pair; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class ListRoutingFirewallRulesCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testIsDisplay() { + ListRoutingFirewallRulesCmd cmd = new ListRoutingFirewallRulesCmd(); + assertTrue(cmd.getDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.TRUE); + assertTrue(cmd.getDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.FALSE); + assertFalse(cmd.getDisplay()); + } + + @Test + public void testTrafficType() { + ListRoutingFirewallRulesCmd cmd = new ListRoutingFirewallRulesCmd(); + assertNull(cmd.getTrafficType()); + + ReflectionTestUtils.setField(cmd, "trafficType", "Ingress"); + assertEquals(FirewallRule.TrafficType.Ingress, cmd.getTrafficType()); + + ReflectionTestUtils.setField(cmd, "trafficType", "Egress"); + assertEquals(FirewallRule.TrafficType.Egress, cmd.getTrafficType()); + } + + @Test + public void testOtherProperties() { + ListRoutingFirewallRulesCmd cmd = new ListRoutingFirewallRulesCmd(); + + long id = 1L; + long networkId = 3L; + + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "networkId", networkId); + + assertEquals(id, (long) cmd.getId()); + assertEquals(networkId, (long) cmd.getNetworkId()); + assertNull(cmd.getIpAddressId()); + } + + + @Test + public void testExecute() throws Exception { + ListRoutingFirewallRulesCmd cmd = new ListRoutingFirewallRulesCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + ReflectionTestUtils.setField(cmd, "_responseGenerator", _responseGenerator); + + Long id = 1L; + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + List firewallRules = Arrays.asList(firewallRule); + Pair, Integer> result = new Pair<>(firewallRules, 1); + + Mockito.when(routedIpv4Manager.listRoutingFirewallRules(cmd)).thenReturn(result); + + FirewallResponse ruleResponse = Mockito.mock(FirewallResponse.class); + Mockito.when(_responseGenerator.createFirewallResponse(firewallRule)).thenReturn(ruleResponse); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof ListResponse); + ListResponse listResponse = (ListResponse) cmd.getResponseObject(); + Assert.assertEquals(1, (int) listResponse.getCount()); + Assert.assertEquals(ruleResponse, listResponse.getResponses().get(0)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmdTest.java new file mode 100644 index 00000000000..dd0319df696 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmdTest.java @@ -0,0 +1,106 @@ +// 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.api.command.user.network.routing; + +import com.cloud.event.EventTypes; +import com.cloud.network.firewall.FirewallService; +import com.cloud.network.rules.FirewallRule; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateRoutingFirewallRuleCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + FirewallService _firewallService = Mockito.spy(FirewallService.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testIsDisplay() { + UpdateRoutingFirewallRuleCmd cmd = new UpdateRoutingFirewallRuleCmd(); + assertTrue(cmd.isDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.TRUE); + assertTrue(cmd.isDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.FALSE); + assertFalse(cmd.isDisplay()); + } + + @Test + public void testOtherProperties() { + UpdateRoutingFirewallRuleCmd cmd = new UpdateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); + + long id = 1L; + long accountId = 2L; + long networkId = 3L; + + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(firewallRule.getAccountId()).thenReturn(accountId); + Mockito.when(firewallRule.getNetworkId()).thenReturn(networkId); + Mockito.when(_firewallService.getFirewallRule(id)).thenReturn(firewallRule); + + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, (long) cmd.getId()); + assertEquals(accountId, cmd.getEntityOwnerId()); + assertEquals(networkId, (long) cmd.getApiResourceId()); + assertEquals(ApiCommandResourceType.Network, cmd.getApiResourceType()); + assertEquals(EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_UPDATE, cmd.getEventType()); + assertEquals("Updating ipv4 routing firewall rule", cmd.getEventDescription()); + } + + + @Test + public void testExecute() throws Exception { + UpdateRoutingFirewallRuleCmd cmd = new UpdateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); + ReflectionTestUtils.setField(cmd, "_responseGenerator", _responseGenerator); + + Long id = 1L; + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(routedIpv4Manager.updateRoutingFirewallRule(cmd)).thenReturn(firewallRule); + + FirewallResponse ruleResponse = Mockito.mock(FirewallResponse.class); + Mockito.when(_responseGenerator.createFirewallResponse(firewallRule)).thenReturn(ruleResponse); + + try { + ReflectionTestUtils.setField(cmd, "id", id); + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(ruleResponse, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java index a28e9e9fd04..2505c67e87d 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java @@ -86,6 +86,20 @@ public class CreateVPCCmdTest extends TestCase { Assert.assertEquals(cmd.getCidr(), cidr); } + @Test + public void testGetCidrSize() { + int cidrSize = 24; + ReflectionTestUtils.setField(cmd, "cidrSize", cidrSize); + Assert.assertEquals(cidrSize, (int) cmd.getCidrSize()); + } + + @Test + public void testAsNumber() { + long asNumber = 10000; + ReflectionTestUtils.setField(cmd, "asNumber", asNumber); + Assert.assertEquals(asNumber, (long) cmd.getAsNumber()); + } + @Test public void testGetDisplayText() { String displayText = "VPC Network"; diff --git a/api/src/test/java/org/apache/cloudstack/api/response/ASNRangeResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/ASNRangeResponseTest.java new file mode 100644 index 00000000000..50248383b4f --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/ASNRangeResponseTest.java @@ -0,0 +1,51 @@ +// 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.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public final class ASNRangeResponseTest { + + private static String uuid = "uuid"; + private static String zoneId = "zoneid"; + private static long startASNumber = 10; + private static long endASNumber = 20; + private static Date created = new Date(); + + @Test + public void testASNRangeResponse() { + final ASNRangeResponse response = new ASNRangeResponse(); + + response.setId(uuid); + response.setZoneId(zoneId); + response.setStartASNumber(startASNumber); + response.setEndASNumber(endASNumber); + response.setCreated(created); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(startASNumber, (long) response.getStartASNumber()); + Assert.assertEquals(endASNumber, (long) response.getEndASNumber()); + Assert.assertEquals(created, response.getCreated()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/ASNumberResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/ASNumberResponseTest.java new file mode 100644 index 00000000000..9515984134e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/ASNumberResponseTest.java @@ -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.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public final class ASNumberResponseTest { + + private static String uuid = "uuid"; + private static String accountId = "account-id"; + private static String accountName = "account-name"; + private static String domainId = "domain-uuid"; + private static String domainName = "domain-name"; + private static Long asNumber = 15000L; + private static String asNumberRangeId = "as-number-range-uuid"; + private static String asNumberRange = "10000-20000"; + private static String zoneId = "zone-id"; + private static String zoneName = "zone-name"; + private static Date allocated = new Date(); + private static String allocationState = "allocated"; + + private static String associatedNetworkId = "network-id"; + + private static String associatedNetworkName = "network-name"; + + private static String vpcId = "vpc-uuid"; + private static String vpcName = "vpc-name"; + private static Date created = new Date(); + + + + @Test + public void testASNumberResponse() { + final ASNumberResponse response = new ASNumberResponse(); + + response.setId(uuid); + response.setAccountId(accountId); + response.setAccountName(accountName); + response.setDomainId(domainId); + response.setDomainName(domainName); + response.setAsNumber(asNumber); + response.setAsNumberRangeId(asNumberRangeId); + response.setAsNumberRange(asNumberRange); + response.setZoneId(zoneId); + response.setZoneName(zoneName); + response.setAllocated(allocated); + response.setAllocationState(allocationState); + response.setAssociatedNetworkId(associatedNetworkId); + response.setAssociatedNetworkName(associatedNetworkName); + response.setVpcId(vpcId); + response.setVpcName(vpcName); + response.setCreated(created); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(accountId, response.getAccountId()); + Assert.assertEquals(accountName, response.getAccountName()); + Assert.assertEquals(domainId, response.getDomainId()); + Assert.assertEquals(domainName, response.getDomainName()); + Assert.assertEquals(asNumber, response.getAsNumber()); + Assert.assertEquals(asNumberRangeId, response.getAsNumberRangeId()); + Assert.assertEquals(asNumberRange, response.getAsNumberRange()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(zoneName, response.getZoneName()); + Assert.assertEquals(allocated, response.getAllocated()); + Assert.assertEquals(allocationState, response.getAllocationState()); + Assert.assertEquals(associatedNetworkId, response.getAssociatedNetworkId()); + Assert.assertEquals(associatedNetworkName, response.getAssociatedNetworkName()); + Assert.assertEquals(vpcId, response.getVpcId()); + Assert.assertEquals(vpcName, response.getVpcName()); + Assert.assertEquals(created, response.getCreated()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/BgpPeerResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/BgpPeerResponseTest.java new file mode 100644 index 00000000000..7c82eb84368 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/BgpPeerResponseTest.java @@ -0,0 +1,81 @@ +// 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.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@RunWith(MockitoJUnitRunner.class) +public final class BgpPeerResponseTest { + + private static String uuid = "uuid"; + private static String ip4Address = "ip4-address"; + private static String ip6Address = "ip6-address"; + private static Long asNumber = 15000L; + private static String password = "password"; + private static String accountName = "account-name"; + private static String domainId = "domain-uuid"; + private static String domainName = "domain-name"; + private static String projectId = "project-uuid"; + private static String projectName = "project-name"; + private static String zoneId = "zone-id"; + private static String zoneName = "zone-name"; + private static Date created = new Date(); + + @Test + public void testBgpPeerResponse() { + final BgpPeerResponse response = new BgpPeerResponse(); + + response.setId(uuid); + response.setIp4Address(ip4Address); + response.setIp6Address(ip6Address); + response.setAsNumber(asNumber); + response.setPassword(password); + response.setAccountName(accountName); + response.setDomainId(domainId); + response.setDomainName(domainName); + response.setProjectId(projectId); + response.setProjectName(projectName); + response.setZoneId(zoneId); + response.setZoneName(zoneName); + response.setCreated(created); + Map details = new HashMap<>(); + details.put("key", "value"); + response.setDetails(details); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(ip4Address, response.getIp4Address()); + Assert.assertEquals(ip6Address, response.getIp6Address()); + Assert.assertEquals(asNumber, response.getAsNumber()); + Assert.assertEquals(password, response.getPassword()); + Assert.assertEquals(accountName, response.getAccountName()); + Assert.assertEquals(domainId, response.getDomainId()); + Assert.assertEquals(domainName, response.getDomainName()); + Assert.assertEquals(projectId, response.getProjectId()); + Assert.assertEquals(projectName, response.getProjectName()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(zoneName, response.getZoneName()); + Assert.assertEquals(created, response.getCreated()); + Assert.assertEquals(details, response.getDetails()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponseTest.java new file mode 100644 index 00000000000..add9544de7d --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponseTest.java @@ -0,0 +1,66 @@ +// 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.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public final class DataCenterIpv4SubnetResponseTest { + + private static String uuid = "uuid"; + private static String subnet = "10.10.10.0/26"; + private static String accountName = "account-name"; + private static String domainId = "domain-uuid"; + private static String domainName = "domain-name"; + private static String projectId = "project-uuid"; + private static String projectName = "project-name"; + private static String zoneId = "zone-id"; + private static String zoneName = "zone-name"; + private static Date created = new Date(); + + @Test + public void testDataCenterIpv4SubnetResponse() { + final DataCenterIpv4SubnetResponse response = new DataCenterIpv4SubnetResponse(); + + response.setId(uuid); + response.setSubnet(subnet); + response.setAccountName(accountName); + response.setDomainId(domainId); + response.setDomainName(domainName); + response.setProjectId(projectId); + response.setProjectName(projectName); + response.setZoneId(zoneId); + response.setZoneName(zoneName); + response.setCreated(created); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(subnet, response.getSubnet()); + Assert.assertEquals(accountName, response.getAccountName()); + Assert.assertEquals(domainId, response.getDomainId()); + Assert.assertEquals(domainName, response.getDomainName()); + Assert.assertEquals(projectId, response.getProjectId()); + Assert.assertEquals(projectName, response.getProjectName()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(zoneName, response.getZoneName()); + Assert.assertEquals(created, response.getCreated()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/Ipv4RouteResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/Ipv4RouteResponseTest.java new file mode 100644 index 00000000000..717668d054e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/Ipv4RouteResponseTest.java @@ -0,0 +1,48 @@ +// 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.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public final class Ipv4RouteResponseTest { + + private static String subnet = "10.10.10.0/24"; + private static String gateway = "10.10.10.1"; + + @Test + public void testIpv4RouteResponse() { + final Ipv4RouteResponse response = new Ipv4RouteResponse(subnet, gateway); + + Assert.assertEquals(subnet, response.getSubnet()); + Assert.assertEquals(gateway, response.getGateway()); + } + + @Test + public void testIpv4RouteResponse2() { + final Ipv4RouteResponse response = new Ipv4RouteResponse(); + + response.setSubnet(subnet); + response.setGateway(gateway); + + Assert.assertEquals(subnet, response.getSubnet()); + Assert.assertEquals(gateway, response.getGateway()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponseTest.java new file mode 100644 index 00000000000..6fb5141e7a9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponseTest.java @@ -0,0 +1,81 @@ +// 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.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public final class Ipv4SubnetForGuestNetworkResponseTest { + + private static String uuid = "uuid"; + private static String parentId = "parent-id"; + private static String parentSubnet = "10.10.0.0/20"; + private static String subnet = "10.10.0.0/24"; + private static String state = "Allocating"; + + private static String zoneId = "zone-id"; + private static String zoneName = "zone-name"; + private static Date allocated = new Date(); + private static String networkId = "network-id"; + private static String networkName = "network-name"; + private static String vpcId = "vpc-uuid"; + private static String vpcName = "vpc-name"; + private static Date created = new Date(); + private static Date removed = new Date(); + + + + @Test + public void testIpv4SubnetForGuestNetworkResponse() { + final Ipv4SubnetForGuestNetworkResponse response = new Ipv4SubnetForGuestNetworkResponse(); + + response.setId(uuid); + response.setSubnet(subnet); + response.setParentId(parentId); + response.setParentSubnet(parentSubnet); + response.setState(state); + response.setZoneId(zoneId); + response.setZoneName(zoneName); + response.setAllocatedTime(allocated); + response.setNetworkId(networkId); + response.setNetworkName(networkName); + response.setVpcId(vpcId); + response.setVpcName(vpcName); + response.setCreated(created); + response.setRemoved(removed); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(subnet, response.getSubnet()); + Assert.assertEquals(parentId, response.getParentId()); + Assert.assertEquals(parentSubnet, response.getParentSubnet()); + Assert.assertEquals(state, response.getState()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(zoneName, response.getZoneName()); + Assert.assertEquals(allocated, response.getAllocatedTime()); + Assert.assertEquals(networkId, response.getNetworkId()); + Assert.assertEquals(networkName, response.getNetworkName()); + Assert.assertEquals(vpcId, response.getVpcId()); + Assert.assertEquals(vpcName, response.getVpcName()); + Assert.assertEquals(created, response.getCreated()); + Assert.assertEquals(removed, response.getRemoved()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/network/BgpPeerTOTest.java b/api/src/test/java/org/apache/cloudstack/network/BgpPeerTOTest.java new file mode 100644 index 00000000000..2d1f8868ffc --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/network/BgpPeerTOTest.java @@ -0,0 +1,67 @@ +// 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.network; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class BgpPeerTOTest { + + private static Long peerId = 100L; + private static String ip4Address = "ip4-address"; + private static String ip6Address = "ip6-address"; + private static Long peerAsNumber = 15000L; + private static String peerPassword = "peer-password"; + private static Long networkId = 200L; + private static Long networkAsNumber = 20000L; + private static String guestIp4Cidr = "10.10.10.0/24"; + private static String guestIp6Cidr = "fd00:1111:2222:3333::1/64"; + + @Test + public void testBgpPeerTO1() { + BgpPeerTO bgpPeerTO = new BgpPeerTO(networkId); + + Assert.assertEquals(networkId, bgpPeerTO.getNetworkId()); + } + + @Test + public void testBgpPeerTO2() { + Map details = new HashMap<>(); + details.put(BgpPeer.Detail.EBGP_MultiHop, "100"); + + BgpPeerTO bgpPeerTO = new BgpPeerTO(peerId, ip4Address, ip6Address, peerAsNumber, peerPassword, + networkId, networkAsNumber, guestIp4Cidr, guestIp6Cidr, details); + + Assert.assertEquals(peerId, bgpPeerTO.getPeerId()); + Assert.assertEquals(peerAsNumber, bgpPeerTO.getPeerAsNumber()); + Assert.assertEquals(ip4Address, bgpPeerTO.getIp4Address()); + Assert.assertEquals(ip6Address, bgpPeerTO.getIp6Address()); + Assert.assertEquals(peerPassword, bgpPeerTO.getPeerPassword()); + Assert.assertEquals(networkId, bgpPeerTO.getNetworkId()); + Assert.assertEquals(networkAsNumber, bgpPeerTO.getNetworkAsNumber()); + Assert.assertEquals(guestIp4Cidr, bgpPeerTO.getGuestIp4Cidr()); + Assert.assertEquals(guestIp6Cidr, bgpPeerTO.getGuestIp6Cidr()); + + Assert.assertNotNull(bgpPeerTO.getDetails()); + details = bgpPeerTO.getDetails(); + Assert.assertEquals(1, details.size()); + Assert.assertEquals("100", details.get(BgpPeer.Detail.EBGP_MultiHop)); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersAnswer.java b/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersAnswer.java new file mode 100644 index 00000000000..9645b300db5 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersAnswer.java @@ -0,0 +1,46 @@ +// +// 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.agent.api.routing; + +import java.util.Arrays; + +import com.cloud.agent.api.Answer; + +public class SetBgpPeersAnswer extends Answer { + String[] results; + + protected SetBgpPeersAnswer() { + } + + public SetBgpPeersAnswer(SetBgpPeersCommand cmd, boolean success, String[] results) { + super(cmd, success, null); + if (results != null) { + assert (cmd.getBpgPeers().length == results.length) : "BGP peers and their results should be the same length"; + this.results = Arrays.copyOf(results, results.length); + } + } + + public String[] getResults() { + if (results != null) { + return Arrays.copyOf(results, results.length); + } + return null; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersCommand.java b/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersCommand.java new file mode 100644 index 00000000000..36371a196c8 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersCommand.java @@ -0,0 +1,39 @@ +// +// 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.agent.api.routing; + +import java.util.List; + +import org.apache.cloudstack.network.BgpPeerTO; + +public class SetBgpPeersCommand extends NetworkElementCommand { + BgpPeerTO[] bpgPeers; + + protected SetBgpPeersCommand() { + } + + public SetBgpPeersCommand(List bpgPeers) { + this.bpgPeers = bpgPeers.toArray(new BgpPeerTO[bpgPeers.size()]); + } + + public BgpPeerTO[] getBpgPeers() { + return bpgPeers; + } +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java index e435c838b7d..f9ea3e05e97 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java @@ -41,6 +41,7 @@ public class VRScripts { public static final String DHCP_CONFIG = "dhcp.json"; public static final String IP_ALIAS_CONFIG = "ip_aliases.json"; public static final String LOAD_BALANCER_CONFIG = "load_balancer.json"; + public static final String BGP_PEERS_CONFIG = "bgp_peers.json"; public static final String SYSTEM_VM_PATCHED = "patched.sh"; public final static String CONFIG_CACHE_LOCATION = "/var/cache/cloud/"; diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/AbstractConfigItemFacade.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/AbstractConfigItemFacade.java index 46dd801bebf..83dfa2a62ca 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/AbstractConfigItemFacade.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/AbstractConfigItemFacade.java @@ -37,6 +37,7 @@ import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.SetBgpPeersCommand; import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetIpv6FirewallRulesCommand; import com.cloud.agent.api.routing.SetMonitorServiceCommand; @@ -98,6 +99,7 @@ public abstract class AbstractConfigItemFacade { flyweight.put(SetSourceNatCommand.class, new SetSourceNatConfigItem()); flyweight.put(IpAssocCommand.class, new IpAssociationConfigItem()); flyweight.put(IpAssocVpcCommand.class, new IpAssociationConfigItem()); + flyweight.put(SetBgpPeersCommand.class, new SetBgpPeersConfigItem()); } protected String destinationFile; diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItem.java new file mode 100644 index 00000000000..68f4275bb6b --- /dev/null +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItem.java @@ -0,0 +1,46 @@ +// +// 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.agent.resource.virtualnetwork.facade; + +import java.util.Arrays; +import java.util.List; + +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetBgpPeersCommand; +import com.cloud.agent.resource.virtualnetwork.ConfigItem; +import com.cloud.agent.resource.virtualnetwork.VRScripts; +import com.cloud.agent.resource.virtualnetwork.model.BgpPeers; +import com.cloud.agent.resource.virtualnetwork.model.ConfigBase; + +public class SetBgpPeersConfigItem extends AbstractConfigItemFacade { + + @Override + public List generateConfig(final NetworkElementCommand cmd) { + final SetBgpPeersCommand command = (SetBgpPeersCommand) cmd; + return generateConfigItems(new BgpPeers(Arrays.asList(command.getBpgPeers()))); + } + + @Override + protected List generateConfigItems(final ConfigBase configuration) { + destinationFile = VRScripts.BGP_PEERS_CONFIG; + + return super.generateConfigItems(configuration); + } +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeers.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeers.java new file mode 100644 index 00000000000..54a1ab2e091 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeers.java @@ -0,0 +1,45 @@ +// +// 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.agent.resource.virtualnetwork.model; + +import org.apache.cloudstack.network.BgpPeerTO; + +import java.util.List; + +public class BgpPeers extends ConfigBase { + private List peers; + + public BgpPeers() { + super(ConfigBase.BGP_PEERS); + } + + public BgpPeers(List bgpPeers) { + super(ConfigBase.BGP_PEERS); + this.peers = bgpPeers; + } + + public List getPeers() { + return peers; + } + + public void setPeers(List bgpPeers) { + this.peers = bgpPeers; + } +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ConfigBase.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ConfigBase.java index ade80d71384..e370b764f22 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ConfigBase.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ConfigBase.java @@ -39,6 +39,7 @@ public abstract class ConfigBase { public static final String MONITORSERVICE = "monitorservice"; public static final String DHCP_CONFIG = "dhcpconfig"; public static final String LOAD_BALANCER = "loadbalancer"; + public final static String BGP_PEERS = "bgppeers"; private String type = UNKNOWN; diff --git a/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersAnswerTest.java b/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersAnswerTest.java new file mode 100644 index 00000000000..4cd15e4465a --- /dev/null +++ b/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersAnswerTest.java @@ -0,0 +1,54 @@ +// 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.agent.api.routing; + +import org.apache.cloudstack.network.BgpPeerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class SetBgpPeersAnswerTest { + + @Test + public void testSetBgpPeersAnswer() { + + String good = "good"; + String[] results = new String[1]; + results[0] = good; + + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + SetBgpPeersCommand command = new SetBgpPeersCommand(bgpPeerTOs); + + SetBgpPeersAnswer answer = new SetBgpPeersAnswer(command, true, results); + + Assert.assertNotNull(answer.getResults()); + Assert.assertEquals(1, answer.getResults().length); + Assert.assertEquals(good, answer.getResults()[0]); + } + + @Test + public void testSetBgpPeersAnswer2() { + SetBgpPeersAnswer answer = new SetBgpPeersAnswer(); + + Assert.assertNull(answer.getResults()); + } +} diff --git a/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersCommandTest.java b/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersCommandTest.java new file mode 100644 index 00000000000..882c3b9da30 --- /dev/null +++ b/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersCommandTest.java @@ -0,0 +1,47 @@ +// 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.agent.api.routing; + +import org.apache.cloudstack.network.BgpPeerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class SetBgpPeersCommandTest { + + @Test + public void testSetBgpPeersCommand1() { + SetBgpPeersCommand command = new SetBgpPeersCommand(); + Assert.assertNull(command.getBpgPeers()); + } + + @Test + public void testSetBgpPeersCommand2() { + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + + SetBgpPeersCommand command = new SetBgpPeersCommand(bgpPeerTOs); + Assert.assertNotNull(command.getBpgPeers()); + Assert.assertEquals(1, command.getBpgPeers().length); + Assert.assertEquals(bgpPeerTO, command.getBpgPeers()[0]); + } +} diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItemTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItemTest.java new file mode 100644 index 00000000000..5f177c88abf --- /dev/null +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItemTest.java @@ -0,0 +1,56 @@ +// 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.agent.resource.virtualnetwork.facade; + +import com.cloud.agent.api.routing.SetBgpPeersCommand; +import com.cloud.agent.resource.virtualnetwork.ConfigItem; +import com.cloud.agent.resource.virtualnetwork.FileConfigItem; +import com.cloud.agent.resource.virtualnetwork.ScriptConfigItem; +import com.cloud.agent.resource.virtualnetwork.VRScripts; + +import org.apache.cloudstack.network.BgpPeerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class SetBgpPeersConfigItemTest { + + + @Test + public void testSetBgpPeersConfigItem() { + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + SetBgpPeersCommand command = new SetBgpPeersCommand(bgpPeerTOs); + + SetBgpPeersConfigItem setBgpPeersConfigItem = new SetBgpPeersConfigItem(); + + List configItems = setBgpPeersConfigItem.generateConfig(command); + Assert.assertNotNull(configItems); + + Assert.assertEquals(2, configItems.size()); + Assert.assertTrue(configItems.get(0) instanceof FileConfigItem); + Assert.assertTrue(configItems.get(1) instanceof ScriptConfigItem); + + Assert.assertEquals(VRScripts.CONFIG_PERSIST_LOCATION, ((FileConfigItem) configItems.get(0)).getFilePath()); + Assert.assertTrue((((FileConfigItem) configItems.get(0)).getFileName().startsWith(VRScripts.BGP_PEERS_CONFIG))); + Assert.assertEquals(VRScripts.UPDATE_CONFIG, ((ScriptConfigItem) configItems.get(1)).getScript()); + } +} diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeersTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeersTest.java new file mode 100644 index 00000000000..eba423e55ed --- /dev/null +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeersTest.java @@ -0,0 +1,56 @@ +// 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.agent.resource.virtualnetwork.model; + +import org.apache.cloudstack.network.BgpPeerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class BgpPeersTest { + + @Test + public void testBgpPeers() { + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + + BgpPeers bgpPeers = new BgpPeers(bgpPeerTOs); + Assert.assertEquals(ConfigBase.BGP_PEERS, bgpPeers.getType()); + Assert.assertNotNull(bgpPeers.getPeers()); + Assert.assertEquals(1, bgpPeers.getPeers().size()); + Assert.assertEquals(bgpPeerTO, bgpPeers.getPeers().get(0)); + } + + @Test + public void testBgpPeers2() { + BgpPeers bgpPeers = new BgpPeers(); + Assert.assertEquals(ConfigBase.BGP_PEERS, bgpPeers.getType()); + + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + bgpPeers.setPeers(bgpPeerTOs); + + Assert.assertNotNull(bgpPeers.getPeers()); + Assert.assertEquals(1, bgpPeers.getPeers().size()); + Assert.assertEquals(bgpPeerTO, bgpPeers.getPeers().get(0)); + } +} diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index 84098bbc654..d8e97f0277b 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -199,7 +199,7 @@ public interface NetworkOrchestrationService { Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, boolean bypassVlanOverlapCheck, String networkDomain, Account owner, Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String ip6Gateway, String ip6Cidr, Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6, - String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; + String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; UserDataServiceProvider getPasswordResetProvider(Network network); diff --git a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java index c877ebbe8d2..01fd54430d6 100644 --- a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java +++ b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java @@ -223,7 +223,8 @@ public interface ConfigurationManager { Integer networkRate, Map> serviceProviderMap, boolean isDefault, Network.GuestType type, boolean systemOnly, Long serviceOfferingId, boolean conserveMode, Map> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent, Map details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive, Boolean forVpc, - Boolean forTungsten, boolean forNsx, String mode, List domainIds, List zoneIds, boolean enableOffering, final NetUtils.InternetProtocol internetProtocol); + Boolean forTungsten, boolean forNsx, NetworkOffering.NetworkMode networkMode, List domainIds, List zoneIds, boolean enableOffering, final NetUtils.InternetProtocol internetProtocol, + NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber); Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, boolean forSystemVms, Long podId, String startIP, String endIP, String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr, boolean forNsx) diff --git a/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java b/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java index 24012a2487b..c6279e08012 100644 --- a/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java +++ b/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java @@ -110,7 +110,8 @@ public interface VpcManager { Network createVpcGuestNetwork(long ntwkOffId, String name, String displayText, String gateway, String cidr, String vlanId, String networkDomain, Account owner, Long domainId, PhysicalNetwork pNtwk, long zoneId, ACLType aclType, Boolean subdomainAccess, long vpcId, Long aclId, Account caller, - Boolean displayNetworkEnabled, String externalId, String ip6Gateway, String ip6Cidr, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs) + Boolean displayNetworkEnabled, String externalId, String ip6Gateway, String ip6Cidr, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, + Pair vrIfaceMTUs, Integer networkCidrSize) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; /** diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index ce4c6bab94a..c8a0ce9e7e9 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -38,7 +38,10 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.ASNumberVO; +import com.cloud.bgp.BGPService; import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.ASNumberDao; import com.cloud.dc.dao.VlanDetailsDao; import com.cloud.network.dao.NsxProviderDao; import org.apache.cloudstack.acl.ControlledEntity.ACLType; @@ -56,6 +59,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.dao.NetworkPermissionDao; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; @@ -350,6 +354,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra List networkGurus; @Inject private NsxProviderDao nsxProviderDao; + @Inject + private ASNumberDao asNumberDao; + @Inject + private BGPService bgpService; @Override public List getNetworkGurus() { @@ -430,6 +438,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra NicSecondaryIpDao _nicSecondaryIpDao; @Inject ClusterDao clusterDao; + @Inject + RoutedIpv4Manager routedIpv4Manager; protected StateMachine2 _stateMachine; ScheduledExecutorService _executor; @@ -548,27 +558,27 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (_networkOfferingDao.findByUniqueName(NetworkOffering.QuickCloudNoServices) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.QuickCloudNoServices, "Offering for QuickCloud with no services", TrafficType.Guest, null, true, Availability.Optional, null, new HashMap>(), true, Network.GuestType.Shared, false, null, true, null, true, - false, null, false, null, true, false, false, false, null, null, null, true, null); + false, null, false, null, true, false, false, false, null, null, null, true, null, null, false); } //#2 - SG enabled network offering if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOfferingWithSGService) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, "Offering for Shared Security group enabled networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, - null, true, false, null, false, null, true, false, false, false, null, null, null, true, null); + null, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false); } //#3 - shared network offering with no SG service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOffering, "Offering for Shared networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false, null, false, - null, true, false, false, false, null,null, null, true, null); + null, true, false, false, false, null,null, null, true, null, null, false); } if (_networkOfferingDao.findByUniqueName(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE, "Offering for Tungsten Shared Security group enabled networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultTungstenSharedSGEnabledNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, - null, true, false, null, false, null, true, false, true, false, null, null,null, true, null); + null, true, false, null, false, null, true, false, true, false, null, null,null, true, null, null, false); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } @@ -578,14 +588,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService, "Offering for Isolated networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Required, null, defaultIsolatedSourceNatEnabledNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null, - true, false, false, false, null, null,null, true, null); + true, false, false, false, null, null,null, true, null, null, false); } //#5 - default vpc offering with LB service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks, "Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null, - defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null); + defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false); } //#6 - default vpc offering with no LB service @@ -594,14 +604,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra defaultVPCOffProviders.remove(Service.Lb); offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB, "Offering for Isolated VPC networks with Source Nat service enabled and LB service disabled", TrafficType.Guest, null, false, Availability.Optional, - null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null); + null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false); } //#7 - isolated offering with source nat disabled if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOffering, "Offering for Isolated networks with no Source Nat service", TrafficType.Guest, null, true, Availability.Optional, null, defaultIsolatedNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, - true, null, true, false, null, false, null, true, false, false, false, null, null, null, true, null); + true, null, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false); } //#8 - network offering with internal lb service @@ -623,7 +633,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB, "Offering for Isolated VPC networks with Internal Lb support", TrafficType.Guest, null, false, Availability.Optional, null, internalLbOffProviders, - true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, true, null); + true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, true, null, null, false); offering.setInternalLb(true); offering.setPublicLb(false); _networkOfferingDao.update(offering.getId(), offering); @@ -654,7 +664,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedEIPandELBNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedEIPandELBNetworkOffering, "Offering for Shared networks with Elastic IP and Elastic LB capabilities", TrafficType.Guest, null, true, Availability.Optional, null, - netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false, false, false, null, null, null, true, null); + netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false); offering.setDedicatedLB(false); _networkOfferingDao.update(offering.getId(), offering); } @@ -1618,13 +1628,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // Associate a source NAT IP (if one isn't already associated with the network) if this is a // 1) 'Isolated' or 'Shared' guest virtual network in the advance zone - // 2) network has sourceNat service + // 2) network has SourceNat or Gateway service // 3) network offering does not support a shared source NAT rule final boolean sharedSourceNat = offering.isSharedSourceNat(); final DataCenter zone = _dcDao.findById(network.getDataCenterId()); - if (!sharedSourceNat && _networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat) + if (!sharedSourceNat + && (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat) || _networkModel.areServicesSupportedInNetwork(network.getId(), Service.Gateway)) && (network.getGuestType() == Network.GuestType.Isolated || network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced)) { List ips = null; @@ -1742,6 +1753,13 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra success = false; } + // apply BGP settings + if (!bgpService.applyBgpPeers(network, false)) { + logger.warn("Failed to apply bpg peers as a part of network id {} restart", networkId); + success = false; + } + + // apply static nat if (!_rulesMgr.applyStaticNatsForNetwork(networkId, false, caller)) { logger.warn("Failed to apply static nats a part of network id {} restart", networkId); @@ -2669,7 +2687,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, null, owner, null, pNtwk, pNtwk.getDataCenterId(), ACLType.Account, null, vpcId, null, null, true, null, null, null, true, null, null, - null, null, null, null, null); + null, null, null, null, null, null); } @Override @@ -2678,11 +2696,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra boolean bypassVlanOverlapCheck, String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk, final long zoneId, final ACLType aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway, final String ip6Cidr, final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, - String routerIp, String routerIpv6, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { + String routerIp, String routerIpv6, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, + Pair vrIfaceMTUs, Integer networkCidrSize) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { // create Isolated/Shared/L2 network return createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, networkDomain, owner, domainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, - isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType, externalId, false, routerIp, routerIpv6, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs); + isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType, externalId, false, routerIp, routerIpv6, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs, networkCidrSize); } @DB @@ -2691,7 +2710,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra final long zoneId, final ACLType aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway, final String ip6Cidr, final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, final Boolean isPrivateNetwork, String routerIp, String routerIpv6, final String ip4Dns1, final String ip4Dns2, - final String ip6Dns1, final String ip6Dns2, Pair vrIfaceMTUs) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { + final String ip6Dns1, final String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); final DataCenterVO zone = _dcDao.findById(zoneId); @@ -2907,7 +2926,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced && ntwkOff.getTrafficType() == TrafficType.Guest && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated - && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))); + && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat) + && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.Gateway))); if (cidr == null && ip6Cidr == null && cidrRequired) { if (ntwkOff.getGuestType() == GuestType.Shared) { throw new InvalidParameterValueException(String.format("Gateway/netmask are required when creating %s networks.", Network.GuestType.Shared)); @@ -3033,6 +3053,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra userNetwork.setPvlanType(isolatedPvlanType); } } + userNetwork.setNetworkCidrSize(networkCidrSize); final List networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId, isDisplayNetworkEnabled); Network network = null; @@ -3394,6 +3415,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } else { // commit transaction only when ips and vlans for the network are released successfully + routedIpv4Manager.releaseBgpPeersForGuestNetwork(networkId); + routedIpv4Manager.releaseIpv4SubnetForGuestNetwork(networkId); ipv6Service.releaseIpv6SubnetForNetwork(networkId); ipv6Service.removePublicIpv6PlaceholderNics(networkFinal); try { @@ -3414,6 +3437,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra networkDetailsDao.removeDetails(networkFinal.getId()); networkPermissionDao.removeAllPermissions(networkFinal.getId()); + ASNumberVO asNumber = asNumberDao.findByZoneAndNetworkId(zone.getId(), networkId); + if (asNumber != null) { + logger.debug(String.format("Releasing AS number %s from network %s", asNumber.getAsNumber(), networkId)); + bgpService.releaseASNumber(zone.getId(), asNumber.getAsNumber(), true); + } } final NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, networkFinal.getNetworkOfferingId()); @@ -3946,6 +3974,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra final NetworkVO network = _networksDao.findById(networkId); final NetworkOfferingVO networkOffering= _networkOfferingDao.findById(network.getNetworkOfferingId()); + //remove BGP peers from the network + if (routedIpv4Manager.removeBgpPeersFromNetwork(network) != null) { + logger.debug("Successfully removed BGP peers from network id={}", networkId); + } else { + success = false; + logger.warn("Failed to remove BGP peers from network as a part of network id={} cleanup", networkId); + } + //remove all PF/Static Nat rules for the network try { if (_rulesMgr.revokeAllPFStaticNatRulesForNetwork(networkId, callerUserId, caller)) { diff --git a/engine/schema/src/main/java/com/cloud/dc/ASNumberRangeVO.java b/engine/schema/src/main/java/com/cloud/dc/ASNumberRangeVO.java new file mode 100644 index 00000000000..3790213b3ad --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/ASNumberRangeVO.java @@ -0,0 +1,104 @@ +// 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.dc; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.utils.db.GenericDao; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.UUID; + +@Entity +@Table(name = "as_number_range") +public class ASNumberRangeVO implements ASNumberRange { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "start_as_number") + private long startASNumber; + + @Column(name = "end_as_number") + private long endASNumber; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public ASNumberRangeVO() { + this.uuid = UUID.randomUUID().toString(); + this.created = GregorianCalendar.getInstance().getTime(); + } + + public ASNumberRangeVO(long dataCenterId, long startASNumber, long endASNumber) { + this(); + this.dataCenterId = dataCenterId; + this.startASNumber = startASNumber; + this.endASNumber = endASNumber; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public long getDataCenterId() { + return dataCenterId; + } + + @Override + public long getStartASNumber() { + return startASNumber; + } + + @Override + public long getEndASNumber() { + return endASNumber; + } + + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } +} diff --git a/engine/schema/src/main/java/com/cloud/dc/ASNumberVO.java b/engine/schema/src/main/java/com/cloud/dc/ASNumberVO.java new file mode 100644 index 00000000000..529d1cfb5fe --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/ASNumberVO.java @@ -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.dc; + +import com.cloud.bgp.ASNumber; +import com.cloud.utils.db.GenericDao; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "as_number") +public class ASNumberVO implements ASNumber { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "account_id") + private Long accountId; + + @Column(name = "domain_id") + private Long domainId; + + @Column(name = "as_number") + private long asNumber; + + @Column(name = "as_number_range_id") + private long asNumberRangeId; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "allocated") + @Temporal(value = TemporalType.TIMESTAMP) + private Date allocatedTime; + + @Column(name = "is_allocated") + private boolean allocated; + + @Column(name = "network_id") + private Long networkId; + + @Column(name = "vpc_id") + private Long vpcId; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public ASNumberVO() { + this.uuid = UUID.randomUUID().toString(); + this.created = new Date(); + } + + public ASNumberVO(long asNumber, long asNumberRangeId, long dataCenterId) { + this(); + this.asNumber = asNumber; + this.asNumberRangeId = asNumberRangeId; + this.dataCenterId = dataCenterId; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + @Override + public Long getAccountId() { + return accountId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + @Override + public Long getDomainId() { + return domainId; + } + + @Override + public long getAsNumber() { + return asNumber; + } + + @Override + public long getAsNumberRangeId() { + return asNumberRangeId; + } + + @Override + public long getDataCenterId() { + return dataCenterId; + } + + public void setAllocatedTime(Date date) { + this.allocatedTime = date; + } + + @Override + public Date getAllocatedTime() { + return allocatedTime; + } + + public void setAllocated(boolean allocated) { + this.allocated = allocated; + } + + @Override + public boolean isAllocated() { + return allocated; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + @Override + public Long getNetworkId() { + return networkId; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } + + public Long getVpcId() { + return vpcId; + } + + public void setVpcId(Long vpcId) { + this.vpcId = vpcId; + } +} diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDao.java new file mode 100644 index 00000000000..192f6bbaf31 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDao.java @@ -0,0 +1,41 @@ +// 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.dc.dao; + +import com.cloud.dc.ASNumberVO; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface ASNumberDao extends GenericDao { + + Pair, Integer> searchAndCountByZoneOrRangeOrAllocated(Long zoneId, Long asnRangeId, Integer asNumber, Long networkId, Long vpcId, + Boolean allocated, Long accountId, Long domainId, String keyword, Account caller, + Long startIndex, Long pageSizeVal); + ASNumberVO findByAsNumber(Long asNumber); + + ASNumberVO findOneByAllocationStateAndZone(long zoneId, boolean allocated); + + List listAllocatedByASRange(Long asRangeId); + + ASNumberVO findByZoneAndNetworkId(long zoneId, long networkId); + ASNumberVO findByZoneAndVpcId(long zoneId, long vpcId); + + int removeASRangeNumbers(long rangeId); +} diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDaoImpl.java new file mode 100644 index 00000000000..1d2adf4d424 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDaoImpl.java @@ -0,0 +1,141 @@ +// 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.dc.dao; + +import com.cloud.dc.ASNumberVO; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +import java.util.Arrays; +import java.util.List; + +public class ASNumberDaoImpl extends GenericDaoBase implements ASNumberDao { + + private final SearchBuilder asNumberSearch; + + public ASNumberDaoImpl() { + asNumberSearch = createSearchBuilder(); + asNumberSearch.and("zoneId", asNumberSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + asNumberSearch.and("rangeId", asNumberSearch.entity().getAsNumberRangeId(), SearchCriteria.Op.EQ); + asNumberSearch.and("isAllocated", asNumberSearch.entity().isAllocated(), SearchCriteria.Op.EQ); + asNumberSearch.and("asNumber", asNumberSearch.entity().getAsNumber(), SearchCriteria.Op.EQ); + asNumberSearch.and("networkId", asNumberSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + asNumberSearch.and("vpcId", asNumberSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + asNumberSearch.and("accountId", asNumberSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + asNumberSearch.and("domainId", asNumberSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + asNumberSearch.done(); + } + + @Override + public Pair, Integer> searchAndCountByZoneOrRangeOrAllocated(Long zoneId, Long asnRangeId, + Integer asNumber, Long networkId, Long vpcId, + Boolean allocated, + Long accountId, Long domainId, + String keyword, Account caller, + Long startIndex, Long pageSizeVal) { + SearchCriteria sc = asNumberSearch.create(); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (asnRangeId != null) { + sc.setParameters("rangeId", asnRangeId); + } + if (networkId != null) { + sc.setParameters("networkId", networkId); + } + if (vpcId != null) { + sc.setParameters("vpcId", vpcId); + } + if (allocated != null) { + sc.setParameters("isAllocated", allocated); + } + if (asNumber != null) { + sc.setParameters("asNumber", asNumber); + } + if (accountId != null) { + sc.setParameters("accountId", accountId); + } + if (domainId != null) { + sc.setParameters("domainId", domainId); + } + if (keyword != null) { + sc.addAnd("asNumber", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + } + if (Arrays.asList(Account.Type.DOMAIN_ADMIN, Account.Type.RESOURCE_DOMAIN_ADMIN).contains(caller.getType())) { + SearchCriteria scc = asNumberSearch.create(); + scc.addOr("domainId", SearchCriteria.Op.NULL); + scc.addOr("domainId", SearchCriteria.Op.EQ, caller.getDomainId()); + sc.addAnd("domainId", SearchCriteria.Op.SC, scc); + } else if (Arrays.asList(Account.Type.NORMAL, Account.Type.PROJECT).contains(caller.getType())) { + SearchCriteria scc = asNumberSearch.create(); + scc.addOr("domainId", SearchCriteria.Op.NULL); + scc.addOr("accountId", SearchCriteria.Op.EQ, caller.getAccountId()); + sc.addAnd("domainId", SearchCriteria.Op.SC, scc); + } + Filter searchFilter = new Filter(ASNumberVO.class, "id", true, startIndex, pageSizeVal); + return searchAndCount(sc, searchFilter); + } + + @Override + public ASNumberVO findByAsNumber(Long asNumber) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("asNumber", asNumber); + return findOneBy(sc); + } + + @Override + public ASNumberVO findOneByAllocationStateAndZone(long zoneId, boolean allocated) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("isAllocated", allocated); + return findOneBy(sc); + } + + @Override + public List listAllocatedByASRange(Long asRangeId) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("rangeId", asRangeId); + sc.setParameters("isAllocated", true); + return listBy(sc); + } + + public ASNumberVO findByZoneAndNetworkId(long zoneId, long networkId) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("networkId", networkId); + return findOneBy(sc); + } + + @Override + public ASNumberVO findByZoneAndVpcId(long zoneId, long vpcId) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("vpcId", vpcId); + return findOneBy(sc); + } + + @Override + public int removeASRangeNumbers(long rangeId) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("rangeId", rangeId); + return remove(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.java new file mode 100644 index 00000000000..3309a6f5fe5 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.java @@ -0,0 +1,27 @@ +// 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.dc.dao; + +import com.cloud.dc.ASNumberRangeVO; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface ASNumberRangeDao extends GenericDao { + + List listByZoneId(long zoneId); +} diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDaoImpl.java new file mode 100644 index 00000000000..4a4170685dc --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDaoImpl.java @@ -0,0 +1,42 @@ +// 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.dc.dao; + +import com.cloud.dc.ASNumberRangeVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +import java.util.List; + +public class ASNumberRangeDaoImpl extends GenericDaoBase implements ASNumberRangeDao { + + private final SearchBuilder searchBuilder; + + public ASNumberRangeDaoImpl() { + searchBuilder = createSearchBuilder(); + searchBuilder.and("zoneId", searchBuilder.entity().getDataCenterId(), SearchCriteria.Op.EQ); + searchBuilder.done(); + } + + @Override + public List listByZoneId(long zoneId) { + SearchCriteria sc = searchBuilder.create(); + sc.setParameters("zoneId", zoneId); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java index 21200dbf9b5..b80ccd9cd1b 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java @@ -72,4 +72,6 @@ public interface FirewallRulesDao extends GenericDao { void loadSourceCidrs(FirewallRuleVO rule); void loadDestinationCidrs(FirewallRuleVO rule); + + List listRoutingIngressFirewallRules(long networkId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java index 3ac860b08c5..1698e0ed2da 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -50,6 +50,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i protected SearchBuilder VmSearch; protected final SearchBuilder SystemRuleSearch; protected final GenericSearchBuilder RulesByIpCount; + protected final SearchBuilder RoutingFirewallRulesSearch; @Inject protected FirewallRulesCidrsDao _firewallRulesCidrsDao; @@ -104,6 +105,13 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i RulesByIpCount.and("ipAddressId", RulesByIpCount.entity().getSourceIpAddressId(), Op.EQ); RulesByIpCount.and("state", RulesByIpCount.entity().getState(), Op.EQ); RulesByIpCount.done(); + + RoutingFirewallRulesSearch = createSearchBuilder(); + RoutingFirewallRulesSearch.and("networkId", RoutingFirewallRulesSearch.entity().getNetworkId(), Op.EQ); + RoutingFirewallRulesSearch.and("purpose", RoutingFirewallRulesSearch.entity().getPurpose(), Op.EQ); + RoutingFirewallRulesSearch.and("trafficType", RoutingFirewallRulesSearch.entity().getTrafficType(), Op.EQ); + RoutingFirewallRulesSearch.and("ipId", RoutingFirewallRulesSearch.entity().getSourceIpAddressId(), Op.NULL); + RoutingFirewallRulesSearch.done(); } @Override @@ -386,4 +394,12 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i rule.setDestinationCidrsList(destCidrs); } + @Override + public List listRoutingIngressFirewallRules(long networkId) { + SearchCriteria sc = RoutingFirewallRulesSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("purpose", Purpose.Firewall); + sc.setParameters("trafficType", TrafficType.Ingress); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDao.java index e0509f80c2a..1675c89811a 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDao.java @@ -30,6 +30,8 @@ import com.cloud.utils.db.GenericDao; public interface NetworkServiceMapDao extends GenericDao { boolean areServicesSupportedInNetwork(long networkId, Service... services); + boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services); + boolean canProviderSupportServiceInNetwork(long networkId, Service service, Provider provider); List getServicesInNetwork(long networkId); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDaoImpl.java index 31e083075fa..f25bee5da47 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDaoImpl.java @@ -90,6 +90,28 @@ public class NetworkServiceMapDaoImpl extends GenericDaoBase sc = MultipleServicesSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("provider", provider.getName()); + + if (services != null) { + String[] servicesStr = new String[services.length]; + + int i = 0; + for (Service service : services) { + servicesStr[i] = service.getName(); + i++; + } + + sc.setParameters("service", (Object[])servicesStr); + } + + List networkServices = listBy(sc); + return !networkServices.isEmpty(); + } + @Override public boolean canProviderSupportServiceInNetwork(long networkId, Service service, Provider provider) { SearchCriteria sc = AllFieldsSearch.create(); 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 581f7899069..02abaacd854 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 @@ -203,6 +203,9 @@ public class NetworkVO implements Network { @Column(name = "private_mtu") Integer privateMtu; + @Transient + Integer networkCidrSize; + public NetworkVO() { uuid = UUID.randomUUID().toString(); } @@ -444,6 +447,7 @@ public class NetworkVO implements Network { return gateway; } + @Override public void setGateway(String gateway) { this.gateway = gateway; } @@ -457,6 +461,7 @@ public class NetworkVO implements Network { return cidr; } + @Override public void setCidr(String cidr) { this.cidr = cidr; } @@ -759,4 +764,13 @@ public class NetworkVO implements Network { public void setPrivateMtu(Integer privateMtu) { this.privateMtu = privateMtu; } + + @Override + public Integer getNetworkCidrSize() { + return networkCidrSize; + } + + public void setNetworkCidrSize(Integer networkCidrSize) { + this.networkCidrSize = networkCidrSize; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java index 350dda3f3b8..41254ba4a8b 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java @@ -28,6 +28,7 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.offering.NetworkOffering; import com.cloud.utils.db.GenericDao; @Entity @@ -61,8 +62,8 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "for_nsx") boolean forNsx = false; - @Column(name = "nsx_mode") - String nsxMode; + @Column(name = "network_mode") + NetworkOffering.NetworkMode networkMode; @Column(name = GenericDao.REMOVED_COLUMN) Date removed; @@ -85,6 +86,13 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "sort_key") int sortKey; + @Column(name="routing_mode") + @Enumerated(value = EnumType.STRING) + private NetworkOffering.RoutingMode routingMode; + + @Column(name = "specify_as_number") + private Boolean specifyAsNumber = false; + public VpcOfferingVO() { this.uuid = UUID.randomUUID().toString(); } @@ -158,12 +166,12 @@ public class VpcOfferingVO implements VpcOffering { this.forNsx = forNsx; } - public String getNsxMode() { - return nsxMode; + public NetworkOffering.NetworkMode getNetworkMode() { + return networkMode; } - public void setNsxMode(String nsxMode) { - this.nsxMode = nsxMode; + public void setNetworkMode(NetworkOffering.NetworkMode networkMode) { + this.networkMode = networkMode; } public void setUniqueName(String uniqueName) { @@ -226,4 +234,21 @@ public class VpcOfferingVO implements VpcOffering { return sortKey; } + @Override + public NetworkOffering.RoutingMode getRoutingMode() { + return routingMode; + } + + public void setRoutingMode(NetworkOffering.RoutingMode routingMode) { + this.routingMode = routingMode; + } + + @Override + public Boolean isSpecifyAsNumber() { + return specifyAsNumber; + } + + public void setSpecifyAsNumber(Boolean specifyAsNumber) { + this.specifyAsNumber = specifyAsNumber; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java index c2024e06c51..27d8227284b 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java @@ -159,6 +159,10 @@ public class VpcVO implements Vpc { return cidr; } + public void setCidr(String cidr) { + this.cidr = cidr; + } + @Override public long getDomainId() { return domainId; diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java index 264a1ebc75e..aa17723f0b1 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java @@ -33,4 +33,6 @@ public interface VpcOfferingDao extends GenericDao { NetUtils.InternetProtocol getVpcOfferingInternetProtocol(long offeringId); boolean isIpv6Supported(long offeringId); + + boolean isRoutedVpc(long offeringId); } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDaoImpl.java index 1cc6a21da76..b83fd891305 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDaoImpl.java @@ -19,6 +19,7 @@ package com.cloud.network.vpc.dao; import javax.inject.Inject; +import com.cloud.offering.NetworkOffering; import org.apache.cloudstack.api.ApiConstants; import org.springframework.stereotype.Component; @@ -84,4 +85,9 @@ public class VpcOfferingDaoImpl extends GenericDaoBase impl NetUtils.InternetProtocol internetProtocol = getVpcOfferingInternetProtocol(offeringId); return NetUtils.InternetProtocol.isIpv6EnabledProtocol(internetProtocol); } + + @Override + public boolean isRoutedVpc(long offeringId) { + return NetworkOffering.NetworkMode.ROUTED.equals(findById(offeringId).getNetworkMode()); + } } diff --git a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java index b2fabf2e3cd..0bf110757d7 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java @@ -139,8 +139,8 @@ public class NetworkOfferingVO implements NetworkOffering { @Column(name = "for_nsx") boolean forNsx = false; - @Column(name = "nsx_mode") - String nsxMode; + @Column(name = "network_mode") + NetworkMode networkMode; @Column(name = "egress_default_policy") boolean egressdefaultpolicy; @@ -174,6 +174,13 @@ public class NetworkOfferingVO implements NetworkOffering { @Column(name="service_package_id") String servicePackageUuid = null; + @Column(name="routing_mode") + @Enumerated(value = EnumType.STRING) + private RoutingMode routingMode; + + @Column(name = "specify_as_number") + private Boolean specifyAsNumber = false; + @Override public boolean isKeepAliveEnabled() { return keepAliveEnabled; @@ -211,12 +218,12 @@ public class NetworkOfferingVO implements NetworkOffering { } @Override - public String getNsxMode() { - return nsxMode; + public NetworkMode getNetworkMode() { + return networkMode; } - public void setNsxMode(String nsxMode) { - this.nsxMode = nsxMode; + public void setNetworkMode(NetworkMode networkMode) { + this.networkMode = networkMode; } @Override @@ -582,4 +589,21 @@ public class NetworkOfferingVO implements NetworkOffering { public boolean isSupportsVmAutoScaling() { return supportsVmAutoScaling; } + + @Override + public RoutingMode getRoutingMode() { + return routingMode; + } + + public void setRoutingMode(RoutingMode routingMode) { + this.routingMode = routingMode; + } + + public Boolean isSpecifyAsNumber() { + return specifyAsNumber; + } + + public void setSpecifyAsNumber(Boolean specifyAsNumber) { + this.specifyAsNumber = specifyAsNumber; + } } diff --git a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDao.java b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDao.java index 381d2144df1..abb63a10d06 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDao.java @@ -76,4 +76,6 @@ public interface NetworkOfferingDao extends GenericDao NetUtils.InternetProtocol getNetworkOfferingInternetProtocol(long offeringId, NetUtils.InternetProtocol defaultProtocol); boolean isIpv6Supported(long offeringId); + + boolean isRoutedNetwork(long offeringId); } diff --git a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java index 823ea36b97f..9bc74b13932 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java @@ -292,4 +292,9 @@ public class NetworkOfferingDaoImpl extends GenericDaoBase { + + List listByDataCenterId(long dcId); + List listByDataCenterIdAndAccountId(long dcId, long accountId); + List listByDataCenterIdAndDomainId(long dcId, long domainId); + List listNonDedicatedByDataCenterId(long dcId); + List listByAccountId(long accountId); + List listByDomainId(long domainId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDaoImpl.java new file mode 100644 index 00000000000..49e8a6ef662 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDaoImpl.java @@ -0,0 +1,83 @@ +// 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.datacenter.dao; + +import java.util.List; + +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnetVO; +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@DB +public class DataCenterIpv4GuestSubnetDaoImpl extends GenericDaoBase implements DataCenterIpv4GuestSubnetDao { + + public DataCenterIpv4GuestSubnetDaoImpl() { + } + + @Override + public List listByDataCenterId(long dcId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDataCenterId(), SearchCriteria.Op.EQ, dcId); + return sc.list(); + } + + @Override + public List listByDataCenterIdAndAccountId(long dcId, long accountId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDataCenterId(), SearchCriteria.Op.EQ, dcId); + sc.and(sc.entity().getAccountId(), SearchCriteria.Op.EQ, accountId); + return sc.list(); + } + + @Override + public List listByDataCenterIdAndDomainId(long dcId, long domainId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDataCenterId(), SearchCriteria.Op.EQ, dcId); + sc.and(sc.entity().getDomainId(), SearchCriteria.Op.EQ, domainId); + sc.and(sc.entity().getAccountId(), SearchCriteria.Op.NULL); + return sc.list(); + } + + @Override + public List listNonDedicatedByDataCenterId(long dcId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDataCenterId(), SearchCriteria.Op.EQ, dcId); + sc.and(sc.entity().getDomainId(), SearchCriteria.Op.NULL); + sc.and(sc.entity().getAccountId(), SearchCriteria.Op.NULL); + return sc.list(); + } + + @Override + public List listByAccountId(long accountId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getAccountId(), SearchCriteria.Op.EQ, accountId); + return sc.list(); + } + + @Override + public List listByDomainId(long domainId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDomainId(), SearchCriteria.Op.EQ, domainId); + return sc.list(); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerDetailsVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerDetailsVO.java new file mode 100644 index 00000000000..9e337887011 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerDetailsVO.java @@ -0,0 +1,103 @@ +// 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.network; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "bgp_peer_details") +public class BgpPeerDetailsVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "bgp_peer_id") + private long resourceId; + + @Enumerated(value = EnumType.STRING) + @Column(name = "name") + private BgpPeer.Detail name; + + @Column(name = "value", length = 1024) + private String value; + + @Column(name = "display") + private boolean display; + + public BgpPeerDetailsVO() { + } + + public BgpPeerDetailsVO(long resourceId, BgpPeer.Detail detailName, String value, boolean display) { + this.resourceId = resourceId; + this.name = detailName; + this.value = value; + this.display = display; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getResourceId() { + return resourceId; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public String getName() { + return name.name(); + } + + public BgpPeer.Detail getDetailName() { + return name; + } + + public String getValue() { + return value; + } + + @Override + public boolean isDisplay() { + return display; + } + + public void setId(long id) { + this.id = id; + } + + public void setName(BgpPeer.Detail name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerNetworkMapVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerNetworkMapVO.java new file mode 100644 index 00000000000..b520ecd5cd1 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerNetworkMapVO.java @@ -0,0 +1,104 @@ +// 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.network; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +@Entity +@Table(name = "bgp_peer_network_map") +public class BgpPeerNetworkMapVO implements InternalIdentity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "bgp_peer_id") + private long bgpPeerId; + + @Column(name = "network_id") + private Long networkId; + + @Column(name = "vpc_id") + private Long vpcId; + + @Column(name = "state") + private BgpPeer.State state; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name= GenericDao.REMOVED_COLUMN) + private Date removed; + + /** + * There should never be a public constructor for this class. Since it's + * only here to define the table for the DAO class. + */ + protected BgpPeerNetworkMapVO() { + } + + public BgpPeerNetworkMapVO(long bgpPeerId, Long networkId, Long vpcId, BgpPeer.State state) { + this.bgpPeerId = bgpPeerId; + this.networkId = networkId; + this.vpcId = vpcId; + this.state = state; + } + + @Override + public long getId() { + return id; + } + + public long getBgpPeerId() { + return bgpPeerId; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getVpcId() { + return vpcId; + } + + public BgpPeer.State getState() { + return state; + } + + public void setState(BgpPeer.State state) { + this.state = state; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerVO.java new file mode 100644 index 00000000000..0203b34fb1e --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerVO.java @@ -0,0 +1,170 @@ +// 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.network; + +import java.util.Date; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.utils.db.GenericDao; + +@Entity +@Table(name = "bgp_peers") +public class BgpPeerVO implements BgpPeer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "ip4_address") + private String ip4Address; + + @Column(name = "ip6_address") + private String ip6Address; + + @Column(name = "as_number") + private Long asNumber; + + @Column(name = "password") + private String password; + + @Column(name = "domain_id") + Long domainId; + + @Column(name = "account_id") + Long accountId; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name= GenericDao.REMOVED_COLUMN) + private Date removed; + + protected BgpPeerVO() { + uuid = UUID.randomUUID().toString(); + } + + public BgpPeerVO(long dcId, String ip4Address, String ip6Address, Long asNumber, String password) { + this(); + this.dataCenterId = dcId; + this.ip4Address = ip4Address; + this.ip6Address = ip6Address; + this.asNumber = asNumber; + this.password = password; + } + + @Override + public String toString() { + return String.format("BgpPeerVO [%s|%s|%s]", asNumber, ip4Address, ip6Address); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public long getDataCenterId() { + return dataCenterId; + } + + public void setDataCenterId(long dataCenterId) { + this.dataCenterId = dataCenterId; + } + + @Override + public String getIp4Address() { + return ip4Address; + } + + public void setIp4Address(String ip4Address) { + this.ip4Address = ip4Address; + } + + @Override + public String getIp6Address() { + return ip6Address; + } + + public void setIp6Address(String ip6Address) { + this.ip6Address = ip6Address; + } + + @Override + public Long getAsNumber() { + return asNumber; + } + + public void setAsNumber(Long asNumber) { + this.asNumber = asNumber; + } + + @Override + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public Long getDomainId() { + return domainId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + @Override + public Long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + @Override + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMapVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMapVO.java new file mode 100644 index 00000000000..cc726ba3d35 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMapVO.java @@ -0,0 +1,143 @@ +// 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.network; + +import java.util.Date; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import com.cloud.utils.db.GenericDao; + +@Entity +@Table(name = "ip4_guest_subnet_network_map") +public class Ipv4GuestSubnetNetworkMapVO implements Ipv4GuestSubnetNetworkMap { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "parent_id") + private Long parentId; + + @Column(name = "subnet") + private String subnet; + + @Column(name = "vpc_id") + private Long vpcId; + + @Column(name = "network_id") + private Long networkId; + + @Column(name = "state") + private State state; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "allocated") + Date allocated; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name= GenericDao.REMOVED_COLUMN) + private Date removed; + + protected Ipv4GuestSubnetNetworkMapVO() { + uuid = UUID.randomUUID().toString(); + } + + protected Ipv4GuestSubnetNetworkMapVO(Long parentId, String subnet, Long networkId, Ipv4GuestSubnetNetworkMap.State state) { + this.parentId = parentId; + this.subnet = subnet; + this.networkId = networkId; + this.state = state; + uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public Long getParentId() { + return parentId; + } + + @Override + public String getSubnet() { + return subnet; + } + + @Override + public Long getVpcId() { + return vpcId; + } + + public void setVpcId(Long vpcId) { + this.vpcId = vpcId; + } + + @Override + public Long getNetworkId() { + return networkId; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + @Override + public State getState() { + return state; + } + + public void setState(Ipv4GuestSubnetNetworkMap.State state) { + this.state = state; + } + + public void setAllocated(Date allocated) { + this.allocated = allocated; + } + + @Override + public Date getAllocated() { + return allocated; + } + + @Override + public Date getCreated() { + return created; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDao.java new file mode 100644 index 00000000000..8ca4c2d86da --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDao.java @@ -0,0 +1,40 @@ +// 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.network.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerVO; + +import java.util.List; +import java.util.Map; + +public interface BgpPeerDao extends GenericDao { + List listNonRevokeByNetworkId(long networkId); + + List listNonRevokeByVpcId(long vpcId); + + BgpPeerVO findByZoneAndAsNumberAndAddress(long zoneId, Long asNumber, String ip4Address, String ip6Address); + + BgpPeerVO persist(BgpPeerVO bgpPeerVO, Map details); + + List listAvailableBgpPeerIdsForAccount(long zoneId, long domainId, long accountId, boolean useSystemBgpPeers); + + int removeByAccountId(long accountId); + int removeByDomainId(long domainId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDaoImpl.java new file mode 100644 index 00000000000..0f95f7c3cd5 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDaoImpl.java @@ -0,0 +1,193 @@ +// 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.network.dao; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerDetailsVO; +import org.apache.cloudstack.network.BgpPeerNetworkMapVO; +import org.apache.cloudstack.network.BgpPeerVO; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Component +@DB +public class BgpPeerDaoImpl extends GenericDaoBase implements BgpPeerDao { + protected SearchBuilder NetworkIdSearch; + protected SearchBuilder VpcIdSearch; + protected SearchBuilder AllFieldsSearch; + + private static final String LIST_ALL_BGP_PEERS_IDS_FOR_ACCOUNT = "SELECT id FROM `cloud`.`bgp_peers` WHERE removed IS NULL AND data_center_id = ? " + + "AND ((domain_id IS NULL AND account_id IS NULL) " + + "OR (domain_id = ? AND account_id IS NULL) " + + "OR (domain_id = ? AND account_id = ?))"; + + private static final String LIST_DEDICATED_BGP_PEERS_IDS_FOR_ACCOUNT = "SELECT id FROM `cloud`.`bgp_peers` WHERE removed IS NULL AND data_center_id = ? " + + "AND ((domain_id = ? AND account_id IS NULL) " + + "OR (domain_id = ? AND account_id = ?))"; + + @Inject + BgpPeerNetworkMapDao bgpPeerNetworkMapDao; + @Inject + BgpPeerDetailsDao bgpPeerDetailsDao; + + @PostConstruct + public void init() { + final SearchBuilder networkSearchBuilder = bgpPeerNetworkMapDao.createSearchBuilder(); + networkSearchBuilder.and("networkId", networkSearchBuilder.entity().getNetworkId(), SearchCriteria.Op.EQ); + networkSearchBuilder.and("state", networkSearchBuilder.entity().getState(), SearchCriteria.Op.IN); + networkSearchBuilder.and("removed", networkSearchBuilder.entity().getRemoved(), SearchCriteria.Op.NULL); + NetworkIdSearch = createSearchBuilder(); + NetworkIdSearch.join("network", networkSearchBuilder, networkSearchBuilder.entity().getBgpPeerId(), + NetworkIdSearch.entity().getId(), JoinBuilder.JoinType.INNER); + NetworkIdSearch.done(); + + final SearchBuilder vpcSearchBuilder = bgpPeerNetworkMapDao.createSearchBuilder(); + vpcSearchBuilder.and("vpcId", vpcSearchBuilder.entity().getVpcId(), SearchCriteria.Op.EQ); + vpcSearchBuilder.and("state", vpcSearchBuilder.entity().getState(), SearchCriteria.Op.IN); + vpcSearchBuilder.and("removed", vpcSearchBuilder.entity().getRemoved(), SearchCriteria.Op.NULL); + VpcIdSearch = createSearchBuilder(); + VpcIdSearch.join("vpc", vpcSearchBuilder, vpcSearchBuilder.entity().getBgpPeerId(), + VpcIdSearch.entity().getId(), JoinBuilder.JoinType.INNER); + VpcIdSearch.done(); + + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("zoneId", AllFieldsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("domainId", AllFieldsSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("asNumber", AllFieldsSearch.entity().getAsNumber(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("ip4Address", AllFieldsSearch.entity().getIp4Address(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("ip6Address", AllFieldsSearch.entity().getIp6Address(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); + } + + @Override + public List listNonRevokeByNetworkId(long networkId) { + SearchCriteria sc = NetworkIdSearch.create(); + sc.setJoinParameters("network", "networkId", networkId); + sc.setJoinParameters("network", "state", BgpPeer.State.Active, BgpPeer.State.Add); + return listBy(sc); + } + + @Override + public List listNonRevokeByVpcId(long vpcId) { + SearchCriteria sc = VpcIdSearch.create(); + sc.setJoinParameters("vpc", "vpcId", vpcId); + sc.setJoinParameters("vpc", "state", BgpPeer.State.Active, BgpPeer.State.Add); + return listBy(sc); + } + + @Override + public BgpPeerVO findByZoneAndAsNumberAndAddress(long zoneId, Long asNumber, String ip4Address, String ip6Address) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters( "zoneId", zoneId); + sc.setParameters( "asNumber", asNumber); + if (ip4Address != null) { + sc.setParameters( "ip4Address", ip4Address); + } + if (ip6Address != null) { + sc.setParameters( "ip6Address", ip6Address); + } + return findOneBy(sc); + } + + @Override + public BgpPeerVO persist(BgpPeerVO bgpPeerVO, Map details) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + BgpPeerVO vo = super.persist(bgpPeerVO); + + // persist the details + if (details != null && !details.isEmpty()) { + for (BgpPeer.Detail detail : details.keySet()) { + bgpPeerDetailsDao.persist(new BgpPeerDetailsVO(bgpPeerVO.getId(), detail, details.get(detail), true)); + } + } + + txn.commit(); + return vo; + } + + @Override + public List listAvailableBgpPeerIdsForAccount(long zoneId, long domainId, long accountId, boolean useSystemBgpPeers) { + if (useSystemBgpPeers) { + return listBgpPeerIdsForAccount(zoneId, domainId, accountId, false); + } else { + List dedicatedBgpPeerIds = listBgpPeerIdsForAccount(zoneId, domainId, accountId, true); + if (CollectionUtils.isNotEmpty(dedicatedBgpPeerIds)) { + return dedicatedBgpPeerIds; + } + return listBgpPeerIdsForAccount(zoneId, domainId, accountId, false); + } + } + + private List listBgpPeerIdsForAccount(long zoneId, long domainId, long accountId, boolean isDedicated) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement pstmt = null; + List result = new ArrayList(); + + StringBuilder sql = isDedicated ? new StringBuilder(LIST_DEDICATED_BGP_PEERS_IDS_FOR_ACCOUNT): new StringBuilder(LIST_ALL_BGP_PEERS_IDS_FOR_ACCOUNT); + + try { + pstmt = txn.prepareAutoCloseStatement(sql.toString()); + pstmt.setLong(1, zoneId); + pstmt.setLong(2, domainId); + pstmt.setLong(3, domainId); + pstmt.setLong(4, accountId); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result.add(rs.getLong(1)); + } + return result; + } catch (SQLException e) { + throw new CloudRuntimeException("DB Exception on: " + sql, e); + } catch (Throwable e) { + throw new CloudRuntimeException("Caught: " + sql, e); + } + } + + @Override + public int removeByAccountId(long accountId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + return remove(sc); + } + + @Override + public int removeByDomainId(long domainId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + return remove(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDao.java new file mode 100644 index 00000000000..377bc45ebfe --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDao.java @@ -0,0 +1,33 @@ +// 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.network.dao; + +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerDetailsVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import java.util.List; +import java.util.Map; + +public interface BgpPeerDetailsDao extends ResourceDetailsDao { + Map getBgpPeerDetails(long bgpPeerId); + String getDetail(long offeringId, BgpPeer.Detail detailName); + List findDomainIds(final long resourceId); + List findZoneIds(final long resourceId); + + int removeByBgpPeerId(long bgpPeerId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDaoImpl.java new file mode 100644 index 00000000000..a974cf5e276 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDaoImpl.java @@ -0,0 +1,123 @@ +// 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.network.dao; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; + +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Func; +import com.cloud.utils.db.SearchCriteria.Op; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerDetailsVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.apache.commons.lang3.EnumUtils; + +public class BgpPeerDetailsDaoImpl extends ResourceDetailsDaoBase implements BgpPeerDetailsDao { + protected final SearchBuilder DetailSearch; + private final GenericSearchBuilder ValueSearch; + + public BgpPeerDetailsDaoImpl() { + + DetailSearch = createSearchBuilder(); + DetailSearch.and("resourceId", DetailSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ); + DetailSearch.and("value", DetailSearch.entity().getValue(), SearchCriteria.Op.EQ); + DetailSearch.and("display", DetailSearch.entity().isDisplay(), SearchCriteria.Op.EQ); + DetailSearch.done(); + + ValueSearch = createSearchBuilder(String.class); + ValueSearch.select(null, Func.DISTINCT, ValueSearch.entity().getValue()); + ValueSearch.and("resourceId", ValueSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + ValueSearch.and("name", ValueSearch.entity().getName(), Op.EQ); + ValueSearch.and("display", ValueSearch.entity().isDisplay(), SearchCriteria.Op.EQ); + ValueSearch.done(); + } + + @Override + public Map getBgpPeerDetails(long bgpPeerId) { + SearchCriteria sc = DetailSearch.create(); + sc.setParameters("resourceId", bgpPeerId); + sc.setParameters("display", true); + + List results = search(sc, null); + if (results.size() == 0) { + return null; + } + Map details = new HashMap<>(results.size()); + for (BgpPeerDetailsVO result : results) { + details.put(result.getDetailName(), result.getValue()); + } + + return details; + } + + @Override + public String getDetail(long bgpPeerId, BgpPeer.Detail detailName) { + SearchCriteria sc = ValueSearch.create(); + sc.setParameters("name", detailName); + sc.setParameters("resourceId", bgpPeerId); + List results = customSearch(sc, null); + if (results.isEmpty()) { + return null; + } else { + return results.get(0); + } + } + + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + persist(new BgpPeerDetailsVO(resourceId, EnumUtils.getEnumIgnoreCase(BgpPeer.Detail.class, key), value, display)); + } + + @Override + public List findDomainIds(long resourceId) { + final List domainIds = new ArrayList<>(); + for (final BgpPeerDetailsVO detail: findDetails(resourceId, ApiConstants.DOMAIN_ID)) { + final Long domainId = Long.valueOf(detail.getValue()); + if (domainId > 0) { + domainIds.add(domainId); + } + } + return domainIds; + } + + @Override + public List findZoneIds(long resourceId) { + final List zoneIds = new ArrayList<>(); + for (final BgpPeerDetailsVO detail: findDetails(resourceId, ApiConstants.ZONE_ID)) { + final Long zoneId = Long.valueOf(detail.getValue()); + if (zoneId > 0) { + zoneIds.add(zoneId); + } + } + return zoneIds; + } + + @Override + public int removeByBgpPeerId(long bgpPeerId) { + SearchCriteria sc = DetailSearch.create(); + sc.setParameters("resourceId", bgpPeerId); + return remove(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDao.java new file mode 100644 index 00000000000..8d8ec8c998a --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDao.java @@ -0,0 +1,48 @@ +// 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.network.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.network.BgpPeerNetworkMapVO; + +import java.util.List; + + +public interface BgpPeerNetworkMapDao extends GenericDao { + + void persistForNetwork(long networkId, List bgpPeerIds); + + List listByBgpPeerId(long bgpPeerId); + + List listByNetworkId(long networkId); + + List listUsedNetworksByOtherDomains(long bgpPeerId, Long domainId); + + List listUsedNetworksByOtherAccounts(long bgpPeerId, Long accountId); + + int removeByNetworkId(long networkId); + + void persistForVpc(long vpcId, List bgpPeerIds); + + List listByVpcId(long vpcId); + + List listUsedVpcsByOtherDomains(long bgpPeerId, Long domainId); + + List listUsedVpcsByOtherAccounts(long bgpPeerId, Long accountId); + + int removeByVpcId(long vpcId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDaoImpl.java new file mode 100644 index 00000000000..a5e5f47684a --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDaoImpl.java @@ -0,0 +1,185 @@ +// 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.network.dao; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.utils.db.JoinBuilder; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerNetworkMapVO; +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; + +@Component +public class BgpPeerNetworkMapDaoImpl extends GenericDaoBase implements BgpPeerNetworkMapDao { + + protected SearchBuilder BgpPeerNetworkVpcSearch; + protected SearchBuilder NetworkDomainAccountNeqSearch; + protected SearchBuilder VpcDomainAccountNeqSearch; + + @Inject + NetworkDao networkDao; + @Inject + VpcDao vpcDao; + + public BgpPeerNetworkMapDaoImpl() { + } + + @PostConstruct + public void init() { + BgpPeerNetworkVpcSearch = createSearchBuilder(); + BgpPeerNetworkVpcSearch.and("bgpPeerId", BgpPeerNetworkVpcSearch.entity().getBgpPeerId(), SearchCriteria.Op.EQ); + BgpPeerNetworkVpcSearch.and("networkId", BgpPeerNetworkVpcSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + BgpPeerNetworkVpcSearch.and("vpcId", BgpPeerNetworkVpcSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + BgpPeerNetworkVpcSearch.done(); + + final SearchBuilder networkSearchBuilder = networkDao.createSearchBuilder(); + networkSearchBuilder.and("domainId", networkSearchBuilder.entity().getDomainId(), SearchCriteria.Op.NEQ); + networkSearchBuilder.and("accountId", networkSearchBuilder.entity().getAccountId(), SearchCriteria.Op.NEQ); + NetworkDomainAccountNeqSearch = createSearchBuilder(); + NetworkDomainAccountNeqSearch.and("bgpPeerId", NetworkDomainAccountNeqSearch.entity().getBgpPeerId(), SearchCriteria.Op.EQ); + NetworkDomainAccountNeqSearch.join("network", networkSearchBuilder, networkSearchBuilder.entity().getId(), + NetworkDomainAccountNeqSearch.entity().getNetworkId(), JoinBuilder.JoinType.INNER); + NetworkDomainAccountNeqSearch.done(); + + final SearchBuilder vpcSearchBuilder = vpcDao.createSearchBuilder(); + vpcSearchBuilder.and("domainId", vpcSearchBuilder.entity().getDomainId(), SearchCriteria.Op.NEQ); + vpcSearchBuilder.and("accountId", vpcSearchBuilder.entity().getAccountId(), SearchCriteria.Op.NEQ); + VpcDomainAccountNeqSearch = createSearchBuilder(); + VpcDomainAccountNeqSearch.and("bgpPeerId", VpcDomainAccountNeqSearch.entity().getBgpPeerId(), SearchCriteria.Op.EQ); + VpcDomainAccountNeqSearch.join("vpc", vpcSearchBuilder, vpcSearchBuilder.entity().getId(), + VpcDomainAccountNeqSearch.entity().getVpcId(), JoinBuilder.JoinType.INNER); + VpcDomainAccountNeqSearch.done(); + } + + @Override + public void persistForNetwork(long networkId, List bgpPeerIds) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + txn.start(); + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("networkId", networkId); + expunge(sc); + + for (Long bgpPeerId : bgpPeerIds) { + BgpPeerNetworkMapVO vo = new BgpPeerNetworkMapVO(bgpPeerId, networkId, null, BgpPeer.State.Active); + persist(vo); + } + + txn.commit(); + } + + @Override + public List listByBgpPeerId(long bgpPeerId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + + return search(sc, null); + } + + @Override + public List listByNetworkId(long networkId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("networkId", networkId); + + return search(sc, null); + } + + @Override + public List listUsedNetworksByOtherDomains(long bgpPeerId, Long domainId) { + SearchCriteria sc = NetworkDomainAccountNeqSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + sc.setJoinParameters("network", "domainId", domainId); + return listBy(sc); + } + + @Override + public List listUsedNetworksByOtherAccounts(long bgpPeerId, Long accountId) { + SearchCriteria sc = NetworkDomainAccountNeqSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + sc.setJoinParameters("network", "accountId", accountId); + return listBy(sc); + } + + @Override + public int removeByNetworkId(long networkId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("networkId", networkId); + + return remove(sc); + } + + @Override + public void persistForVpc(long vpcId, List bgpPeerIds) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + txn.start(); + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("vpcId", vpcId); + expunge(sc); + + for (Long bgpPeerId : bgpPeerIds) { + BgpPeerNetworkMapVO vo = new BgpPeerNetworkMapVO(bgpPeerId, null, vpcId, BgpPeer.State.Active); + persist(vo); + } + + txn.commit(); + } + + @Override + public List listByVpcId(long vpcId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("vpcId", vpcId); + + return search(sc, null); + } + + @Override + public List listUsedVpcsByOtherDomains(long bgpPeerId, Long domainId) { + SearchCriteria sc = VpcDomainAccountNeqSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + sc.setJoinParameters("vpc", "domainId", domainId); + return listBy(sc); + } + + @Override + public List listUsedVpcsByOtherAccounts(long bgpPeerId, Long accountId) { + SearchCriteria sc = VpcDomainAccountNeqSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + sc.setJoinParameters("vpc", "accountId", accountId); + return listBy(sc); + } + + @Override + public int removeByVpcId(long vpcId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("vpcId", vpcId); + + return remove(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDao.java new file mode 100644 index 00000000000..c3f860009db --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDao.java @@ -0,0 +1,38 @@ +// 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.network.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMapVO; + +public interface Ipv4GuestSubnetNetworkMapDao extends GenericDao { + List listByParent(long parentId); + List listUsedByParent(long parentId); + List listUsedByOtherDomains(long parentId, Long domainId); + List listUsedByOtherAccounts(long parentId, Long accountId); + Ipv4GuestSubnetNetworkMapVO findFirstAvailable(long parentId, long cidrSize); + Ipv4GuestSubnetNetworkMapVO findByNetworkId(long networkId); + Ipv4GuestSubnetNetworkMapVO findByVpcId(long vpcId); + Ipv4GuestSubnetNetworkMapVO findBySubnet(String subnet); + List findSubnetsInStates(Ipv4GuestSubnetNetworkMap.State... states); + void deleteByParentId(long parentId); + List listAllNoParent(); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDaoImpl.java new file mode 100644 index 00000000000..95e53448907 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDaoImpl.java @@ -0,0 +1,170 @@ +// 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.network.dao; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMapVO; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@DB +public class Ipv4GuestSubnetNetworkMapDaoImpl extends GenericDaoBase implements Ipv4GuestSubnetNetworkMapDao { + + protected SearchBuilder ParentStateSearch; + protected SearchBuilder ParentIdSearch; + protected SearchBuilder NoParentSearch; + protected SearchBuilder NetworkIdSearch; + protected SearchBuilder SubnetSearch; + protected SearchBuilder StatesSearch; + protected SearchBuilder DomainAccountNeqSearch; + + @Inject + NetworkDao networkDao; + + @PostConstruct + public void init() { + ParentStateSearch = createSearchBuilder(); + ParentStateSearch.and("parentId", ParentStateSearch.entity().getParentId(), SearchCriteria.Op.EQ); + ParentStateSearch.and("state", ParentStateSearch.entity().getState(), SearchCriteria.Op.IN); + ParentStateSearch.and("subnet", ParentStateSearch.entity().getSubnet(), SearchCriteria.Op.LIKE); + ParentStateSearch.done(); + ParentIdSearch = createSearchBuilder(); + ParentIdSearch.and("parentId", ParentIdSearch.entity().getParentId(), SearchCriteria.Op.EQ); + ParentIdSearch.done(); + NoParentSearch = createSearchBuilder(); + NoParentSearch.and("parentId", NoParentSearch.entity().getParentId(), SearchCriteria.Op.NULL); + NoParentSearch.done(); + NetworkIdSearch = createSearchBuilder(); + NetworkIdSearch.and("networkId", NetworkIdSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + NetworkIdSearch.and("vpcId", NetworkIdSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + NetworkIdSearch.done(); + SubnetSearch = createSearchBuilder(); + SubnetSearch.and("subnet", SubnetSearch.entity().getSubnet(), SearchCriteria.Op.EQ); + SubnetSearch.done(); + StatesSearch = createSearchBuilder(); + StatesSearch.and("state", StatesSearch.entity().getState(), SearchCriteria.Op.IN); + StatesSearch.done(); + + final SearchBuilder networkSearchBuilder = networkDao.createSearchBuilder(); + networkSearchBuilder.and("domainId", networkSearchBuilder.entity().getDomainId(), SearchCriteria.Op.NEQ); + networkSearchBuilder.and("accountId", networkSearchBuilder.entity().getAccountId(), SearchCriteria.Op.NEQ); + DomainAccountNeqSearch = createSearchBuilder(); + DomainAccountNeqSearch.and("parentId", DomainAccountNeqSearch.entity().getParentId(), SearchCriteria.Op.EQ); + DomainAccountNeqSearch.join("network", networkSearchBuilder, networkSearchBuilder.entity().getId(), + DomainAccountNeqSearch.entity().getNetworkId(), JoinBuilder.JoinType.INNER); + DomainAccountNeqSearch.done(); + } + + @Override + public List listByParent(long parentId) { + SearchCriteria sc = ParentIdSearch.create(); + sc.setParameters("parentId", parentId); + return listBy(sc, null); + } + + @Override + public List listUsedByParent(long parentId) { + SearchCriteria sc = ParentStateSearch.create(); + sc.setParameters("parentId", parentId); + sc.setParameters("state", (Object[]) new Ipv4GuestSubnetNetworkMap.State[]{Ipv4GuestSubnetNetworkMap.State.Allocated, Ipv4GuestSubnetNetworkMap.State.Allocating}); + return listBy(sc, null); + } + + @Override + public List listUsedByOtherDomains(long parentId, Long domainId) { + SearchCriteria sc = DomainAccountNeqSearch.create(); + sc.setParameters("parentId", parentId); + sc.setJoinParameters("network", "domainId", domainId); + return listBy(sc); + } + + @Override + public List listUsedByOtherAccounts(long parentId, Long accountId) { + SearchCriteria sc = DomainAccountNeqSearch.create(); + sc.setParameters("parentId", parentId); + sc.setJoinParameters("network", "accountId", accountId); + return listBy(sc); + } + + @Override + public Ipv4GuestSubnetNetworkMapVO findFirstAvailable(long parentId, long cidrSize) { + SearchCriteria sc = ParentStateSearch.create(); + sc.setParameters("parentId", parentId); + sc.setParameters("subnet", "%/" + cidrSize); + sc.setParameters("state", (Object[]) new Ipv4GuestSubnetNetworkMap.State[]{Ipv4GuestSubnetNetworkMap.State.Free}); + Filter searchFilter = new Filter(Ipv4GuestSubnetNetworkMapVO.class, "id", true, null, 1L); + List list = listBy(sc, searchFilter); + return CollectionUtils.isNotEmpty(list) ? list.get(0) : null; + } + + @Override + public Ipv4GuestSubnetNetworkMapVO findByNetworkId(long networkId) { + SearchCriteria sc = NetworkIdSearch.create(); + sc.setParameters("networkId", networkId); + return findOneBy(sc); + } + + @Override + public Ipv4GuestSubnetNetworkMapVO findByVpcId(long vpcId) { + SearchCriteria sc = NetworkIdSearch.create(); + sc.setParameters("vpcId", vpcId); + return findOneBy(sc); + } + + @Override + public Ipv4GuestSubnetNetworkMapVO findBySubnet(String subnet) { + SearchCriteria sc = SubnetSearch.create(); + sc.setParameters("subnet", subnet); + return findOneBy(sc); + } + + @Override + public List findSubnetsInStates(Ipv4GuestSubnetNetworkMap.State... states) { + SearchCriteria sc = StatesSearch.create(); + sc.setParameters("state", (Object[])states); + return listBy(sc); + } + + @Override + public void deleteByParentId(long parentId) { + SearchCriteria sc = ParentIdSearch.create(); + sc.setParameters("parentId", parentId); + remove(sc); + } + + @Override + public List listAllNoParent() { + SearchCriteria sc = NoParentSearch.create(); + return listBy(sc, null); + } +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 0be1e610009..171685ce413 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -290,6 +290,13 @@ + + + + + + + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql index 041997557aa..582460cb10b 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql @@ -158,6 +158,130 @@ WHERE name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth") AND value NOT IN ("true", "y", "t", "1", "on", "yes"); +-- Create tables for static and dynamic routing +CREATE TABLE `cloud`.`dc_ip4_guest_subnets` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40) DEFAULT NULL, + `data_center_id` bigint(20) unsigned NOT NULL COMMENT 'zone it belongs to', + `subnet` varchar(255) NOT NULL COMMENT 'subnet of the ip4 network', + `domain_id` bigint unsigned DEFAULT NULL COMMENT 'domain the subnet belongs to', + `account_id` bigint unsigned DEFAULT NULL COMMENT 'owner of this subnet', + `created` datetime DEFAULT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_dc_ip4_guest_subnets__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center`(`id`), + CONSTRAINT `fk_dc_ip4_guest_subnets__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`), + CONSTRAINT `fk_dc_ip4_guest_subnets__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`), + CONSTRAINT `uc_dc_ip4_guest_subnets__uuid` UNIQUE (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`ip4_guest_subnet_network_map` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40) DEFAULT NULL, + `parent_id` bigint(20) unsigned COMMENT 'ip4 guest subnet which subnet belongs to', + `subnet` varchar(255) NOT NULL COMMENT 'subnet of the ip4 network', + `network_id` bigint(20) unsigned DEFAULT NULL COMMENT 'network which subnet is associated to', + `vpc_id` bigint(20) unsigned DEFAULT NULL COMMENT 'VPC which subnet is associated to', + `state` varchar(255) NOT NULL COMMENT 'state of the subnet', + `allocated` datetime DEFAULT NULL, + `created` datetime DEFAULT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_ip4_guest_subnet_network_map__parent_id` FOREIGN KEY (`parent_id`) REFERENCES `dc_ip4_guest_subnets`(`id`), + CONSTRAINT `fk_ip4_guest_subnet_network_map__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks`(`id`), + CONSTRAINT `fk_ip4_guest_subnet_network_map__vpc_id` FOREIGN KEY (`vpc_id`) REFERENCES `vpc`(`id`), + CONSTRAINT `uc_ip4_guest_subnet_network_map__uuid` UNIQUE (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `cloud`.`network_offerings` RENAME COLUMN `nsx_mode` TO `network_mode`; +ALTER TABLE `cloud`.`vpc_offerings` RENAME COLUMN `nsx_mode` TO `network_mode`; +ALTER TABLE `cloud`.`event` MODIFY COLUMN `type` varchar(50) NOT NULL; + +-- Add tables for AS Numbers and range +CREATE TABLE IF NOT EXISTS `cloud`.`as_number_range` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) DEFAULT NULL, + `data_center_id` bigint unsigned NOT NULL COMMENT 'zone that it belongs to', + `start_as_number` bigint unsigned NOT NULL COMMENT 'start AS number of the range', + `end_as_number` bigint unsigned NOT NULL COMMENT 'end AS number of the range', + `created` datetime DEFAULT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'date removed', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_as_number_range__uuid` (`uuid`), + UNIQUE KEY `uk_as_number_range__range` (`data_center_id`,`start_as_number`,`end_as_number`, `removed`), + CONSTRAINT `fk_as_number_range__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`as_number` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) DEFAULT NULL, + `account_id` bigint unsigned DEFAULT NULL, + `domain_id` bigint unsigned DEFAULT NULL, + `as_number` bigint unsigned NOT NULL COMMENT 'the AS Number', + `as_number_range_id` bigint unsigned NOT NULL, + `data_center_id` bigint unsigned NOT NULL COMMENT 'zone that it belongs to', + `allocated` datetime DEFAULT NULL COMMENT 'Date this AS Number was allocated to some network', + `is_allocated` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'indicates if the AS Number is allocated to some network', + `network_id` bigint unsigned DEFAULT NULL COMMENT 'Network this AS Number is associated with', + `vpc_id` bigint unsigned DEFAULT NULL COMMENT 'VPC this AS Number is associated with', + `created` datetime DEFAULT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'date removed', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_as_number__uuid` (`uuid`), + UNIQUE KEY `uk_as_number__number` (`data_center_id`,`as_number`,`as_number_range_id`), + CONSTRAINT `fk_as_number__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`), + CONSTRAINT `fk_as_number__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_as_number__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`), + CONSTRAINT `fk_as_number__as_number_range_id` FOREIGN KEY (`as_number_range_id`) REFERENCES `as_number_range` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','routing_mode', 'varchar(10) COMMENT "routing mode for the offering"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','specify_as_number', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "specify AS number when using dynamic routing"'); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','routing_mode', 'varchar(10) COMMENT "routing mode for the offering"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','specify_as_number', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "specify AS number when using dynamic routing"'); + +-- Tables for Dynamic Routing +CREATE TABLE IF NOT EXISTS `cloud`.`bgp_peers` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) DEFAULT NULL, + `data_center_id` bigint(20) unsigned NOT NULL COMMENT 'zone it belongs to', + `ip4_address` varchar(40) DEFAULT NULL COMMENT 'IPv4 address of the BGP peer', + `ip6_address` varchar(40) DEFAULT NULL COMMENT 'IPv6 address of the BGP peer', + `as_number` bigint unsigned NOT NULL COMMENT 'AS number of the BGP peer', + `password` varchar(255) DEFAULT NULL COMMENT 'Password of the BGP peer', + `domain_id` bigint unsigned DEFAULT NULL COMMENT 'domain the subnet belongs to', + `account_id` bigint unsigned DEFAULT NULL COMMENT 'owner of this subnet', + `created` datetime DEFAULT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'date removed', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_bgp_peers__uuid` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`bgp_peer_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `bgp_peer_id` bigint unsigned NOT NULL COMMENT 'bgp peer id', + `name` varchar(255) NOT NULL, + `value` varchar(1024) NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_bgp_peer_details__bgp_peer_id` FOREIGN KEY `fk_bgp_peer_details__bgp_peer_id`(`bgp_peer_id`) REFERENCES `bgp_peers`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`bgp_peer_network_map` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `bgp_peer_id` bigint(20) unsigned COMMENT 'id of the BGP peer', + `network_id` bigint(20) unsigned DEFAULT NULL COMMENT 'network which BGP peer is associated to', + `vpc_id` bigint(20) unsigned DEFAULT NULL COMMENT 'vpc which BGP peer is associated to', + `state` varchar(40) DEFAULT NULL, + `created` datetime DEFAULT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'date removed', + PRIMARY KEY (`id`), + CONSTRAINT `fk_bgp_peer_network_map__bgp_peer_id` FOREIGN KEY (`bgp_peer_id`) REFERENCES `bgp_peers`(`id`), + CONSTRAINT `fk_bgp_peer_network_map__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks`(`id`), + CONSTRAINT `fk_bgp_peer_network_map__vpc_id` FOREIGN KEY (`vpc_id`) REFERENCES `vpc`(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + CREATE TABLE `cloud`.`shared_filesystem`( `id` bigint unsigned NOT NULL auto_increment COMMENT 'ID', `uuid` varchar(40) COMMENT 'UUID', diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql index bae73deda32..b6abaabcd48 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql @@ -61,8 +61,10 @@ SELECT `network_offerings`.`for_vpc` AS `for_vpc`, `network_offerings`.`for_tungsten` AS `for_tungsten`, `network_offerings`.`for_nsx` AS `for_nsx`, - `network_offerings`.`nsx_mode` AS `nsx_mode`, + `network_offerings`.`network_mode` AS `network_mode`, `network_offerings`.`service_package_id` AS `service_package_id`, + `network_offerings`.`routing_mode` AS `routing_mode`, + `network_offerings`.`specify_as_number` AS `specify_as_number`, GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql index 9aca869b510..c74d50590de 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql @@ -29,7 +29,7 @@ select `vpc_offerings`.`state` AS `state`, `vpc_offerings`.`default` AS `default`, `vpc_offerings`.`for_nsx` AS `for_nsx`, - `vpc_offerings`.`nsx_mode` AS `nsx_mode`, + `vpc_offerings`.`network_mode` AS `network_mode`, `vpc_offerings`.`created` AS `created`, `vpc_offerings`.`removed` AS `removed`, `vpc_offerings`.`service_offering_id` AS `service_offering_id`, @@ -37,6 +37,8 @@ select `vpc_offerings`.`supports_region_level_vpc` AS `supports_region_level_vpc`, `vpc_offerings`.`redundant_router_service` AS `redundant_router_service`, `vpc_offerings`.`sort_key` AS `sort_key`, + `vpc_offerings`.`routing_mode` AS `routing_mode`, + `vpc_offerings`.`specify_as_number` AS `specify_as_number`, group_concat(distinct `domain`.`id` separator ',') AS `domain_id`, group_concat(distinct `domain`.`uuid` separator ',') AS `domain_uuid`, group_concat(distinct `domain`.`name` separator ',') AS `domain_name`, diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index 06f92181506..39e4bbf935f 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -40,6 +40,8 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.uservm.UserVm; +import com.cloud.vm.UserVmService; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.annotation.AnnotationService; @@ -69,6 +71,7 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -157,7 +160,6 @@ import com.cloud.user.UserAccount; import com.cloud.user.UserVO; import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserDao; -import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.component.ComponentContext; @@ -175,7 +177,6 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.net.NetUtils; -import com.cloud.vm.UserVmService; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDao; @@ -264,6 +265,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne @Inject private UserVmService userVmService; + @Inject + RoutedIpv4Manager routedIpv4Manager; private void logMessage(final Level logLevel, final String message, final Exception e) { if (logLevel == Level.WARN) { @@ -429,6 +432,13 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne if (!networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData)) { throw new InvalidParameterValueException(String.format("Network ID: %s does not support userdata that is required for Kubernetes cluster", network.getUuid())); } + if (!networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)) { + throw new InvalidParameterValueException(String.format("Network ID: %s does not support DHCP that is required for Kubernetes cluster", network.getUuid())); + } + if (routedIpv4Manager.isRoutedNetwork(network)) { + logger.debug("No need to add firewall and port forwarding rules for ROUTED network. Assume the VMs are reachable."); + return; + } Long vpcId = network.getVpcId(); if (vpcId == null && !networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall)) { throw new InvalidParameterValueException(String.format("Network ID: %s does not support firewall that is required for Kubernetes cluster", network.getUuid())); @@ -436,9 +446,6 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne if (!networkModel.areServicesSupportedInNetwork(network.getId(), Service.PortForwarding)) { throw new InvalidParameterValueException(String.format("Network ID: %s does not support port forwarding that is required for Kubernetes cluster", network.getUuid())); } - if (!networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)) { - throw new InvalidParameterValueException(String.format("Network ID: %s does not support DHCP that is required for Kubernetes cluster", network.getUuid())); - } if (network.getVpcId() != null) { validateVpcTier(network); return; @@ -592,7 +599,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne if (ntwk != null) { response.setNetworkId(ntwk.getUuid()); response.setAssociatedNetworkName(ntwk.getName()); - if (ntwk.getGuestType() == Network.GuestType.Isolated) { + if (!isDirectAccess(ntwk)) { List ipAddresses = ipAddressDao.listByAssociatedNetwork(ntwk.getId(), true); if (ipAddresses != null && ipAddresses.size() == 1) { response.setIpAddress(ipAddresses.get(0).getAddress().addr()); @@ -829,8 +836,9 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne if (network == null) { throw new InvalidParameterValueException(String.format("%s parameter must be specified along with %s parameter", ApiConstants.EXTERNAL_LOAD_BALANCER_IP_ADDRESS, ApiConstants.NETWORK_ID)); } - if (Network.GuestType.Shared.equals(network.getGuestType())) { - throw new InvalidParameterValueException(String.format("%s parameter must be specified along with %s type of network", ApiConstants.EXTERNAL_LOAD_BALANCER_IP_ADDRESS, Network.GuestType.Shared.toString())); + if (isDirectAccess(network)) { + throw new InvalidParameterValueException(String.format("%s parameter must be specified along with %s network or %s network", + ApiConstants.EXTERNAL_LOAD_BALANCER_IP_ADDRESS, Network.GuestType.Shared, NetworkOffering.NetworkMode.ROUTED)); } } @@ -851,7 +859,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne } else { throw new InvalidParameterValueException(String.format("Network ID: %s is already under use by another Kubernetes cluster", network.getUuid())); } - } else if (Network.GuestType.Shared.equals(network.getGuestType())) { + } + if (isDirectAccess(network)) { if (controlNodesCount > 1 && StringUtils.isEmpty(externalLoadBalancerIpAddress)) { throw new InvalidParameterValueException(String.format("Multi-control nodes, HA Kubernetes cluster with %s network ID: %s needs an external load balancer IP address. %s parameter can be used", network.getGuestType().toString(), network.getUuid(), ApiConstants.EXTERNAL_LOAD_BALANCER_IP_ADDRESS)); @@ -893,7 +902,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne List details = new ArrayList<>(); long kubernetesClusterId = kubernetesCluster.getId(); - if ((network != null && Network.GuestType.Shared.equals(network.getGuestType())) || kubernetesCluster.getClusterType() == KubernetesCluster.ClusterType.ExternalManaged) { + if ((network != null && isDirectAccess(network)) || kubernetesCluster.getClusterType() == KubernetesCluster.ClusterType.ExternalManaged) { addKubernetesClusterDetailIfIsNotEmpty(details, kubernetesClusterId, ApiConstants.EXTERNAL_LOAD_BALANCER_IP_ADDRESS, externalLoadBalancerIpAddress, true); } @@ -1999,7 +2008,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne false, false, false, true, true, false, forVpc, true, false, false); if (forNsx) { - defaultKubernetesServiceNetworkOffering.setNsxMode(NetworkOffering.NsxMode.NATTED.name()); + defaultKubernetesServiceNetworkOffering.setNetworkMode(NetworkOffering.NetworkMode.NATTED); defaultKubernetesServiceNetworkOffering.setForNsx(true); } defaultKubernetesServiceNetworkOffering.setSupportsVmAutoScaling(true); @@ -2015,6 +2024,11 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne } } + @Override + public boolean isDirectAccess(Network network) { + return Network.GuestType.Shared.equals(network.getGuestType()) || routedIpv4Manager.isRoutedNetwork(network); + } + @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java index 6acc876493e..9d86c564de4 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.response.RemoveVirtualMachinesFromKubernetesClu import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import com.cloud.network.Network; import com.cloud.utils.component.PluggableService; import com.cloud.utils.exception.CloudRuntimeException; @@ -122,4 +123,6 @@ public interface KubernetesClusterService extends PluggableService, Configurable boolean addVmsToCluster(AddVirtualMachinesToKubernetesClusterCmd cmd); List removeVmsFromCluster(RemoveVirtualMachinesFromKubernetesClusterCmd cmd); + + boolean isDirectAccess(Network network); } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java index 0ef3ee96d32..2d993ac3cb6 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java @@ -65,7 +65,6 @@ import com.cloud.kubernetes.version.dao.KubernetesSupportedVersionDao; import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; -import com.cloud.network.Network.GuestType; import com.cloud.network.NetworkModel; import com.cloud.network.NetworkService; import com.cloud.network.dao.IPAddressDao; @@ -429,13 +428,14 @@ public class KubernetesClusterActionWorker { logger.warn(String.format("Network for Kubernetes cluster : %s cannot be found", kubernetesCluster.getName())); return new Pair<>(null, port); } + if (manager.isDirectAccess(network)) { + return getKubernetesClusterServerIpSshPortForSharedNetwork(controlVm); + } if (network.getVpcId() != null) { return getKubernetesClusterServerIpSshPortForVpcTier(network, acquireNewPublicIpForVpcTierIfNeeded); } if (Network.GuestType.Isolated.equals(network.getGuestType())) { return getKubernetesClusterServerIpSshPortForIsolatedNetwork(network); - } else if (Network.GuestType.Shared.equals(network.getGuestType())) { - return getKubernetesClusterServerIpSshPortForSharedNetwork(controlVm); } logger.warn(String.format("Unable to retrieve server IP address for Kubernetes cluster : %s", kubernetesCluster.getName())); return new Pair<>(null, port); @@ -654,7 +654,7 @@ public class KubernetesClusterActionWorker { protected boolean deployProvider() { Network network = networkDao.findById(kubernetesCluster.getNetworkId()); // Since the provider creates IP addresses, don't deploy it unless the underlying network supports it - if (network.getGuestType() != GuestType.Isolated) { + if (manager.isDirectAccess(network)) { logMessage(Level.INFO, String.format("Skipping adding the provider as %s is not on an isolated network", kubernetesCluster.getName()), null); return true; diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java index 047d32f1f9f..50d7fb14085 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java @@ -223,7 +223,7 @@ public class KubernetesClusterDestroyWorker extends KubernetesClusterResourceMod private void checkForRulesToDelete() throws ManagementServerException { NetworkVO kubernetesClusterNetwork = networkDao.findById(kubernetesCluster.getNetworkId()); - if (kubernetesClusterNetwork != null && kubernetesClusterNetwork.getGuestType() != Network.GuestType.Shared) { + if (kubernetesClusterNetwork != null && !manager.isDirectAccess(kubernetesClusterNetwork)) { deleteKubernetesClusterNetworkRules(); } } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java index f5ac553733d..aa500de9190 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java @@ -163,9 +163,9 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif * @throws ManagementServerException */ private void scaleKubernetesClusterNetworkRules(final List clusterVMIds) throws ManagementServerException { - if (!Network.GuestType.Isolated.equals(network.getGuestType())) { + if (manager.isDirectAccess(network)) { if (logger.isDebugEnabled()) { - logger.debug(String.format("Network : %s for Kubernetes cluster : %s is not an isolated network, therefore, no need for network rules", network.getName(), kubernetesCluster.getName())); + logger.debug(String.format("Network : %s for Kubernetes cluster : %s is not an isolated network or ROUTED network, therefore, no need for network rules", network.getName(), kubernetesCluster.getName())); } return; } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java index 9e8a43791e8..fb963a13044 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java @@ -189,7 +189,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif Pair> ipAddresses = getKubernetesControlNodeIpAddresses(zone, network, owner); String controlNodeIp = ipAddresses.first(); Map requestedIps = ipAddresses.second(); - if (Network.GuestType.Shared.equals(network.getGuestType()) && StringUtils.isEmpty(serverIp)) { + if (StringUtils.isEmpty(serverIp) && manager.isDirectAccess(network)) { serverIp = controlNodeIp; } Network.IpAddresses addrs = new Network.IpAddresses(controlNodeIp, null); @@ -380,9 +380,9 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif } protected void setupKubernetesClusterNetworkRules(Network network, List clusterVMs) throws ManagementServerException { - if (!Network.GuestType.Isolated.equals(network.getGuestType())) { + if (manager.isDirectAccess(network)) { if (logger.isDebugEnabled()) { - logger.debug(String.format("Network : %s for Kubernetes cluster : %s is not an isolated network, therefore, no need for network rules", network.getName(), kubernetesCluster.getName())); + logger.debug(String.format("Network : %s for Kubernetes cluster : %s is not an isolated network or ROUTED network, therefore, no need for network rules", network.getName(), kubernetesCluster.getName())); } return; } @@ -492,7 +492,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif } publicIpAddress = publicIpSshPort.first(); if (StringUtils.isEmpty(publicIpAddress) && - (Network.GuestType.Isolated.equals(network.getGuestType()) || kubernetesCluster.getControlNodeCount() > 1)) { // Shared network, single-control node cluster won't have an IP yet + (!manager.isDirectAccess(network) || kubernetesCluster.getControlNodeCount() > 1)) { // Shared network, single-control node cluster won't have an IP yet logTransitStateAndThrow(Level.ERROR, String.format("Failed to start Kubernetes cluster : %s as no public IP found for the cluster" , kubernetesCluster.getName()), kubernetesCluster.getId(), KubernetesCluster.Event.CreateFailed); } // Allow account creating the kubernetes cluster to access systemVM template @@ -537,7 +537,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif attachIsoKubernetesVMs(clusterVMs); if (!KubernetesClusterUtil.isKubernetesClusterControlVmRunning(kubernetesCluster, publicIpAddress, publicIpSshPort.second(), startTimeoutTime)) { String msg = String.format("Failed to setup Kubernetes cluster : %s is not in usable state as the system is unable to access control node VMs of the cluster", kubernetesCluster.getName()); - if (kubernetesCluster.getControlNodeCount() > 1 && Network.GuestType.Shared.equals(network.getGuestType())) { + if (kubernetesCluster.getControlNodeCount() > 1 && manager.isDirectAccess(network)) { msg = String.format("%s. Make sure external load-balancer has port forwarding rules for SSH access on ports %d-%d and API access on port %d", msg, CLUSTER_NODES_DEFAULT_START_SSH_PORT, diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java index 791e245b17c..6da2e216ff8 100644 --- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java +++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java @@ -217,7 +217,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager ConfigurationManager configMgr = (ConfigurationManager) _configService; NetworkOfferingVO voffer = configMgr.createNetworkOffering(offeringName, offeringDisplayText, TrafficType.Public, null, true, Availability.Optional, null, serviceProviderMap, true, - Network.GuestType.Shared, false, null, false, null, true, false, null, true, null, false, false, false, false, null, null, null, true, null); + Network.GuestType.Shared, false, null, false, null, true, false, null, true, null, false, false, false, false, null, null, null, true, null, null, false); long id = voffer.getId(); _networkOfferingDao.update(id, voffer); return _networkOfferingDao.findById(id); @@ -252,7 +252,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager ConfigurationManager configMgr = (ConfigurationManager)_configService; NetworkOfferingVO voffer = configMgr.createNetworkOffering(offeringName, offeringDisplayText, TrafficType.Guest, null, false, Availability.Optional, null, serviceProviderMap, true, - Network.GuestType.Isolated, false, null, false, null, false, true, null, true, null, false, offeringName.equals(vpcRouterOfferingName), false, false, null, null, null, true, null); + Network.GuestType.Isolated, false, null, false, null, false, true, null, true, null, false, offeringName.equals(vpcRouterOfferingName), false, false, null, null, null, true, null, null, false); if (offeringName.equals(vpcRouterOfferingName)) { voffer.setInternalLb(true); } @@ -293,7 +293,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager } serviceProviderMap.put(svc, providerSet); } - vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, false, null, null, null, VpcOffering.State.Enabled); + vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, false, null, null, null, VpcOffering.State.Enabled, null, false); long id = vpcOffer.getId(); _vpcOffDao.update(id, (VpcOfferingVO)vpcOffer); return _vpcOffDao.findById(id); 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 0d556da9f2c..032967d4061 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 @@ -87,9 +87,9 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr public boolean canHandle(NetworkOffering offering, DataCenter.NetworkType networkType, PhysicalNetwork physicalNetwork) { return networkType == DataCenter.NetworkType.Advanced && isMyTrafficType(offering.getTrafficType()) - && isMyIsolationMethod(physicalNetwork) && (NetworkOffering.NsxMode.ROUTED.name().equals(offering.getNsxMode()) + && isMyIsolationMethod(physicalNetwork) && (NetworkOffering.NetworkMode.ROUTED.equals(offering.getNetworkMode()) || (networkOfferingServiceMapDao.isProviderForNetworkOffering( - offering.getId(), Network.Provider.Nsx) && NetworkOffering.NsxMode.NATTED.name().equals(offering.getNsxMode()))); + offering.getId(), Network.Provider.Nsx) && NetworkOffering.NetworkMode.NATTED.equals(offering.getNetworkMode()))); } @Override @@ -231,7 +231,7 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(network.getNetworkOfferingId()); - if (isNull(network.getVpcId()) && networkOfferingVO.getNsxMode().equals(NetworkOffering.NsxMode.NATTED.name())) { + if (isNull(network.getVpcId()) && networkOfferingVO.getNetworkMode().equals(NetworkOffering.NetworkMode.NATTED)) { long domainId = domain.getId(); long accountId = account.getId(); long dataCenterId = zone.getId(); @@ -322,7 +322,7 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr logger.debug(String.format("Creating a Tier 1 Gateway for the network %s before creating the NSX segment", networkVO.getName())); long networkOfferingId = networkVO.getNetworkOfferingId(); NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(networkOfferingId); - boolean isSourceNatSupported = !NetworkOffering.NsxMode.ROUTED.name().equals(networkOfferingVO.getNsxMode()) && + boolean isSourceNatSupported = !NetworkOffering.NetworkMode.ROUTED.equals(networkOfferingVO.getNetworkMode()) && networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(networkVO.getNetworkOfferingId(), Network.Service.SourceNat); CreateNsxTier1GatewayCommand nsxTier1GatewayCommand = new CreateNsxTier1GatewayCommand(domain.getId(), account.getId(), zone.getId(), networkVO.getId(), networkVO.getName(), false, isSourceNatSupported); diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java index 7463a19fd4e..4df71056601 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java @@ -133,7 +133,7 @@ public class NsxPublicNetworkGuru extends PublicNetworkGuru { Network.Service[] services = { Network.Service.SourceNat }; long networkOfferingId = vpc.getVpcOfferingId(); VpcOfferingVO vpcVO = vpcOfferingDao.findById(networkOfferingId); - boolean sourceNatEnabled = !NetworkOffering.NsxMode.ROUTED.name().equals(vpcVO.getNsxMode()) && + boolean sourceNatEnabled = !NetworkOffering.NetworkMode.ROUTED.equals(vpcVO.getNetworkMode()) && vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), services); logger.info(String.format("Creating Tier 1 Gateway for VPC %s", vpc.getName())); @@ -146,7 +146,7 @@ public class NsxPublicNetworkGuru extends PublicNetworkGuru { boolean hasNatSupport = false; VpcOffering vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId()); - hasNatSupport = NetworkOffering.NsxMode.NATTED.name().equals(vpcOffering.getNsxMode()); + hasNatSupport = NetworkOffering.NetworkMode.NATTED.equals(vpcOffering.getNetworkMode()); if (!hasNatSupport) { return nic; diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxGuestNetworkGuruTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxGuestNetworkGuruTest.java index 66b9684203b..cb79873f364 100644 --- a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxGuestNetworkGuruTest.java +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxGuestNetworkGuruTest.java @@ -149,7 +149,7 @@ public class NsxGuestNetworkGuruTest { when(offering.getTrafficType()).thenReturn(Networks.TrafficType.Guest); when(offering.getGuestType()).thenReturn(Network.GuestType.Isolated); - when(offering.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name()); + when(offering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.NATTED); when(offering.getId()).thenReturn(1L); when(plan.getDataCenterId()).thenReturn(1L); @@ -319,7 +319,7 @@ public class NsxGuestNetworkGuruTest { anyLong())).thenReturn(new NsxAnswer(new NsxCommand(), true, "")); when(networkVO.getNetworkOfferingId()).thenReturn(1L); when(networkOfferingDao.findById(1L)).thenReturn(offeringVO); - when(offeringVO.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name()); + when(offeringVO.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.NATTED); guru.createNsxSegment(networkVO, dataCenter); verify(nsxControllerUtils, times(1)).sendNsxCommand(any(CreateNsxTier1GatewayCommand.class), anyLong()); diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java index da21bf11b64..1abef392345 100644 --- a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java @@ -165,7 +165,7 @@ public class NsxPublicNetworkGuruTest { when(vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(anyLong(), any())).thenReturn(true); when(nsxService.createVpcNetwork(anyLong(), anyLong(), anyLong(), anyLong(), anyString(), anyBoolean())).thenReturn(true); when(vpcOfferingDao.findById(anyLong())).thenReturn(vpcOffering); - when(vpcOffering.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name()); + when(vpcOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.NATTED); when(nsxControllerUtils.sendNsxCommand(any(CreateOrUpdateNsxTier1NatRuleCommand.class), anyLong())).thenReturn(new NsxAnswer(new NsxCommand(), true, "")); diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index e53de0c1d47..b563641745f 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -39,7 +39,13 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.bgp.ASNumber; +import com.cloud.bgp.ASNumberRange; +import com.cloud.dc.ASNumberRangeVO; +import com.cloud.dc.ASNumberVO; import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.ASNumberDao; +import com.cloud.dc.dao.ASNumberRangeDao; import com.cloud.dc.dao.VlanDetailsDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.BucketVO; @@ -61,6 +67,8 @@ import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.ApplicationLoadBalancerInstanceResponse; import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse; import org.apache.cloudstack.api.response.ApplicationLoadBalancerRuleResponse; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ASNumberResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.AutoScalePolicyResponse; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; @@ -69,6 +77,7 @@ import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.api.response.BackupRepositoryResponse; import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.BackupScheduleResponse; +import org.apache.cloudstack.api.response.BgpPeerResponse; import org.apache.cloudstack.api.response.BucketResponse; import org.apache.cloudstack.api.response.CapabilityResponse; import org.apache.cloudstack.api.response.CapacityResponse; @@ -110,6 +119,7 @@ import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; import org.apache.cloudstack.api.response.IpForwardingRuleResponse; import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.IpRangeResponse; +import org.apache.cloudstack.api.response.Ipv4RouteResponse; import org.apache.cloudstack.api.response.Ipv6RouteResponse; import org.apache.cloudstack.api.response.IsolationMethodResponse; import org.apache.cloudstack.api.response.LBHealthCheckPolicyResponse; @@ -206,6 +216,9 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.management.ManagementServerHost; +import org.apache.cloudstack.network.BgpPeerVO; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.apache.cloudstack.network.dao.BgpPeerDao; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; @@ -351,6 +364,7 @@ import com.cloud.network.vpc.StaticRoute; import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcOffering; import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcOfferingDao; import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; @@ -494,9 +508,19 @@ public class ApiResponseHelper implements ResponseGenerator { VlanDetailsDao vlanDetailsDao; @Inject BackupRepositoryDao backupRepositoryDao; + @Inject + private ASNumberRangeDao asNumberRangeDao; + @Inject + private ASNumberDao asNumberDao; @Inject ObjectStoreDao _objectStoreDao; + @Inject + VpcOfferingDao vpcOfferingDao; + @Inject + BgpPeerDao bgpPeerDao; + @Inject + RoutedIpv4Manager routedIpv4Manager; @Override public UserResponse createUserResponse(User user) { @@ -2389,7 +2413,9 @@ public class ApiResponseHelper implements ResponseGenerator { response.setForVpc(_configMgr.isOfferingForVpc(offering)); response.setForTungsten(offering.isForTungsten()); response.setForNsx(offering.isForNsx()); - response.setNsxMode(offering.getNsxMode()); + if (offering.getNetworkMode() != null) { + response.setNetworkMode(offering.getNetworkMode().name()); + } response.setServices(serviceResponses); //set network offering details Map details = _ntwkModel.getNtwkOffDetails(offering.getId()); @@ -2492,7 +2518,6 @@ public class ApiResponseHelper implements ResponseGenerator { } } response.setReservedIpRange(reservation); - // return vlan information only to Root admin if (network.getBroadcastUri() != null && view == ResponseView.Full) { String broadcastUri = network.getBroadcastUri().toString(); @@ -2540,6 +2565,13 @@ public class ApiResponseHelper implements ResponseGenerator { if (Network.GuestType.Isolated.equals(network.getGuestType()) && network.getVpcId() == null) { response.setEgressDefaultPolicy(networkOffering.isEgressDefaultPolicy()); } + ASNumberVO asNumberVO = networkOffering.isForVpc() ? + asNumberDao.findByZoneAndVpcId(network.getDataCenterId(), network.getVpcId()) : + asNumberDao.findByZoneAndNetworkId(network.getDataCenterId(), network.getId()); + if (Objects.nonNull(asNumberVO)) { + response.setAsNumberId(asNumberVO.getUuid()); + response.setAsNumber(asNumberVO.getAsNumber()); + } } if (network.getAclType() != null) { @@ -2720,6 +2752,29 @@ public class ApiResponseHelper implements ResponseGenerator { response.setBytesReceived(bytesReceived); response.setBytesSent(bytesSent); + if (networkOfferingDao.isRoutedNetwork(network.getNetworkOfferingId())) { + if (routedIpv4Manager.isDynamicRoutedNetwork(network)) { + response.setIpv4Routing(Network.Routing.Dynamic.name()); + } else { + response.setIpv4Routing(Network.Routing.Static.name()); + } + response.setIpv4Routes(new LinkedHashSet<>()); + List ips = network.getVpcId() != null ? userIpAddressDao.listByAssociatedVpc(network.getVpcId(), true): + userIpAddressDao.listByAssociatedNetwork(network.getId(), true); + for (IpAddress ip : ips) { + Ipv4RouteResponse route = new Ipv4RouteResponse(network.getCidr(), ip.getAddress().addr()); + response.addIpv4Route(route); + } + + if (view == ResponseView.Full) { + List bgpPeerVOS = bgpPeerDao.listNonRevokeByNetworkId(network.getId()); + for (BgpPeerVO bgpPeerVO : bgpPeerVOS) { + BgpPeerResponse bgpPeerResponse = routedIpv4Manager.createBgpPeerResponse(bgpPeerVO); + response.addBgpPeer(bgpPeerResponse); + } + } + } + if (networkOfferingDao.isIpv6Supported(network.getNetworkOfferingId())) { response.setInternetProtocol(networkOfferingDao.getNetworkOfferingInternetProtocol(network.getNetworkOfferingId(), NetUtils.InternetProtocol.IPv4).toString()); response.setIpv6Routing(Network.Routing.Static.toString()); @@ -2784,19 +2839,19 @@ public class ApiResponseHelper implements ResponseGenerator { List cidrs = ApiDBUtils.findFirewallSourceCidrs(fwRule.getId()); response.setCidrList(StringUtils.join(cidrs, ",")); - if(fwRule.getTrafficType() == FirewallRule.TrafficType.Egress){ - List destCidrs = ApiDBUtils.findFirewallDestCidrs(fwRule.getId()); - response.setDestCidr(StringUtils.join(destCidrs,",")); - } + List destCidrs = ApiDBUtils.findFirewallDestCidrs(fwRule.getId()); + response.setDestCidr(StringUtils.join(destCidrs,",")); if (fwRule.getTrafficType() == FirewallRule.TrafficType.Ingress) { - IpAddress ip = ApiDBUtils.findIpAddressById(fwRule.getSourceIpAddressId()); - response.setPublicIpAddressId(ip.getUuid()); - response.setPublicIpAddress(ip.getAddress().addr()); + if (fwRule.getSourceIpAddressId() != null) { + IpAddress ip = ApiDBUtils.findIpAddressById(fwRule.getSourceIpAddressId()); + response.setPublicIpAddressId(ip.getUuid()); + response.setPublicIpAddress(ip.getAddress().addr()); + } } - Network network = ApiDBUtils.findNetworkById(fwRule.getNetworkId()); - response.setNetworkId(network.getUuid()); + Network network = ApiDBUtils.findNetworkById(fwRule.getNetworkId()); + response.setNetworkId(network.getUuid()); FirewallRule.State state = fwRule.getState(); String stateToSet = state.toString(); @@ -2807,6 +2862,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setIcmpCode(fwRule.getIcmpCode()); response.setIcmpType(fwRule.getIcmpType()); response.setForDisplay(fwRule.isDisplay()); + response.setTrafficType(fwRule.getTrafficType().toString()); // set tag information List tags = ApiDBUtils.listByResourceTypeAndId(ResourceObjectType.FirewallRule, fwRule.getId()); @@ -3390,7 +3446,11 @@ public class ApiResponseHelper implements ResponseGenerator { response.setUsesDistributedRouter(vpc.usesDistributedRouter()); response.setRedundantRouter(vpc.isRedundant()); response.setRegionLevelVpc(vpc.isRegionLevelVpc()); - + ASNumberVO asNumberVO = asNumberDao.findByZoneAndVpcId(vpc.getZoneId(), vpc.getId()); + if (Objects.nonNull(asNumberVO)) { + response.setAsNumberId(asNumberVO.getUuid()); + response.setAsNumber(asNumberVO.getAsNumber()); + } Map> serviceProviderMap = ApiDBUtils.listVpcOffServices(vpc.getVpcOfferingId()); List serviceResponses = new ArrayList(); for (Map.Entry>entry : serviceProviderMap.entrySet()) { @@ -3448,6 +3508,31 @@ public class ApiResponseHelper implements ResponseGenerator { response.setDns2(vpc.getIp4Dns2()); response.setIpv6Dns1(vpc.getIp6Dns1()); response.setIpv6Dns2(vpc.getIp6Dns2()); + + // add IPv4 routes + if (vpcOfferingDao.isRoutedVpc(vpc.getVpcOfferingId())) { + if (Objects.nonNull(asNumberVO)) { + response.setIpv4Routing(Network.Routing.Dynamic.name()); + } else { + response.setIpv4Routing(Network.Routing.Static.name()); + } + response.setIpv4Routes(new LinkedHashSet<>()); + List ips = userIpAddressDao.listByAssociatedVpc(vpc.getId(), true); + for (Network network : networkDao.listByVpc(vpc.getId())) { + for (IPAddressVO ip : ips) { + Ipv4RouteResponse route = new Ipv4RouteResponse(network.getCidr(), ip.getAddress().addr()); + response.addIpv4Route(route); + } + } + if (view == ResponseView.Full) { + List bgpPeerVOS = bgpPeerDao.listNonRevokeByVpcId(vpc.getId()); + for (BgpPeerVO bgpPeerVO : bgpPeerVOS) { + BgpPeerResponse bgpPeerResponse = routedIpv4Manager.createBgpPeerResponse(bgpPeerVO); + response.addBgpPeer(bgpPeerResponse); + } + } + } + response.setObjectName("vpc"); return response; } @@ -5289,6 +5374,59 @@ public class ApiResponseHelper implements ResponseGenerator { return bucketResponse; } + @Override + public ASNRangeResponse createASNumberRangeResponse(ASNumberRange asnRange) { + ASNRangeResponse response = new ASNRangeResponse(); + response.setId(asnRange.getUuid()); + DataCenterVO zone = ApiDBUtils.findZoneById(asnRange.getDataCenterId()); + if (zone != null) { + response.setZoneId(zone.getUuid()); + } + response.setStartASNumber(asnRange.getStartASNumber()); + response.setEndASNumber(asnRange.getEndASNumber()); + response.setCreated(asnRange.getCreated()); + response.setObjectName("asnumberrange"); + return response; + } + + @Override + public ASNumberResponse createASNumberResponse(ASNumber asn) { + ASNumberResponse response = new ASNumberResponse(); + response.setId(asn.getUuid()); + if (asn.getAccountId() != null) { + Account account = ApiDBUtils.findAccountById(asn.getAccountId()); + response.setAccountId(account.getUuid()); + response.setAccountName(account.getAccountName()); + } + if (asn.getDomainId() != null) { + DomainVO domain = ApiDBUtils.findDomainById(asn.getDomainId()); + response.setDomainId(domain.getUuid()); + response.setDomainName(domain.getName()); + } + DataCenterVO zone = ApiDBUtils.findZoneById(asn.getDataCenterId()); + response.setZoneId(zone.getUuid()); + response.setZoneName(zone.getName()); + response.setAsNumber(asn.getAsNumber()); + ASNumberRangeVO range = asNumberRangeDao.findById(asn.getAsNumberRangeId()); + response.setAsNumberRangeId(range.getUuid()); + String rangeText = String.format("%s-%s", range.getStartASNumber(), range.getEndASNumber()); + response.setAsNumberRange(rangeText); + response.setAllocated(asn.getAllocatedTime()); + response.setAllocationState(asn.isAllocated() ? "Allocated" : "Free"); + if (asn.getVpcId() != null) { + VpcVO vpc = ApiDBUtils.findVpcById(asn.getVpcId()); + response.setVpcId(vpc.getUuid()); + response.setVpcName(vpc.getName()); + } else if (asn.getNetworkId() != null) { + NetworkVO network = ApiDBUtils.findNetworkById(asn.getNetworkId()); + response.setAssociatedNetworkId(network.getUuid()); + response.setAssociatedNetworkName(network.getName()); + } + response.setCreated(asn.getCreated()); + response.setObjectName("asnumber"); + return response; + } + @Override public BackupRepositoryResponse createBackupRepositoryResponse(BackupRepository backupRepository) { BackupRepositoryResponse response = new BackupRepositoryResponse(); diff --git a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java index 902c9c9bb25..68e7ba7cc7c 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java @@ -18,9 +18,12 @@ package com.cloud.api.query.dao; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.dc.ASNumberRangeVO; +import com.cloud.dc.dao.ASNumberRangeDao; import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.element.NsxProviderVO; import org.apache.cloudstack.annotation.AnnotationService; @@ -56,6 +59,8 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase asNumberRange = asNumberRangeDao.listByZoneId(dataCenter.getId()); + String asRange = asNumberRange.stream().map(range -> range.getStartASNumber() + "-" + range.getEndASNumber()).collect(Collectors.joining(", ")); + zoneResponse.setAsnRange(asRange); + zoneResponse.setResourceDetails(ApiDBUtils.getResourceDetails(dataCenter.getId(), ResourceObjectType.Zone)); zoneResponse.setHasAnnotation(annotationDao.hasAnnotations(dataCenter.getUuid(), AnnotationService.EntityType.ZONE.name(), _accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId()))); diff --git a/server/src/main/java/com/cloud/api/query/dao/NetworkOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/NetworkOfferingJoinDaoImpl.java index 3d50b88846c..22b51f6fd8c 100644 --- a/server/src/main/java/com/cloud/api/query/dao/NetworkOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/NetworkOfferingJoinDaoImpl.java @@ -124,6 +124,12 @@ public class NetworkOfferingJoinDaoImpl extends GenericDaoBase endASNumber) { + String msg = "Please specify a valid AS Number range"; + LOGGER.error(msg); + throw new InvalidParameterException(msg); + } + + List asNumberRanges = asNumberRangeDao.listByZoneId(zoneId); + for (ASNumberRangeVO asNumberRange : asNumberRanges) { + if (isASNumbersOverlap(asNumberRange.getStartASNumber(), asNumberRange.getEndASNumber(), startASNumber, endASNumber)) { + throw new InvalidParameterException(String.format("New AS number range (%s-%s) has conflict with an existing AS number range (%s-%s)", + startASNumber, endASNumber, asNumberRange.getStartASNumber(), asNumberRange.getEndASNumber())); + } + } + + try { + return Transaction.execute((TransactionCallback) status -> { + LOGGER.debug(String.format("Persisting AS Number Range %s-%s for the zone %s", startASNumber, endASNumber, zone.getName())); + ASNumberRangeVO asNumberRangeVO = new ASNumberRangeVO(zoneId, startASNumber, endASNumber); + asNumberRangeDao.persist(asNumberRangeVO); + + for (long asn = startASNumber; asn <= endASNumber; asn++) { + LOGGER.debug(String.format("Persisting AS Number %s for zone %s", asn, zone.getName())); + ASNumberVO asNumber = new ASNumberVO(asn, asNumberRangeVO.getId(), zoneId); + asNumberDao.persist(asNumber); + } + return asNumberRangeVO; + }); + } catch (Exception e) { + String err = String.format("Error creating AS Number range %s-%s for zone %s: %s", startASNumber, endASNumber, zone.getName(), e.getMessage()); + LOGGER.error(err, e); + throw new CloudRuntimeException(err); + } + } + + protected boolean isASNumbersOverlap(long startNumber1, long endNumber1, long startNumber2, long endNumber2) { + if (startNumber1 > endNumber2 || startNumber2 > endNumber1) { + return false; + } + return true; + } + + @Override + public List listASNumberRanges(Long zoneId) { + List rangeVOList = zoneId != null ? asNumberRangeDao.listByZoneId(zoneId) : asNumberRangeDao.listAll(); + return new ArrayList<>(rangeVOList); + } + + @Override + public Pair, Integer> listASNumbers(ListASNumbersCmd cmd) { + Long zoneId = cmd.getZoneId(); + Long asNumberRangeId = cmd.getAsNumberRangeId(); + Integer asNumber = cmd.getAsNumber(); + Boolean allocated = cmd.getAllocated(); + Long networkId = cmd.getNetworkId(); + Long vpcId = cmd.getVpcId(); + String accountName = cmd.getAccount(); + Long domainId = cmd.getDomainId(); + Long startIndex = cmd.getStartIndex(); + Long pageSizeVal = cmd.getPageSizeVal(); + String keyword = cmd.getKeyword(); + + Account userAccount = null; + Domain domain = null; + final Account caller = CallContext.current().getCallingAccount(); + if (Objects.nonNull(accountName)) { + if (domainId != null) { + userAccount = accountDao.findActiveAccount(accountName, domainId); + domain = domainDao.findById(domainId); + } else { + userAccount = accountDao.findActiveAccount(accountName, caller.getDomainId()); + domain = domainDao.findById(caller.getDomainId()); + } + } + + if (Objects.nonNull(accountName) && Objects.isNull(userAccount)) { + throw new InvalidParameterException(String.format("Failed to find user Account: %s", accountName)); + } + + Long networkSearchId = networkId; + Long vpcSerchId = vpcId; + if (networkId != null) { + NetworkVO network = networkDao.findById(networkId); + if (network == null) { + throw new InvalidParameterException(String.format("Failed to find network with ID: %s", networkId)); + } + if (network.getVpcId() != null) { + LOGGER.debug(String.format("The network %s is a VPC tier, searching for the AS number on the VPC with ID %s", + network.getName(), network.getVpcId())); + networkSearchId = null; + vpcSerchId = network.getVpcId(); + } + } + Pair, Integer> pair = asNumberDao.searchAndCountByZoneOrRangeOrAllocated(zoneId, asNumberRangeId, + asNumber, networkSearchId, vpcSerchId, allocated, Objects.nonNull(userAccount) ? userAccount.getId() : null, + Objects.nonNull(domain) ? domain.getId() : null, keyword, caller, startIndex, pageSizeVal); + return new Pair<>(new ArrayList<>(pair.first()), pair.second()); + } + + @Override + public boolean allocateASNumber(long zoneId, Long asNumber, Long networkId, Long vpcId) { + ASNumberVO asNumberVO = isOfferingSpecifyAsNumber(networkId, vpcId) ? + asNumberDao.findByAsNumber(asNumber) : + asNumberDao.findOneByAllocationStateAndZone(zoneId, false); + if (asNumberVO == null || asNumberVO.getDataCenterId() != zoneId) { + if (asNumber != null) { + LOGGER.error(String.format("Cannot find AS number %s in zone with ID %s", asNumber, zoneId)); + return false; + } + throw new CloudRuntimeException(String.format("Cannot allocate AS number in zone with ID %s", zoneId)); + } + long accountId, domainId; + String netName; + if (Objects.nonNull(vpcId)) { + VpcVO vpc = vpcDao.findById(vpcId); + if (vpc == null) { + LOGGER.error(String.format("Cannot find VPC with ID %s", vpcId)); + return false; + } + accountId = vpc.getAccountId(); + domainId = vpc.getDomainId(); + netName = vpc.getName(); + } else { + NetworkVO network = networkDao.findById(networkId); + if (network == null) { + LOGGER.error(String.format("Cannot find network with ID %s", networkId)); + return false; + } + accountId = network.getAccountId(); + domainId = network.getDomainId(); + netName = network.getName(); + } + + LOGGER.debug(String.format("Allocating the AS Number %s to %s %s on zone %s", asNumber, + (Objects.nonNull(vpcId) ? "VPC" : "network"), netName, zoneId)); + asNumberVO.setAllocated(true); + asNumberVO.setAllocatedTime(new Date()); + if (Objects.nonNull(vpcId)) { + asNumberVO.setVpcId(vpcId); + } else { + asNumberVO.setNetworkId(networkId); + } + asNumberVO.setAccountId(accountId); + asNumberVO.setDomainId(domainId); + return asNumberDao.update(asNumberVO.getId(), asNumberVO); + } + + private boolean isOfferingSpecifyAsNumber(Long networkId, Long vpcId) { + if (Objects.nonNull(vpcId)) { + VpcVO vpc = vpcDao.findById(vpcId); + if (vpc == null) { + throw new CloudRuntimeException(String.format("Cannot find VPC with ID %s", vpcId)); + } + VpcOfferingVO vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId()); + return NetworkOffering.RoutingMode.Dynamic == vpcOffering.getRoutingMode() && BooleanUtils.toBoolean(vpcOffering.isSpecifyAsNumber()); + } else { + NetworkVO network = networkDao.findById(networkId); + NetworkOfferingVO networkOffering = networkOfferingDao.findById(network.getNetworkOfferingId()); + return NetworkOffering.RoutingMode.Dynamic == networkOffering.getRoutingMode() && BooleanUtils.toBoolean(networkOffering.isSpecifyAsNumber()); + } + } + + private Pair logAndReturnErrorMessage(String msg) { + LOGGER.error(msg); + return new Pair<>(false, msg); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_AS_NUMBER_RELEASE, eventDescription = "Releasing AS Number") + public Pair releaseASNumber(long zoneId, long asNumber, boolean isDestroyNetworkOperation) { + ASNumberVO asNumberVO = asNumberDao.findByAsNumber(asNumber); + if (asNumberVO == null) { + return logAndReturnErrorMessage(String.format("Cannot find AS Number %s on zone %s", asNumber, zoneId)); + } + if (!asNumberVO.isAllocated()) { + LOGGER.debug(String.format("The AS Number %s is not allocated to any network on zone %s, ignoring release", asNumber, zoneId)); + return new Pair<>(true, ""); + } + Long networkId = asNumberVO.getNetworkId(); + Long vpcId = asNumberVO.getVpcId(); + if (!isDestroyNetworkOperation) { + Pair checksResult = performReleaseASNumberChecks(networkId, vpcId, asNumber); + if (checksResult != null) { + return checksResult; + } + } + LOGGER.debug(String.format("Releasing AS Number %s on zone %s from previous allocation", asNumber, zoneId)); + asNumberVO.setAllocated(false); + asNumberVO.setAllocatedTime(null); + asNumberVO.setDomainId(null); + asNumberVO.setAccountId(null); + if (vpcId != null) { + asNumberVO.setVpcId(null); + } else { + asNumberVO.setNetworkId(null); + } + boolean update = asNumberDao.update(asNumberVO.getId(), asNumberVO); + String msg = update ? "OK" : "Could not update database record for AS number"; + return new Pair<>(update, msg); + } + + private Pair performReleaseASNumberChecks(Long networkId, Long vpcId, long asNumber) { + if (networkId != null) { + NetworkVO network = networkDao.findById(networkId); + if (network == null) { + return logAndReturnErrorMessage(String.format("Cannot find a network with ID %s which acquired the AS number %s", networkId, asNumber)); + } + NetworkOfferingVO offering = networkOfferingDao.findById(network.getNetworkOfferingId()); + if (offering == null) { + return logAndReturnErrorMessage(String.format("Cannot find a network offering with ID %s", network.getNetworkOfferingId())); + } + if (offering.isSpecifyAsNumber()) { + return logAndReturnErrorMessage(String.format("Cannot release the AS number %s as it is acquired by a network that requires AS number", asNumber)); + } + } else if (vpcId != null) { + VpcVO vpc = vpcDao.findById(vpcId); + if (vpc == null) { + return logAndReturnErrorMessage(String.format("Cannot find a VPC with ID %s which acquired the AS number %s", vpcId, asNumber)); + } + VpcOfferingVO vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId()); + if (vpcOffering == null) { + return logAndReturnErrorMessage(String.format("Cannot find a VPC offering with ID %s", vpc.getVpcOfferingId())); + } + if (vpcOffering.isSpecifyAsNumber()) { + return logAndReturnErrorMessage(String.format("Cannot release the AS number %s as it is acquired by a VPC that requires AS number", asNumber)); + } + } + return null; + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_AS_RANGE_DELETE, eventDescription = "Deleting AS Range") + public boolean deleteASRange(long id) { + final ASNumberRange asRange = asNumberRangeDao.findById(id); + if (asRange == null) { + throw new CloudRuntimeException(String.format("Could not find a AS range with id: %s", id)); + } + long startASNumber = asRange.getStartASNumber(); + long endASNumber = asRange.getEndASNumber(); + long zoneId = asRange.getDataCenterId(); + List allocatedAsNumbers = asNumberDao.listAllocatedByASRange(asRange.getId()); + if (Objects.nonNull(allocatedAsNumbers) && !allocatedAsNumbers.isEmpty()) { + throw new CloudRuntimeException(String.format("There are %s AS numbers in use from the range %s-%s, cannot remove the range", + allocatedAsNumbers.size(), startASNumber, endASNumber)); + } + try { + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + int removedASNumbers = asNumberDao.removeASRangeNumbers(asRange.getId()); + LOGGER.debug(String.format("Removed %s AS numbers from the range %s-%s", removedASNumbers, + startASNumber, endASNumber)); + asNumberRangeDao.remove(id); + LOGGER.debug(String.format("Removing the AS Number Range %s-%s for the zone %s", startASNumber, + endASNumber, zoneId)); + } + }); + } catch (Exception e) { + String err = String.format("Error removing AS Number range %s-%s for zone %s: %s", + startASNumber, endASNumber, zoneId, e.getMessage()); + LOGGER.error(err, e); + throw new CloudRuntimeException(err); + } + return true; + } + + @Override + public boolean applyBgpPeers(Network network, boolean continueOnError) throws ResourceUnavailableException { + if (!routedIpv4Manager.isDynamicRoutedNetwork(network)) { + return true; + } + final String gatewayProviderStr = ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Network.Service.Gateway); + if (gatewayProviderStr != null) { + NetworkElement provider = networkModel.getElementImplementingProvider(gatewayProviderStr); + if (provider != null && provider instanceof BgpServiceProvider) { + List bgpPeers; + if (network.getVpcId() != null) { + bgpPeers = bgpPeerDao.listNonRevokeByVpcId(network.getVpcId()); + } else { + bgpPeers = bgpPeerDao.listNonRevokeByNetworkId(network.getId()); + } + if (CollectionUtils.isEmpty(bgpPeers)) { + Account owner = accountDao.findByIdIncludingRemoved(network.getAccountId()); + List bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, network.getDataCenterId()); + bgpPeers = bgpPeerIds.stream() + .map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId)) + .collect(Collectors.toList()); + } + LOGGER.debug(String.format("Applying BPG Peers for network [%s]: [%s]", network, bgpPeers)); + return ((BgpServiceProvider) provider).applyBgpPeers(null, network, bgpPeers); + } + } + return true; + } + + @Override + public boolean applyBgpPeers(Vpc vpc, boolean continueOnError) throws ResourceUnavailableException { + if (!routedIpv4Manager.isDynamicRoutedVpc(vpc)) { + return true; + } + final String gatewayProviderStr = vpcServiceMapDao.getProviderForServiceInVpc(vpc.getId(), Network.Service.Gateway); + if (gatewayProviderStr != null) { + NetworkElement provider = networkModel.getElementImplementingProvider(gatewayProviderStr); + if (provider != null && provider instanceof BgpServiceProvider) { + List bgpPeers = bgpPeerDao.listNonRevokeByVpcId(vpc.getId()); + if (CollectionUtils.isEmpty(bgpPeers)) { + Account owner = accountDao.findByIdIncludingRemoved(vpc.getAccountId()); + List bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, vpc.getZoneId()); + bgpPeers = bgpPeerIds.stream() + .map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId)) + .collect(Collectors.toList()); + } + LOGGER.debug(String.format("Applying BPG Peers for VPC [%s]: [%s]", vpc, bgpPeers)); + return ((BgpServiceProvider) provider).applyBgpPeers(vpc, null, bgpPeers); + + } + } + return true; + } +} diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 25c866d8609..0138658737e 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -307,6 +307,8 @@ import com.googlecode.ipv6.IPv6Address; import com.googlecode.ipv6.IPv6Network; import static com.cloud.configuration.Config.SecStorageAllowedInternalDownloadSites; +import static com.cloud.offering.NetworkOffering.RoutingMode.Dynamic; +import static com.cloud.offering.NetworkOffering.RoutingMode.Static; public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable { public static final String PERACCOUNT = "peraccount"; @@ -529,6 +531,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati private static final Set VPC_ONLY_PROVIDERS = Sets.newHashSet(Provider.VPCVirtualRouter, Provider.JuniperContrailVpcRouter, Provider.InternalLbVm); + private static final List SUPPORTED_ROUTING_MODE_STRS = Arrays.asList(Static.toString().toLowerCase(), Dynamic.toString().toLowerCase()); private static final long GiB_TO_BYTES = 1024 * 1024 * 1024; @Override @@ -6101,7 +6104,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati Boolean forVpc = cmd.getForVpc(); Boolean forNsx = cmd.isForNsx(); Boolean forTungsten = cmd.getForTungsten(); - String nsxMode = cmd.getNsxMode(); + String networkModeStr = cmd.getNetworkMode(); boolean nsxSupportInternalLbSvc = cmd.getNsxSupportsInternalLbService(); Integer maxconn = null; boolean enableKeepAlive = false; @@ -6109,6 +6112,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final List domainIds = cmd.getDomainIds(); final List zoneIds = cmd.getZoneIds(); final boolean enable = cmd.getEnable(); + boolean specifyAsNumber = cmd.getSpecifyAsNumber(); + String routingModeString = cmd.getRoutingMode(); // check if valid domain if (CollectionUtils.isNotEmpty(domainIds)) { for (final Long domainId: domainIds) { @@ -6140,20 +6145,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Network Offering cannot be for both Tungsten-Fabric and NSX"); } - if (Boolean.TRUE.equals(forNsx)) { - if (Objects.isNull(nsxMode)) { - throw new InvalidParameterValueException("Mode for an NSX offering needs to be specified. Valid values: " + Arrays.toString(NetworkOffering.NsxMode.values())); - } - if (!EnumUtils.isValidEnum(NetworkOffering.NsxMode.class, nsxMode)) { - throw new InvalidParameterValueException("Invalid mode passed. Valid values: " + Arrays.toString(NetworkOffering.NsxMode.values())); - } - } else { - if (Objects.nonNull(nsxMode)) { - if (logger.isTraceEnabled()) { - logger.trace("nsxMode has is ignored for non-NSX enabled zones"); - } - nsxMode = null; + NetworkOffering.NetworkMode networkMode = null; + if (networkModeStr != null) { + if (!EnumUtils.isValidEnum(NetworkOffering.NetworkMode.class, networkModeStr)) { + throw new InvalidParameterValueException("Invalid mode passed. Valid values: " + Arrays.toString(NetworkOffering.NetworkMode.values())); } + networkMode = NetworkOffering.NetworkMode.valueOf(networkModeStr); } // Verify traffic type @@ -6215,6 +6212,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati _networkSvc.validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(serviceOfferingId); } + NetworkOffering.RoutingMode routingMode = verifyRoutingMode(routingModeString); + // configure service provider map final Map> serviceProviderMap = new HashMap>(); final Set defaultProviders = new HashSet(); @@ -6384,6 +6383,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati serviceCapabilityMap.put(Service.StaticNat, staticNatServiceCapabilityMap); serviceCapabilityMap.put(Service.Connectivity, connectivityServiceCapabilityMap); + final Map gatewayServiceCapabilityMap = cmd.getServiceCapabilities(Service.Gateway); + if (MapUtils.isNotEmpty(gatewayServiceCapabilityMap)) { + serviceCapabilityMap.put(Service.Gateway, gatewayServiceCapabilityMap); + } + // if Firewall service is missing, add Firewall service/provider // combination if (firewallProvider != null) { @@ -6420,7 +6424,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } final NetworkOfferingVO offering = createNetworkOffering(name, displayText, trafficType, tags, specifyVlan, availability, networkRate, serviceProviderMap, false, guestType, false, - serviceOfferingId, conserveMode, serviceCapabilityMap, specifyIpRanges, isPersistent, details, egressDefaultPolicy, maxconn, enableKeepAlive, forVpc, forTungsten, forNsx, nsxMode, domainIds, zoneIds, enable, internetProtocol); + serviceOfferingId, conserveMode, serviceCapabilityMap, specifyIpRanges, isPersistent, details, egressDefaultPolicy, maxconn, enableKeepAlive, forVpc, forTungsten, forNsx, networkMode, domainIds, zoneIds, enable, internetProtocol, routingMode, specifyAsNumber); if (Boolean.TRUE.equals(forNsx) && nsxSupportInternalLbSvc) { offering.setInternalLb(true); offering.setPublicLb(false); @@ -6431,6 +6435,23 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return offering; } + public static NetworkOffering.RoutingMode verifyRoutingMode(String routingModeString) { + NetworkOffering.RoutingMode routingMode = null; + if (routingModeString != null) { + try { + if (!SUPPORTED_ROUTING_MODE_STRS.contains(routingModeString.toLowerCase())) { + throw new IllegalArgumentException(String.format("Unsupported value: %s", routingModeString)); + } + routingMode = routingModeString.equalsIgnoreCase(Static.toString()) ? Static : Dynamic; + } catch (IllegalArgumentException e) { + String msg = String.format("Invalid value %s for Routing Mode, Supported values: %s, %s.", + routingModeString, Static, Dynamic); + throw new InvalidParameterValueException(msg); + } + } + return routingMode; + } + void validateLoadBalancerServiceCapabilities(final Map lbServiceCapabilityMap) { if (lbServiceCapabilityMap != null && !lbServiceCapabilityMap.isEmpty()) { if (lbServiceCapabilityMap.keySet().size() > 4 || !lbServiceCapabilityMap.containsKey(Capability.SupportedLBIsolation)) { @@ -6570,7 +6591,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final Long serviceOfferingId, final boolean conserveMode, final Map> serviceCapabilityMap, final boolean specifyIpRanges, final boolean isPersistent, final Map details, final boolean egressDefaultPolicy, final Integer maxconn, final boolean enableKeepAlive, Boolean forVpc, - Boolean forTungsten, boolean forNsx, String mode, final List domainIds, final List zoneIds, final boolean enableOffering, final NetUtils.InternetProtocol internetProtocol) { + Boolean forTungsten, boolean forNsx, NetworkOffering.NetworkMode networkMode, final List domainIds, final List zoneIds, final boolean enableOffering, final NetUtils.InternetProtocol internetProtocol, + final NetworkOffering.RoutingMode routingMode, final boolean specifyAsNumber) { String servicePackageUuid; String spDescription = null; @@ -6601,11 +6623,63 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } + if (specifyAsNumber && !forNsx) { + String msg = "SpecifyAsNumber can only be true for network offerings for NSX"; + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + + if (specifyAsNumber && !Dynamic.equals(routingMode)) { + String msg = "SpecifyAsNumber can only be true for Dynamic Route Mode network offerings"; + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + + if (specifyAsNumber && Boolean.TRUE.equals(forVpc)) { + String msg = "SpecifyAsNumber cannot be set for VPC network tiers. It needs to be defined at VPC level"; + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + // isPersistent should always be false for Shared network Offerings if (isPersistent && type == GuestType.Shared) { throw new InvalidParameterValueException("isPersistent should be false if network offering's type is " + type); } + // Validate network mode + if (networkMode != null) { + if (type != GuestType.Isolated) { + throw new InvalidParameterValueException("networkMode should be set only for Isolated network offerings"); + } + if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode)) { + boolean useVirtualRouterOnly = true; + for (Service service : serviceProviderMap.keySet()) { + Set providers = serviceProviderMap.get(service); + if (Arrays.asList(Service.SourceNat, Service.StaticNat, Service.Lb, Service.PortForwarding, Service.Vpn).contains(service)) { + if (providers != null) { + throw new InvalidParameterValueException("SourceNat/StaticNat/Lb/PortForwarding/Vpn service are not supported in ROUTED mode"); + } + } + if (useVirtualRouterOnly && Arrays.asList(Service.Firewall, Service.NetworkACL).contains(service)) { + for (Provider provider : providers) { + if (!Provider.VirtualRouter.equals(provider) && !Provider.VPCVirtualRouter.equals(provider)) { + useVirtualRouterOnly = false; + break; + } + } + } + } + if (useVirtualRouterOnly) { + // Add VirtualRouter/VPCVirtualRouter as provider of Gateway service + if (forVpc) { + serviceProviderMap.put(Service.Gateway, Sets.newHashSet(Provider.VPCVirtualRouter)); + } else { + serviceProviderMap.put(Service.Gateway, Sets.newHashSet(Provider.VirtualRouter)); + } + } + } + } + // validate availability value if (availability == NetworkOffering.Availability.Required) { final boolean canOffBeRequired = type == GuestType.Isolated && serviceProviderMap.containsKey(Service.SourceNat); @@ -6682,9 +6756,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } final Map sourceNatServiceCapabilityMap = serviceCapabilityMap.get(Service.SourceNat); - if (sourceNatServiceCapabilityMap != null && !sourceNatServiceCapabilityMap.isEmpty()) { + if (MapUtils.isNotEmpty(sourceNatServiceCapabilityMap)) { sharedSourceNat = isSharedSourceNat(serviceProviderMap, sourceNatServiceCapabilityMap); - redundantRouter = isRedundantRouter(serviceProviderMap, sourceNatServiceCapabilityMap); + redundantRouter = isRedundantRouter(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, sourceNatServiceCapabilityMap); + } + + final Map gatewayServiceCapabilityMap = serviceCapabilityMap.get(Service.Gateway); + if (MapUtils.isNotEmpty(gatewayServiceCapabilityMap)) { + redundantRouter = redundantRouter || isRedundantRouter(serviceProviderMap.get(Service.Gateway), Service.Gateway, gatewayServiceCapabilityMap); } final Map staticNatServiceCapabilityMap = serviceCapabilityMap.get(Service.StaticNat); @@ -6732,14 +6811,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati offeringFinal.setForTungsten(Objects.requireNonNullElse(forTungsten, false)); offeringFinal.setForNsx(Objects.requireNonNullElse(forNsx, false)); - if (Boolean.TRUE.equals(forNsx)) { - offeringFinal.setNsxMode(mode); - } + offeringFinal.setNetworkMode(networkMode); if (enableOffering) { offeringFinal.setState(NetworkOffering.State.Enabled); } + offeringFinal.setSpecifyAsNumber(specifyAsNumber); + if (routingMode != null) { + offeringFinal.setRoutingMode(routingMode); + } + // Set VM AutoScaling capability offeringFinal.setSupportsVmAutoScaling(vmAutoScaling); @@ -6838,11 +6920,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati }); } - boolean isRedundantRouter(Map> serviceProviderMap, Map sourceNatServiceCapabilityMap) { + boolean isRedundantRouter(Set providers, Service service, Map sourceNatServiceCapabilityMap) { boolean redundantRouter = false; String param = sourceNatServiceCapabilityMap.get(Capability.RedundantRouter); if (param != null) { - _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.RedundantRouter, param); + _networkModel.checkCapabilityForProvider(providers, service, Capability.RedundantRouter, param); redundantRouter = param.contains("true"); } return redundantRouter; @@ -6920,6 +7002,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final String tags = cmd.getTags(); final Boolean isTagged = cmd.isTagged(); final Boolean forVpc = cmd.getForVpc(); + final String routingMode = cmd.getRoutingMode(); if (domainId != null) { Domain domain = _entityMgr.findById(Domain.class, domainId); @@ -6993,6 +7076,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } + if (routingMode != null && EnumUtils.isValidEnumIgnoreCase(NetworkOffering.RoutingMode.class, routingMode)) { + sc.addAnd("routingMode", SearchCriteria.Op.EQ, routingMode); + } + // Don't return system network offerings to the user sc.addAnd("systemOnly", SearchCriteria.Op.EQ, false); diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java index 9dbc478f9c0..f5e053cc830 100644 --- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java @@ -1870,7 +1870,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage guestNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null, null, null, null, null, - null, null, null, null, null); + null, null, null, null, null, null); if (guestNetwork == null) { logger.warn("Failed to create default Virtual network for the account " + accountId + "in zone " + zoneId); throw new CloudRuntimeException("Failed to create a Guest Isolated Networks with SourceNAT " diff --git a/server/src/main/java/com/cloud/network/Ipv6ServiceImpl.java b/server/src/main/java/com/cloud/network/Ipv6ServiceImpl.java index a3432a8633a..d5b3cab44a6 100644 --- a/server/src/main/java/com/cloud/network/Ipv6ServiceImpl.java +++ b/server/src/main/java/com/cloud/network/Ipv6ServiceImpl.java @@ -557,9 +557,7 @@ public class Ipv6ServiceImpl extends ComponentLifecycleBase implements Ipv6Servi final long accountId = network.getAccountId(); final long domainId = network.getDomainId(); - if (FirewallRule.TrafficType.Egress.equals(trafficType)) { - accountManager.checkAccess(caller, null, true, network); - } + accountManager.checkAccess(caller, null, true, network); // Verify that the network guru supports the protocol specified Map caps = networkModel.getNetworkServiceCapabilities(network.getId(), Network.Service.Firewall); diff --git a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java index 16473e837a9..39546dc9061 100644 --- a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java @@ -301,7 +301,7 @@ public class NetworkMigrationManagerImpl implements NetworkMigrationManager { copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId, vpc.getAccountId(), vpc.getName(), vpc.getDisplayText(), vpc.getCidr(), vpc.getNetworkDomain(), vpc.getIp4Dns1(), vpc.getIp4Dns2(), - vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(), vpc.getPublicMtu()); + vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(), vpc.getPublicMtu(), null, null, null); copyOfVpcId = copyOfVpc.getId(); //on resume of migration the uuid will be swapped already. So the copy will have the value of the original vpcid. diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java index fb6433b5f6b..3eeee8d5ae3 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -1150,6 +1150,11 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi return _ntwkSrvcDao.canProviderSupportServiceInNetwork(networkId, service, provider); } + @Override + public boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services) { + return _ntwkSrvcDao.isAnyServiceSupportedInNetwork(networkId, provider, services); + } + @Override public List listSupportedNetworkServiceProviders(String serviceName) { Network.Service service = null; diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index 8da4185ac55..126800e0af3 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -41,6 +41,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.bgp.BGPService; import com.cloud.dc.VlanDetailsVO; import com.cloud.dc.dao.VlanDetailsDao; import com.cloud.network.dao.NsxProviderDao; @@ -82,6 +83,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.network.NetworkPermissionVO; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.dao.NetworkPermissionDao; import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; import org.apache.commons.collections.CollectionUtils; @@ -415,6 +417,11 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C NsxProviderDao nsxProviderDao; @Inject private VirtualRouterProviderDao virtualRouterProviderDao; + @Inject + RoutedIpv4Manager routedIpv4Manager; + @Inject + private BGPService bgpService; + List internalLoadBalancerElementServices = new ArrayList<>(); Map internalLoadBalancerElementServiceMap = new HashMap<>(); @@ -1377,6 +1384,40 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } } + void validateNetworkCidrSize(Account caller, Integer cidrSize, String cidr, NetworkOffering networkOffering, long accountId) { + if (!GuestType.Isolated.equals(networkOffering.getGuestType())) { + if (cidrSize != null) { + throw new InvalidParameterValueException("network cidr size is only applicable on Isolated networks"); + } + return; + } + if (ObjectUtils.allNotNull(cidr, cidrSize)) { + throw new InvalidParameterValueException("network cidr and cidr size are mutually exclusive"); + } + if (NetworkOffering.NetworkMode.ROUTED.equals(networkOffering.getNetworkMode()) + && routedIpv4Manager.isVirtualRouterGateway(networkOffering)) { + if (cidr != null) { + if (!networkOffering.isForVpc() && !_accountMgr.isRootAdmin(caller.getId())) { + throw new InvalidParameterValueException("Only root admin can set the gateway/netmask of Isolated networks with ROUTED mode"); + } + return; + } + if (cidrSize == null) { + throw new InvalidParameterValueException("network cidr or cidr size is required for Isolated networks with ROUTED mode"); + } + Integer maxCidrSize = routedIpv4Manager.RoutedNetworkIPv4MaxCidrSize.valueIn(accountId); + if (cidrSize > maxCidrSize) { + throw new InvalidParameterValueException("network cidr size cannot be bigger than maximum cidr size " + maxCidrSize); + } + Integer minCidrSize = routedIpv4Manager.RoutedNetworkIPv4MinCidrSize.valueIn(accountId); + if (cidrSize < minCidrSize) { + throw new InvalidParameterValueException("network cidr size cannot be smaller than minimum cidr size " + minCidrSize); + } + } else if (cidrSize != null) { + throw new InvalidParameterValueException("network cidr size is only applicable on Isolated networks with ROUTED mode: " + cidrSize); + } + } + void validateSharedNetworkRouterIPs(String gateway, String startIP, String endIP, String netmask, String routerIPv4, String routerIPv6, String startIPv6, String endIPv6, String ip6Cidr, NetworkOffering ntwkOff) { if (ntwkOff.getGuestType() == GuestType.Shared) { validateSharedNetworkRouterIPv4(routerIPv4, startIP, endIP, gateway, netmask); @@ -1452,6 +1493,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C boolean hideIpAddressUsage = adminCalledUs && ((CreateNetworkCmdByAdmin)cmd).getHideIpAddressUsage(); String routerIPv4 = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getRouterIp() : null; String routerIPv6 = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6() : null; + Long asNumber = cmd.getAsNumber(); String name = cmd.getNetworkName(); String displayText = cmd.getDisplayText(); @@ -1476,6 +1518,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C String ip4Dns2 = cmd.getIp4Dns2(); String ip6Dns1 = cmd.getIp6Dns1(); String ip6Dns2 = cmd.getIp6Dns2(); + Integer networkCidrSize = cmd.getCidrSize(); + List bgpPeerIds = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getBgpPeerIds() : null; // Validate network offering id NetworkOffering ntwkOff = getAndValidateNetworkOffering(networkOfferingId); @@ -1607,6 +1651,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } } + if (isNonVpcNetworkSupportingDynamicRouting(ntwkOff) && ntwkOff.isSpecifyAsNumber() && asNumber == null) { + throw new InvalidParameterValueException("AS number is required for the network but not passed."); + } + + validateNetworkCidrSize(caller, networkCidrSize, cidr, ntwkOff, owner.getAccountId()); + validateSharedNetworkRouterIPs(gateway, startIP, endIP, netmask, routerIPv4, routerIPv6, startIPv6, endIPv6, ip6Cidr, ntwkOff); Pair ip6GatewayCidr = null; @@ -1655,6 +1705,17 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C throw new InvalidParameterValueException("Only ROOT admin is allowed to specify vlanId or bypass vlan overlap check"); } + // Validate BGP peers + if (CollectionUtils.isNotEmpty(bgpPeerIds)) { + if (vpcId != null) { + throw new InvalidParameterValueException("The BGP peers of VPC tiers will inherit from the VPC, do not add separately."); + } + if (!routedIpv4Manager.isDynamicRoutedNetwork(ntwkOff)) { + throw new InvalidParameterValueException("The network offering does not support Dynamic routing"); + } + routedIpv4Manager.validateBgpPeers(owner, zone.getId(), bgpPeerIds); + } + if (ipv4) { // For non-root admins check cidr limit - if it's allowed by global config value if (!_accountMgr.isRootAdmin(caller.getId()) && cidr != null) { @@ -1729,7 +1790,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zone.getId(), domainId, isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, secondaryVlanId, privateVlanType, ntwkOff, pNtwk, aclType, owner, cidr, createVlan, - externalId, routerIPv4, routerIPv6, associatedNetwork, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, interfaceMTUs); + externalId, routerIPv4, routerIPv6, associatedNetwork, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, interfaceMTUs, networkCidrSize); // retrieve, acquire and associate the correct IP addresses checkAndSetRouterSourceNatIp(owner, cmd, network); @@ -1742,6 +1803,22 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C ipv6Service.assignIpv6SubnetToNetwork(ip6Cidr, network.getId()); } + // assign to network + if (NetworkOffering.NetworkMode.ROUTED.equals(ntwkOff.getNetworkMode())) { + routedIpv4Manager.assignIpv4SubnetToNetwork(network); + } + if (isNonVpcNetworkSupportingDynamicRouting(ntwkOff)) { + try { + bgpService.allocateASNumber(zone.getId(), asNumber, network.getId(), null); + } catch (CloudRuntimeException ex) { + deleteNetwork(network.getId(), true); + throw ex; + } + } + if (CollectionUtils.isNotEmpty(bgpPeerIds)) { + routedIpv4Manager.persistBgpPeersForGuestNetwork(network.getId(), bgpPeerIds); + } + // if the network offering has persistent set to true, implement the network if (ntwkOff.isPersistent()) { return implementedNetworkInCreation(caller, zone, network); @@ -1749,6 +1826,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C return network; } + private boolean isNonVpcNetworkSupportingDynamicRouting(NetworkOffering networkOffering) { + return !networkOffering.isForVpc() && NetworkOffering.RoutingMode.Dynamic == networkOffering.getRoutingMode(); + } + private void validateNetworkCreationSupported(long zoneId, String zoneName, GuestType guestType) { NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId); if (Objects.nonNull(nsxProviderVO) && List.of(GuestType.L2, GuestType.Shared).contains(guestType)) { @@ -1767,7 +1848,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C return _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, null, null, null, false, null, owner, null, physicalNetwork, zoneId, aclType, null, null, null, null, true, null, - null, null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, null, null); } void checkAndSetRouterSourceNatIp(Account owner, CreateNetworkCmd cmd, Network network) throws InsufficientAddressCapacityException, ResourceAllocationException { @@ -2148,12 +2229,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } } - private Network commitNetwork(final Long networkOfferingId, final String gateway, final String startIP, final String endIP, final String netmask, final String networkDomain, final String vlanIdFinal, + protected Network commitNetwork(final Long networkOfferingId, final String gateway, final String startIP, final String endIP, final String netmask, final String networkDomain, final String vlanIdFinal, final Boolean bypassVlanOverlapCheck, final String name, final String displayText, final Account caller, final Long physicalNetworkId, final Long zoneId, final Long domainId, final boolean isDomainSpecific, final Boolean subdomainAccessFinal, final Long vpcId, final String startIPv6, final String endIPv6, final String ip6Gateway, final String ip6Cidr, final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final PVlanType isolatedPvlanType, final NetworkOffering ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal, final String cidr, final boolean createVlan, final String externalId, String routerIp, String routerIpv6, - final Network associatedNetwork, final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, Pair vrIfaceMTUs) throws InsufficientCapacityException, ResourceAllocationException { + final Network associatedNetwork, final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, Pair vrIfaceMTUs, + final Integer networkCidrSize) throws InsufficientCapacityException, ResourceAllocationException { try { Network network = Transaction.execute(new TransactionCallbackWithException() { @Override @@ -2209,7 +2291,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } } network = _vpcMgr.createVpcGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, sharedDomainId, pNtwk, zoneId, aclType, - subdomainAccess, vpcId, aclId, caller, displayNetwork, externalId, ip6Gateway, ip6Cidr, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs); + subdomainAccess, vpcId, aclId, caller, displayNetwork, externalId, ip6Gateway, ip6Cidr, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs, networkCidrSize); } else { if (_configMgr.isOfferingForVpc(ntwkOff)) { throw new InvalidParameterValueException("Network offering can be used for VPC networks only"); @@ -2218,7 +2300,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C throw new InvalidParameterValueException("Internal Lb can be enabled on vpc networks only"); } network = _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, networkDomain, owner, sharedDomainId, pNtwk, - zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, isolatedPvlanType, externalId, routerIp, routerIpv6, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs); + zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, isolatedPvlanType, externalId, routerIp, routerIpv6, ip4Dns1, ip4Dns2, + ip6Dns1, ip6Dns2, vrIfaceMTUs, networkCidrSize); } if (createVlan && network != null) { @@ -4021,6 +4104,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C return false; } + // network mode should be the same + NetworkOffering.NetworkMode oldNetworkMode = oldNetworkOffering.getNetworkMode() == null ? NetworkOffering.NetworkMode.NATTED: oldNetworkOffering.getNetworkMode(); + NetworkOffering.NetworkMode newNetworkMode = newNetworkOffering.getNetworkMode() == null ? NetworkOffering.NetworkMode.NATTED: newNetworkOffering.getNetworkMode(); + if (!oldNetworkMode.equals(newNetworkMode)) { + logger.debug("Network offerings " + newNetworkOfferingId + " and " + oldNetworkOfferingId + " have different values for network mode, can't upgrade"); + return false; + } + return canMoveToPhysicalNetwork(network, oldNetworkOfferingId, newNetworkOfferingId); } diff --git a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java index a4d48519f43..a87504cd07a 100644 --- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java @@ -24,7 +24,9 @@ import java.util.Set; import javax.inject.Inject; +import org.apache.cloudstack.network.BgpPeer; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -95,6 +97,7 @@ import com.cloud.network.rules.LoadBalancerContainer; import com.cloud.network.rules.PortForwardingRule; import com.cloud.network.rules.RulesManager; import com.cloud.network.rules.StaticNat; +import com.cloud.network.vpc.Vpc; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; @@ -119,7 +122,7 @@ import com.cloud.vm.dao.UserVmDao; public class VirtualRouterElement extends AdapterBase implements VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider, LoadBalancingServiceProvider, PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer, -NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServiceProvider{ +NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServiceProvider, BgpServiceProvider { protected static final Map> capabilities = setCapabilities(); @Inject @@ -553,7 +556,9 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ dhcpCapabilities.put(Capability.DhcpAccrossMultipleSubnets, "true"); capabilities.put(Service.Dhcp, dhcpCapabilities); - capabilities.put(Service.Gateway, null); + final Map gatewayCapabilities = new HashMap(); + gatewayCapabilities.put(Capability.RedundantRouter, "true"); + capabilities.put(Service.Gateway, gatewayCapabilities); final Map sourceNatCapabilities = new HashMap(); sourceNatCapabilities.put(Capability.SupportedSourceNatTypes, "peraccount"); @@ -1391,4 +1396,36 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ _routerDao.persist(router); } } + + @Override + public boolean applyBgpPeers(Vpc vpc, Network network, List bgpPeers) throws ResourceUnavailableException { + if (ObjectUtils.allNull(vpc, network)) { + throw new CloudRuntimeException("One of VPC and network must be passed, however both are null."); + } + final List routers; + if (vpc != null) { + routers = _routerDao.listByVpcId(vpc.getId()); + } else { + routers = _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER); + } + + if (CollectionUtils.isEmpty(routers)) { + logger.warn(String.format("Can't find at least one router for vpc %s or network %s !", vpc, network)); + return true; + } + + boolean result = true; + long dataCenterId = vpc != null ? vpc.getZoneId() : network.getDataCenterId(); + final DataCenterVO dcVO = _dcDao.findById(dataCenterId); + final NetworkTopology networkTopology = networkTopologyContext.retrieveNetworkTopology(dcVO); + + for (final DomainRouterVO domainRouterVO : routers) { + if (domainRouterVO.getState() != VirtualMachine.State.Running) { + continue; + } + + result = result && networkTopology.applyBgpPeers(network, bgpPeers, domainRouterVO); + } + return result; + } } diff --git a/server/src/main/java/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/main/java/com/cloud/network/firewall/FirewallManagerImpl.java index 0a4ef980b7b..7194b86e3e1 100644 --- a/server/src/main/java/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/main/java/com/cloud/network/firewall/FirewallManagerImpl.java @@ -39,6 +39,7 @@ import org.apache.cloudstack.api.command.user.ipv6.ListIpv6FirewallRulesCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.springframework.stereotype.Component; import com.cloud.configuration.Config; @@ -154,6 +155,8 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, List _networkAclElements; @Inject IpAddressManager _ipAddrMgr; + @Inject + RoutedIpv4Manager routedIpv4Manager; private boolean _elbEnabled = false; static Boolean rulesContinueOnErrFlag = true; @@ -538,6 +541,9 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, } else if (purpose == Purpose.PortForwarding) { caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.PortForwarding); } else if (purpose == Purpose.Firewall) { + if (routedIpv4Manager.isVirtualRouterGateway(network)) { + throw new CloudRuntimeException("Unable to create routing firewall rule. Please use routing firewall API instead."); + } caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Firewall); } @@ -809,16 +815,12 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, if (apply) { // ingress firewall rule if (rule.getSourceIpAddressId() != null) { - //feteches ingress firewall, ingress firewall rules associated with the ip + //fetches ingress firewall, ingress firewall rules associated with the ip List rules = _firewallDao.listByIpAndPurpose(rule.getSourceIpAddressId(), Purpose.Firewall); return applyFirewallRules(rules, false, caller); - //egress firewall rule } else if (networkId != null) { - boolean isIpv6 = Purpose.Ipv6Firewall.equals(rule.getPurpose()); - List rules = _firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), rule.getPurpose(), FirewallRule.TrafficType.Egress); - if (isIpv6) { - rules.addAll(_firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), Purpose.Ipv6Firewall, FirewallRule.TrafficType.Ingress)); - } + //egress firewall rule, or ipv4/ipv6 routing firewall rule + List rules = _firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), rule.getPurpose(), rule.getTrafficType()); return applyFirewallRules(rules, false, caller); } } else { diff --git a/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java index bdabc6c03a1..4f76488337d 100644 --- a/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java @@ -24,6 +24,8 @@ import javax.inject.Inject; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.RoutedIpv4Manager; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -35,6 +37,7 @@ import com.cloud.event.EventTypes; import com.cloud.event.EventVO; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; @@ -87,6 +90,8 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru { FirewallRulesDao _fwRulesDao; @Inject FirewallRulesCidrsDao _fwRulesCidrDao; + @Inject + RoutedIpv4Manager routedIpv4Manager; public ExternalGuestNetworkGuru() { super(); @@ -121,6 +126,23 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru { /* In order to revert userSpecified network setup */ config.setState(State.Allocated); } + if (NetworkOffering.NetworkMode.ROUTED.equals(offering.getNetworkMode()) && !offering.isForVpc()) { + if (userSpecified.getCidr() != null) { + routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetwork(config, userSpecified.getCidr()); + } else { + if (userSpecified.getNetworkCidrSize() == null) { + throw new InvalidParameterValueException("The network CIDR or CIDR size must be specified."); + } + Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetwork(owner.getDomainId(), owner.getAccountId(), config.getDataCenterId(), userSpecified.getNetworkCidrSize()); + if (subnet != null) { + final String[] cidrTuple = subnet.getSubnet().split("\\/"); + config.setGateway(NetUtils.getIpRangeStartIpFromCidr(cidrTuple[0], Long.parseLong(cidrTuple[1]))); + config.setCidr(subnet.getSubnet()); + } else { + throw new InvalidParameterValueException("Failed to allocate a CIDR with requested size."); + } + } + } return updateNetworkDesignForIPv6IfNeeded(config, userSpecified); } diff --git a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java index f8df8bef236..c46be9bf428 100644 --- a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java @@ -241,7 +241,9 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur if (userSpecified.getCidr() != null) { network.setCidr(userSpecified.getCidr()); network.setGateway(userSpecified.getGateway()); - } else if (offering.getGuestType() != GuestType.L2 && (offering.getGuestType() == GuestType.Shared || !_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty())) { + } else if (offering.getGuestType() != GuestType.L2 + && !NetworkOffering.NetworkMode.ROUTED.equals(offering.getNetworkMode()) + && (offering.getGuestType() == GuestType.Shared || !_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty())) { final String guestNetworkCidr = dc.getGuestNetworkCidr(); if (guestNetworkCidr != null) { final String[] cidrTuple = guestNetworkCidr.split("\\/"); @@ -409,11 +411,12 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur if (network.getVpcId() != null) { final Vpc vpc = _vpcDao.findById(network.getVpcId()); // Redundant Networks need a guest IP that is not the same as the gateway IP. - if (_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.SourceNat, Provider.VPCVirtualRouter) && !vpc.isRedundant()) { + if (_networkModel.isAnyServiceSupportedInNetwork(network.getId(), Provider.VPCVirtualRouter, Service.SourceNat, Service.Gateway) + && !vpc.isRedundant()) { isGateway = true; } } else { - if (_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.SourceNat, Provider.VirtualRouter)) { + if (_networkModel.isAnyServiceSupportedInNetwork(network.getId(), Provider.VirtualRouter, Service.SourceNat, Service.Gateway)) { isGateway = true; } } diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java index ce5024a5e1b..5c962a1cbff 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -31,6 +31,9 @@ import javax.inject.Inject; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerTO; +import org.apache.cloudstack.network.dao.BgpPeerDetailsDao; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.springframework.beans.factory.annotation.Autowired; @@ -48,6 +51,7 @@ import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.SetBgpPeersCommand; import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetIpv6FirewallRulesCommand; import com.cloud.agent.api.routing.SetNetworkACLCommand; @@ -71,9 +75,11 @@ import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.agent.manager.Commands; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.ASNumberVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.ASNumberDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.VlanDao; import com.cloud.domain.Domain; @@ -209,6 +215,10 @@ public class CommandSetupHelper { Ipv6Service ipv6Service; @Inject VirtualRouterProviderDao vrProviderDao; + @Inject + ASNumberDao asNumberDao; + @Inject + BgpPeerDetailsDao bgpPeerDetailsDao; @Autowired @Qualifier("networkHelper") @@ -454,8 +464,12 @@ public class CommandSetupHelper { _rulesDao.loadSourceCidrs((FirewallRuleVO) rule); final FirewallRule.TrafficType traffictype = rule.getTrafficType(); if (traffictype == FirewallRule.TrafficType.Ingress) { - final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); - final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr(), Purpose.Firewall, traffictype); + String srcIp = null; + if (rule.getSourceIpAddressId() != null) { + final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); + srcIp = sourceIp.getAddress().addr(); + } + final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, srcIp, Purpose.Firewall, traffictype); rulesTO.add(ruleTO); } else if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { final NetworkVO network = _networkDao.findById(guestNetworkId); @@ -539,8 +553,12 @@ public class CommandSetupHelper { _rulesDao.loadDestinationCidrs((FirewallRuleVO)rule); final FirewallRule.TrafficType traffictype = rule.getTrafficType(); if (traffictype == FirewallRule.TrafficType.Ingress) { - final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); - final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr(), Purpose.Firewall, traffictype); + String srcIp = null; + if (rule.getSourceIpAddressId() != null) { + final IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); + srcIp = sourceIp.getAddress().addr(); + } + final FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, srcIp, Purpose.Firewall, traffictype); rulesTO.add(ruleTO); } else if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { final NetworkVO network = _networkDao.findById(guestNetworkId); @@ -1404,4 +1422,43 @@ public class CommandSetupHelper { cmds.addCommand("updateNetwork", cmd); } } + + public void createBgpPeersCommands(final List bgpPeers, final VirtualRouter router, final Commands cmds, final Network network) { + List bgpPeerTOs = new ArrayList<>(); + + ASNumberVO asNumberVO = router.getVpcId() != null ? + asNumberDao.findByZoneAndVpcId(router.getDataCenterId(), router.getVpcId()) : + asNumberDao.findByZoneAndNetworkId(router.getDataCenterId(), network.getId()); + if (asNumberVO == null) { + logger.debug("No AS number found for the guest network or VPC, skipping."); + return; + } + + List guestNetworks = new ArrayList<>(); + if (router.getVpcId() != null) { + List networks = _networkDao.listByVpc(router.getVpcId()); + for (NetworkVO networkVO : networks) { + final NetworkOfferingVO offering = _networkOfferingDao.findByIdIncludingRemoved(networkVO.getNetworkOfferingId()); + if (NetworkOffering.RoutingMode.Dynamic.equals(offering.getRoutingMode())) { + guestNetworks.add(networkVO); + } + } + } else { + guestNetworks.add(network); + } + for (BgpPeer bgpPeer: bgpPeers) { + Map bgpPeerDetails = bgpPeerDetailsDao.getBgpPeerDetails(bgpPeer.getId()); + for (Network guestNetwork : guestNetworks) { + bgpPeerTOs.add(new BgpPeerTO(bgpPeer.getId(), bgpPeer.getIp4Address(), bgpPeer.getIp6Address(), bgpPeer.getAsNumber(), bgpPeer.getPassword(), + guestNetwork.getId(), asNumberVO.getAsNumber(), guestNetwork.getCidr(), guestNetwork.getIp6Cidr(), bgpPeerDetails)); + } + } + + final SetBgpPeersCommand cmd = new SetBgpPeersCommand(bgpPeerTOs); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId()); + cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString()); + cmds.addCommand("bgpPeersCommand", cmd); + } } diff --git a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 52a488fad82..8f07fcfca3b 100644 --- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -65,6 +65,10 @@ import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO; import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.network.BgpPeerVO; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.apache.cloudstack.network.dao.BgpPeerDao; +import org.apache.cloudstack.network.dao.BgpPeerNetworkMapDao; import org.apache.cloudstack.network.topology.NetworkTopology; import org.apache.cloudstack.network.topology.NetworkTopologyContext; import org.apache.cloudstack.utils.CloudStackVersion; @@ -341,6 +345,12 @@ Configurable, StateListener bgpPeers = bgpPeerDao.listNonRevokeByVpcId(guestNetwork.getVpcId()); + _commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, guestNetwork); + } + } else { + if (routedIpv4Manager.isDynamicRoutedNetwork(guestNetwork)) { + final List bgpPeers = bgpPeerDao.listNonRevokeByNetworkId(guestNetworkId); + _commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, guestNetwork); + } + } + if (publicIps != null && !publicIps.isEmpty()) { final List vpns = new ArrayList(); final List pfRules = new ArrayList(); @@ -2548,7 +2600,6 @@ Configurable, StateListener dhcpCapabilities = _networkSvc.getNetworkOfferingServiceCapabilities( _networkOfferingDao.findById(_networkDao.findById(guestNetworkId).getNetworkOfferingId()), Service.Dhcp); @@ -3130,6 +3181,9 @@ Configurable, StateListener " + routerNic.getNetworkId() + ". It might be a problem!"); continue; } + if (routedIpv4Manager.isRoutedNetwork(network)) { + continue; + } if (forVpc && network.getTrafficType() == TrafficType.Public || !forVpc && network.getTrafficType() == TrafficType.Guest && network.getGuestType() == Network.GuestType.Isolated) { final NetworkUsageCommand usageCmd = new NetworkUsageCommand(privateIP, router.getHostName(), forVpc, routerNic.getIPv4Address()); diff --git a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java index d6c53504c68..bb517eed524 100644 --- a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -353,6 +353,9 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian if (defaultIp6Dns2 != null) { buf.append(" ip6dns2=").append(defaultIp6Dns2); } + if (routedIpv4Manager.isRoutedVpc(vpc)) { + buf.append(" is_routed=true"); + } } } @@ -461,6 +464,9 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian domainRouterVO.getInstanceName(), domainRouterVO.getType(), details); cmds.addCommand(plugNicCmd); final VpcVO vpc = _vpcDao.findById(domainRouterVO.getVpcId()); + if (routedIpv4Manager.isRoutedVpc(vpc)) { + continue; + } final NetworkUsageCommand netUsageCmd = new NetworkUsageCommand(domainRouterVO.getPrivateIpAddress(), domainRouterVO.getInstanceName(), true, publicNic.getIPv4Address(), vpc.getCidr()); usageCmds.add(netUsageCmd); UserStatisticsVO stats = _userStatsDao.findBy(domainRouterVO.getAccountId(), domainRouterVO.getDataCenterId(), publicNtwk.getId(), publicNic.getIPv4Address(), domainRouterVO.getId(), diff --git a/server/src/main/java/com/cloud/network/rules/BgpPeersRules.java b/server/src/main/java/com/cloud/network/rules/BgpPeersRules.java new file mode 100644 index 00000000000..a4f780c10d3 --- /dev/null +++ b/server/src/main/java/com/cloud/network/rules/BgpPeersRules.java @@ -0,0 +1,48 @@ +// 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.rules; + +import java.util.List; + +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.topology.NetworkTopologyVisitor; + +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.router.VirtualRouter; + +public class BgpPeersRules extends RuleApplier { + + private final List bgpPeers; + + public BgpPeersRules(final List bgpPeers, final Network network) { + super(network); + this.bgpPeers = bgpPeers; + } + + public List getBgpPeers() { + return bgpPeers; + } + + @Override + public boolean accept(final NetworkTopologyVisitor visitor, final VirtualRouter router) throws ResourceUnavailableException { + _router = router; + + return visitor.visit(this); + } +} diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java index 7114bbbd90d..91fceb2cbb3 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -43,12 +43,18 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.ConfigurationManagerImpl; +import com.cloud.dc.ASNumberVO; +import com.cloud.bgp.BGPService; +import com.cloud.dc.dao.ASNumberDao; +import com.google.common.collect.Sets; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.vpc.CreatePrivateGatewayByAdminCmd; +import org.apache.cloudstack.api.command.admin.vpc.CreateVPCCmdByAdmin; import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd; import org.apache.cloudstack.api.command.user.vpc.CreatePrivateGatewayCmd; @@ -63,6 +69,8 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.query.QueryService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.EnumUtils; @@ -180,6 +188,8 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; +import static com.cloud.offering.NetworkOffering.RoutingMode.Dynamic; + public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvisioningService, VpcService { public static final String SERVICE = "service"; @@ -266,7 +276,13 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Qualifier("networkHelper") protected NetworkHelper networkHelper; @Inject + private BGPService bgpService; + @Inject + private ASNumberDao asNumberDao; + @Inject private VpcPrivateGatewayTransactionCallable vpcTxCallable; + @Inject + RoutedIpv4Manager routedIpv4Manager; private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker")); private List vpcElements = null; @@ -328,7 +344,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, true, State.Enabled, null, false, - false, false, false, null); + false, false, false, null, null, false); } // configure default vpc offering with Netscaler as LB Provider @@ -347,7 +363,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis svcProviderMap.put(svc, defaultProviders); } } - createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, svcProviderMap, false, State.Enabled, null, false, false, false, false, null); + createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, + svcProviderMap, false, State.Enabled, null, false, false, false, false, null, null, false); } @@ -368,7 +385,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } createVpcOffering(VpcOffering.redundantVPCOfferingName, VpcOffering.redundantVPCOfferingName, svcProviderMap, true, State.Enabled, - null, false, false, true, false, null); + null, false, false, true, false, null, null, false); } // configure default vpc offering with NSX as network service provider in NAT mode @@ -385,7 +402,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } createVpcOffering(VpcOffering.DEFAULT_VPC_NAT_NSX_OFFERING_NAME, VpcOffering.DEFAULT_VPC_NAT_NSX_OFFERING_NAME, svcProviderMap, false, - State.Enabled, null, false, false, false, true, NetworkOffering.NsxMode.NATTED.name()); + State.Enabled, null, false, false, false, true, NetworkOffering.NetworkMode.NATTED, null, false); } @@ -403,7 +420,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } createVpcOffering(VpcOffering.DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME, VpcOffering.DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME, svcProviderMap, false, - State.Enabled, null, false, false, false, true, NetworkOffering.NsxMode.ROUTED.name()); + State.Enabled, null, false, false, false, true, NetworkOffering.NetworkMode.ROUTED, null, false); } } @@ -464,9 +481,18 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis final List domainIds = cmd.getDomainIds(); final List zoneIds = cmd.getZoneIds(); final Boolean forNsx = cmd.isForNsx(); - String nsxMode = cmd.getNsxMode(); + final String networkModeStr = cmd.getNetworkMode(); final boolean enable = cmd.getEnable(); - nsxMode = validateNsxMode(forNsx, nsxMode); + + NetworkOffering.NetworkMode networkMode = null; + if (networkModeStr != null) { + if (!EnumUtils.isValidEnum(NetworkOffering.NetworkMode.class, networkModeStr)) { + throw new InvalidParameterValueException("Invalid mode passed. Valid values: " + Arrays.toString(NetworkOffering.NetworkMode.values())); + } + networkMode = NetworkOffering.NetworkMode.valueOf(networkModeStr); + } + boolean specifyAsNumber = cmd.getSpecifyAsNumber(); + String routingModeString = cmd.getRoutingMode(); // check if valid domain if (CollectionUtils.isNotEmpty(cmd.getDomainIds())) { @@ -489,35 +515,31 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis _ntwkSvc.validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(serviceOfferingId); } - return createVpcOffering(vpcOfferingName, displayText, supportedServices, - serviceProviderList, serviceCapabilityList, internetProtocol, serviceOfferingId, forNsx, nsxMode, - domainIds, zoneIds, (enable ? State.Enabled : State.Disabled)); - } + NetworkOffering.RoutingMode routingMode = ConfigurationManagerImpl.verifyRoutingMode(routingModeString); - private String validateNsxMode(Boolean forNsx, String nsxMode) { - if (Boolean.TRUE.equals(forNsx)) { - if (Objects.isNull(nsxMode)) { - throw new InvalidParameterValueException("Mode for an NSX offering needs to be specified.Valid values: " + Arrays.toString(NetworkOffering.NsxMode.values())); - } - if (!EnumUtils.isValidEnum(NetworkOffering.NsxMode.class, nsxMode)) { - throw new InvalidParameterValueException("Invalid mode passed. Valid values: " + Arrays.toString(NetworkOffering.NsxMode.values())); - } - } else { - if (Objects.nonNull(nsxMode)) { - if (logger.isTraceEnabled()) { - logger.trace("nsxMode has is ignored for non-NSX enabled zones"); - } - nsxMode = null; - } + if (specifyAsNumber && !forNsx) { + String msg = "SpecifyAsNumber can only be true for VPC offerings for NSX"; + logger.error(msg); + throw new InvalidParameterValueException(msg); } - return nsxMode; + + if (specifyAsNumber && Dynamic != routingMode) { + String msg = "SpecifyAsNumber can only be true for Dynamic Route Mode network offerings"; + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + + return createVpcOffering(vpcOfferingName, displayText, supportedServices, + serviceProviderList, serviceCapabilityList, internetProtocol, serviceOfferingId, forNsx, networkMode, + domainIds, zoneIds, (enable ? State.Enabled : State.Disabled), routingMode, specifyAsNumber); } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create = true) public VpcOffering createVpcOffering(final String name, final String displayText, final List supportedServices, final Map> serviceProviders, final Map serviceCapabilityList, final NetUtils.InternetProtocol internetProtocol, final Long serviceOfferingId, - final Boolean forNsx, final String mode, List domainIds, List zoneIds, State state) { + final Boolean forNsx, final NetworkOffering.NetworkMode networkMode, List domainIds, List zoneIds, State state, + NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber) { if (!Ipv6Service.Ipv6OfferingCreationEnabled.value() && !(internetProtocol == null || NetUtils.InternetProtocol.IPv4.equals(internetProtocol))) { throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for creating IPv6 supported VPC offering", Ipv6Service.Ipv6OfferingCreationEnabled.key())); @@ -559,7 +581,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } - if (!sourceNatSvc) { + if (!NetworkOffering.NetworkMode.ROUTED.equals(networkMode) && !sourceNatSvc) { logger.debug("Automatically adding source nat service to the list of VPC services"); svcProviderMap.put(Service.SourceNat, defaultProviders); } @@ -580,6 +602,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (provider == null) { throw new InvalidParameterValueException("Invalid service provider: " + prvNameStr); } + if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode) + && Arrays.asList(Service.SourceNat, Service.StaticNat, Service.Lb, Service.PortForwarding, Service.Vpn).contains(service) + && Provider.VPCVirtualRouter.equals(provider)) { + throw new InvalidParameterValueException("SourceNat/StaticNat/Lb/PortForwarding/Vpn service are not supported by VPC in ROUTED mode"); + } providers.add(provider); } @@ -592,17 +619,21 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis // add gateway provider (if sourceNat provider is enabled) final Set sourceNatServiceProviders = svcProviderMap.get(Service.SourceNat); + Service redundantRouterService = Service.SourceNat; if (CollectionUtils.isNotEmpty(sourceNatServiceProviders)) { svcProviderMap.put(Service.Gateway, sourceNatServiceProviders); + } else if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode)) { + svcProviderMap.put(Service.Gateway, Sets.newHashSet(Provider.VPCVirtualRouter)); + redundantRouterService = Service.Gateway; } validateConnectivtyServiceCapabilities(svcProviderMap.get(Service.Connectivity), serviceCapabilityList); final boolean supportsDistributedRouter = isVpcOfferingSupportsDistributedRouter(serviceCapabilityList); final boolean offersRegionLevelVPC = isVpcOfferingForRegionLevelVpc(serviceCapabilityList); - final boolean redundantRouter = isVpcOfferingRedundantRouter(serviceCapabilityList); + final boolean redundantRouter = isVpcOfferingRedundantRouter(serviceCapabilityList, redundantRouterService); final VpcOfferingVO offering = createVpcOffering(name, displayText, svcProviderMap, false, state, serviceOfferingId, supportsDistributedRouter, offersRegionLevelVPC, - redundantRouter, forNsx, mode); + redundantRouter, forNsx, networkMode, routingMode, specifyAsNumber); if (offering != null) { List detailsVO = new ArrayList<>(); @@ -630,7 +661,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @DB protected VpcOfferingVO createVpcOffering(final String name, final String displayText, final Map> svcProviderMap, final boolean isDefault, final State state, final Long serviceOfferingId, final boolean supportsDistributedRouter, final boolean offersRegionLevelVPC, - final boolean redundantRouter, Boolean forNsx, String mode) { + final boolean redundantRouter, Boolean forNsx, NetworkOffering.NetworkMode networkMode, NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber) { return Transaction.execute(new TransactionCallback() { @Override @@ -642,7 +673,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis offering.setState(state); } offering.setForNsx(forNsx); - offering.setNsxMode(mode); + offering.setNetworkMode(networkMode); + offering.setSpecifyAsNumber(specifyAsNumber); + if (Objects.nonNull(routingMode)) { + offering.setRoutingMode(routingMode); + } + logger.debug("Adding vpc offering " + offering); offering = _vpcOffDao.persist(offering); // populate services and providers @@ -749,8 +785,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return findCapabilityForService(serviceCapabilitystList, Capability.DistributedRouter, Service.Connectivity); } - private boolean isVpcOfferingRedundantRouter(final Map serviceCapabilitystList) { - return findCapabilityForService(serviceCapabilitystList, Capability.RedundantRouter, Service.SourceNat); + private boolean isVpcOfferingRedundantRouter(final Map serviceCapabilitystList, Service redundantRouterService) { + return findCapabilityForService(serviceCapabilitystList, Capability.RedundantRouter, redundantRouterService); } @Override @@ -1086,7 +1122,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true) public Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwnerId, final String vpcName, final String displayText, final String cidr, String networkDomain, - final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, final Boolean displayVpc, Integer publicMtu) throws ResourceAllocationException { + final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, final Boolean displayVpc, Integer publicMtu, + final Integer cidrSize, final Long asNumber, final List bgpPeerIds) throws ResourceAllocationException { final Account caller = CallContext.current().getCallingAccount(); final Account owner = _accountMgr.getAccount(vpcOwnerId); @@ -1115,6 +1152,21 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw ex; } + if (NetworkOffering.RoutingMode.Dynamic.equals(vpcOff.getRoutingMode()) && vpcOff.isSpecifyAsNumber() && asNumber == null) { + throw new InvalidParameterValueException("AS number is required for the VPC but not passed."); + } + + // Validate VPC cidr/cidrsize + validateVpcCidrSize(caller, owner.getAccountId(), vpcOff, cidr, cidrSize); + + // Validate BGP peers + if (CollectionUtils.isNotEmpty(bgpPeerIds)) { + if (!routedIpv4Manager.isDynamicRoutedVpc(vpcOff)) { + throw new InvalidParameterValueException("The VPC offering does not support Dynamic routing"); + } + routedIpv4Manager.validateBgpPeers(owner, zone.getId(), bgpPeerIds); + } + final boolean isRegionLevelVpcOff = vpcOff.isOffersRegionLevelVPC(); if (isRegionLevelVpcOff && networkDomain == null) { throw new InvalidParameterValueException("Network domain must be specified for region level VPC"); @@ -1156,35 +1208,112 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis checkVpcDns(vpcOff, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); + // validate network domain + if (!NetUtils.verifyDomainName(networkDomain)) { + throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " + + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', " + "the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); + } + final boolean useDistributedRouter = vpcOff.isSupportsDistributedRouter(); final VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, owner.getId(), owner.getDomainId(), vpcOffId, cidr, networkDomain, useDistributedRouter, isRegionLevelVpcOff, vpcOff.isRedundantRouter(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); vpc.setPublicMtu(publicMtu); vpc.setDisplay(Boolean.TRUE.equals(displayVpc)); - return createVpc(displayVpc, vpc); + if (vpc.getCidr() == null && cidrSize != null) { + // Allocate a CIDR for VPC + Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForVpc(vpc, cidrSize); + if (subnet != null) { + vpc.setCidr(subnet.getSubnet()); + } else { + throw new CloudRuntimeException("Failed to allocate a CIDR with requested size for VPC."); + } + } + + Vpc newVpc = createVpc(displayVpc, vpc); + // assign Ipv4 subnet to Routed VPC + if (routedIpv4Manager.isRoutedVpc(vpc)) { + routedIpv4Manager.assignIpv4SubnetToVpc(newVpc); + } + if (CollectionUtils.isNotEmpty(bgpPeerIds)) { + routedIpv4Manager.persistBgpPeersForVpc(newVpc.getId(), bgpPeerIds); + } + return newVpc; + } + + private void validateVpcCidrSize(Account caller, long accountId, VpcOffering vpcOffering, String cidr, Integer cidrSize) { + if (ObjectUtils.allNull(cidr, cidrSize)) { + throw new InvalidParameterValueException("VPC cidr or cidr size must be specified"); + } + if (ObjectUtils.allNotNull(cidr, cidrSize)) { + throw new InvalidParameterValueException("VPC cidr and cidr size are mutually exclusive"); + } + if (routedIpv4Manager.isVpcVirtualRouterGateway(vpcOffering)) { + if (cidr != null) { + if (!_accountMgr.isRootAdmin(caller.getId())) { + throw new InvalidParameterValueException("Only root admin can set the gateway/netmask of VPC with ROUTED mode"); + } + return; + } + // verify VPC cidrsize + Integer maxCidrSize = routedIpv4Manager.RoutedVpcIPv4MaxCidrSize.valueIn(accountId); + if (cidrSize > maxCidrSize) { + throw new InvalidParameterValueException("VPC cidr size cannot be bigger than maximum cidr size " + maxCidrSize); + } + Integer minCidrSize = routedIpv4Manager.RoutedVpcIPv4MinCidrSize.valueIn(accountId); + if (cidrSize < minCidrSize) { + throw new InvalidParameterValueException("VPC cidr size cannot be smaller than minimum cidr size " + minCidrSize); + } + } else { + if (cidrSize != null) { + throw new InvalidParameterValueException("VPC cidr size is only applicable on VPC with Routed mode"); + } + } } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true) public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException { + List bgpPeerIds = (cmd instanceof CreateVPCCmdByAdmin) ? ((CreateVPCCmdByAdmin)cmd).getBgpPeerIds() : null; Vpc vpc = createVpc(cmd.getZoneId(), cmd.getVpcOffering(), cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(), cmd.getCidr(), cmd.getNetworkDomain(), cmd.getIp4Dns1(), cmd.getIp4Dns2(), cmd.getIp6Dns1(), - cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu()); + cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu(), cmd.getCidrSize(), cmd.getAsNumber(), bgpPeerIds); String sourceNatIP = cmd.getSourceNatIP(); boolean forNsx = isVpcForNsx(vpc); - if (sourceNatIP != null || forNsx) { - if (forNsx) { - logger.info("Provided source NAT IP will be ignored in an NSX-enabled zone"); - sourceNatIP = null; + try { + if (sourceNatIP != null || forNsx) { + if (forNsx) { + logger.info("Provided source NAT IP will be ignored in an NSX-enabled zone"); + sourceNatIP = null; + } + logger.info(String.format("Trying to allocate the specified IP [%s] as the source NAT of VPC [%s].", sourceNatIP, vpc)); + allocateSourceNatIp(vpc, sourceNatIP); } - logger.info(String.format("Trying to allocate the specified IP [%s] as the source NAT of VPC [%s].", sourceNatIP, vpc)); - allocateSourceNatIp(vpc, sourceNatIP); + if (isVpcOfferingDynamicRouting(vpc)) { + bgpService.allocateASNumber(vpc.getZoneId(), cmd.getAsNumber(), null, vpc.getId()); + } + } catch (CloudRuntimeException ex) { + try { + deleteVpc(vpc.getId()); + } catch (Exception ex2) { + logger.error("Got exception when delete a VPC created just now: {}", ex2.getMessage()); + } + throw ex; } return vpc; } + private boolean isVpcOfferingDynamicRouting(Vpc vpc) { + VpcOffering vpcOffering = getVpcOffering(vpc.getVpcOfferingId()); + if (vpcOffering == null) { + logger.error(String.format("Cannot find VPC offering with ID %s", vpc.getVpcOfferingId())); + return false; + } + return NetworkOffering.RoutingMode.Dynamic == vpcOffering.getRoutingMode(); + } + private boolean isVpcForNsx(Vpc vpc) { if (vpc == null) { return false; @@ -1220,21 +1349,21 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @DB protected Vpc createVpc(final Boolean displayVpc, final VpcVO vpc) { final String cidr = vpc.getCidr(); - // Validate CIDR - if (!NetUtils.isValidIp4Cidr(cidr)) { - throw new InvalidParameterValueException("Invalid CIDR specified " + cidr); + if (cidr != null) { + // Validate CIDR + if (!NetUtils.isValidIp4Cidr(cidr)) { + throw new InvalidParameterValueException("Invalid CIDR specified " + cidr); + } + + // cidr has to be RFC 1918 complient + if (!NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { + throw new InvalidParameterValueException("Guest Cidr " + cidr + " is not RFC1918 compliant"); + } } - // cidr has to be RFC 1918 complient - if (!NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { - throw new InvalidParameterValueException("Guest Cidr " + cidr + " is not RFC1918 compliant"); - } - - // validate network domain - if (!NetUtils.verifyDomainName(vpc.getNetworkDomain())) { - throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " - + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', " + "the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + // get or create Ipv4 subnet for ROUTED VPC + if (routedIpv4Manager.isRoutedVpc(vpc)) { + routedIpv4Manager.getOrCreateIpv4SubnetForVpc(vpc, cidr); } VpcVO vpcVO = Transaction.execute(new TransactionCallback() { @@ -1915,7 +2044,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis // 2) Only Isolated networks with Source nat service enabled can be // added to vpc - if (!guestNtwkOff.isForNsx() && !(guestNtwkOff.getGuestType() == GuestType.Isolated && supportedSvcs.contains(Service.SourceNat))) { + if (!guestNtwkOff.isForNsx() + && !(guestNtwkOff.getGuestType() == GuestType.Isolated && (supportedSvcs.contains(Service.SourceNat) || supportedSvcs.contains(Service.Gateway)))) { throw new InvalidParameterValueException("Only network offerings of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " are valid for vpc "); @@ -2107,8 +2237,18 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis _networkAclMgr.deleteNetworkACL(networkAcl); } + routedIpv4Manager.releaseBgpPeersForVpc(vpcId); + routedIpv4Manager.releaseIpv4SubnetForVpc(vpcId); + VpcVO vpc = vpcDao.findById(vpcId); annotationDao.removeByEntityType(AnnotationService.EntityType.VPC.name(), vpc.getUuid()); + + ASNumberVO asNumber = asNumberDao.findByZoneAndVpcId(vpc.getZoneId(), vpc.getId()); + if (asNumber != null) { + logger.debug(String.format("Releasing AS number %s from VPC %s", asNumber.getAsNumber(), vpc.getName())); + bgpService.releaseASNumber(vpc.getZoneId(), asNumber.getAsNumber(), true); + } + return success; } @@ -3091,8 +3231,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override public Network createVpcGuestNetwork(final long ntwkOffId, final String name, final String displayText, final String gateway, final String cidr, final String vlanId, String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk, final long zoneId, final ACLType aclType, final Boolean subdomainAccess, - final long vpcId, final Long aclId, final Account caller, final Boolean isDisplayNetworkEnabled, String externalId, String ip6Gateway, String ip6Cidr, final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, Pair vrIfaceMTUs) throws ConcurrentOperationException, InsufficientCapacityException, - ResourceAllocationException { + final long vpcId, final Long aclId, final Account caller, final Boolean isDisplayNetworkEnabled, String externalId, String ip6Gateway, String ip6Cidr, + final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize) + throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { final Vpc vpc = getActiveVpc(vpcId); @@ -3116,7 +3257,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis // 2) Create network final Network guestNetwork = _ntwkMgr.createGuestNetwork(ntwkOffId, name, displayText, gateway, cidr, vlanId, false, networkDomain, owner, domainId, pNtwk, zoneId, aclType, - subdomainAccess, vpcId, ip6Gateway, ip6Cidr, isDisplayNetworkEnabled, null, null, externalId, null, null, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs); + subdomainAccess, vpcId, ip6Gateway, ip6Cidr, isDisplayNetworkEnabled, null, null, externalId, null, null, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs, networkCidrSize); if (guestNetwork != null) { guestNetwork.setNetworkACLId(aclId); @@ -3209,8 +3350,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override public boolean isSrcNatIpRequired(long vpcOfferingId) { final Map> vpcOffSvcProvidersMap = getVpcOffSvcProvidersMap(vpcOfferingId); - return Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.SourceNat)) && (vpcOffSvcProvidersMap.get(Network.Service.SourceNat).contains(Network.Provider.VPCVirtualRouter) || - vpcOffSvcProvidersMap.get(Service.SourceNat).contains(Provider.Nsx)); + return (Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.SourceNat)) + && (vpcOffSvcProvidersMap.get(Network.Service.SourceNat).contains(Network.Provider.VPCVirtualRouter) + || vpcOffSvcProvidersMap.get(Service.SourceNat).contains(Provider.Nsx))) + || (Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.Gateway)) + && vpcOffSvcProvidersMap.get(Service.Gateway).contains(Network.Provider.VPCVirtualRouter)); } /** diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java index 81071db3810..7926498c123 100644 --- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java @@ -1201,28 +1201,28 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio // Offering #9 - network offering for NSX provider - NATTED mode createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING, "Offering for NSX enabled networks - NAT mode", - NetworkOffering.NsxMode.NATTED, false, true); + NetworkOffering.NetworkMode.NATTED, false, true); // Offering #10 - network offering for NSX provider - ROUTED mode createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_ROUTED_NSX_OFFERING, "Offering for NSX enabled networks - ROUTED mode", - NetworkOffering.NsxMode.ROUTED, false, true); + NetworkOffering.NetworkMode.ROUTED, false, true); // Offering #11 - network offering for NSX provider for VPCs - NATTED mode createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING_FOR_VPC, "Offering for NSX enabled networks on VPCs - NAT mode", - NetworkOffering.NsxMode.NATTED, true, true); + NetworkOffering.NetworkMode.NATTED, true, true); // Offering #12 - network offering for NSX provider for VPCs - ROUTED mode createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC, "Offering for NSX enabled networks on VPCs - ROUTED mode", - NetworkOffering.NsxMode.ROUTED, true, true); + NetworkOffering.NetworkMode.ROUTED, true, true); // Offering #13 - network offering for NSX provider for VPCs with Internal LB - NATTED mode createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB, "Offering for NSX enabled networks on VPCs with internal LB - NAT mode", - NetworkOffering.NsxMode.NATTED, true, false); + NetworkOffering.NetworkMode.NATTED, true, false); } }); } - private void createAndPersistDefaultNsxOffering(String name, String displayText, NetworkOffering.NsxMode nsxMode, + private void createAndPersistDefaultNsxOffering(String name, String displayText, NetworkOffering.NetworkMode networkMode, boolean forVpc, boolean publicLB) { NetworkOfferingVO defaultNatNSXNetworkOffering = new NetworkOfferingVO(name, displayText, TrafficType.Guest, false, false, null, @@ -1231,11 +1231,11 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio defaultNatNSXNetworkOffering.setPublicLb(publicLB); defaultNatNSXNetworkOffering.setInternalLb(!publicLB); defaultNatNSXNetworkOffering.setForNsx(true); - defaultNatNSXNetworkOffering.setNsxMode(nsxMode.name()); + defaultNatNSXNetworkOffering.setNetworkMode(networkMode); defaultNatNSXNetworkOffering.setState(NetworkOffering.State.Enabled); defaultNatNSXNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultNatNSXNetworkOffering); - Map serviceProviderMap = getServicesAndProvidersForNSXNetwork(nsxMode, forVpc, publicLB); + Map serviceProviderMap = getServicesAndProvidersForNSXNetwork(networkMode, forVpc, publicLB); for (Map.Entry service : serviceProviderMap.entrySet()) { NetworkOfferingServiceMapVO offService = new NetworkOfferingServiceMapVO(defaultNatNSXNetworkOffering.getId(), service.getKey(), service.getValue()); @@ -1244,7 +1244,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio } } - private Map getServicesAndProvidersForNSXNetwork(NetworkOffering.NsxMode nsxMode, boolean forVpc, boolean publicLB) { + private Map getServicesAndProvidersForNSXNetwork(NetworkOffering.NetworkMode networkMode, boolean forVpc, boolean publicLB) { final Map serviceProviderMap = new HashMap<>(); Provider routerProvider = forVpc ? Provider.VPCVirtualRouter : Provider.VirtualRouter; serviceProviderMap.put(Service.Dhcp, routerProvider); @@ -1255,7 +1255,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio } else { serviceProviderMap.put(Service.Firewall, Provider.Nsx); } - if (nsxMode == NetworkOffering.NsxMode.NATTED) { + if (networkMode == NetworkOffering.NetworkMode.NATTED) { serviceProviderMap.put(Service.SourceNat, Provider.Nsx); serviceProviderMap.put(Service.StaticNat, Provider.Nsx); serviceProviderMap.put(Service.PortForwarding, Provider.Nsx); diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 10d5f651242..cbde58dc721 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -68,6 +68,11 @@ import org.apache.cloudstack.api.command.admin.affinitygroup.UpdateVMAffinityGro import org.apache.cloudstack.api.command.admin.alert.GenerateAlertCmd; import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd; import org.apache.cloudstack.api.command.admin.autoscale.DeleteCounterCmd; +import org.apache.cloudstack.api.command.admin.bgp.CreateASNRangeCmd; +import org.apache.cloudstack.api.command.admin.bgp.DeleteASNRangeCmd; +import org.apache.cloudstack.api.command.admin.bgp.ListASNRangesCmd; +import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd; +import org.apache.cloudstack.api.command.admin.bgp.ReleaseASNumberCmd; import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd; import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd; import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; @@ -4012,6 +4017,11 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(RemoveSecondaryStorageSelectorCmd.class); cmdList.add(ListAffectedVmsForStorageScopeChangeCmd.class); + cmdList.add(CreateASNRangeCmd.class); + cmdList.add(ListASNRangesCmd.class); + cmdList.add(DeleteASNRangeCmd.class); + cmdList.add(ListASNumbersCmd.class); + cmdList.add(ReleaseASNumberCmd.class); // Out-of-band management APIs for admins cmdList.add(EnableOutOfBandManagementForHostCmd.class); diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 07d06fbd2f7..6a9e15a58c7 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -73,6 +73,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleDao; import org.apache.cloudstack.resourcedetail.UserDetailVO; import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao; @@ -320,6 +321,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M private IpAddressManager _ipAddrMgr; @Inject private RoleService roleService; + @Inject + private RoutedIpv4Manager routedIpv4Manager; @Inject private PasswordPolicy passwordPolicy; @@ -1067,6 +1070,12 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } } + // remove dedicated IPv4 subnets + routedIpv4Manager.removeIpv4SubnetsForZoneByAccountId(accountId); + + // remove dedicated BGP peers + routedIpv4Manager.removeBgpPeersByAccountId(accountId); + // release account specific guest vlans List maps = _accountGuestVlanMapDao.listAccountGuestVlanMapsByAccount(accountId); for (AccountGuestVlanMapVO map : maps) { diff --git a/server/src/main/java/com/cloud/user/DomainManagerImpl.java b/server/src/main/java/com/cloud/user/DomainManagerImpl.java index 51705e63f3a..4a81772d6d7 100644 --- a/server/src/main/java/com/cloud/user/DomainManagerImpl.java +++ b/server/src/main/java/com/cloud/user/DomainManagerImpl.java @@ -51,6 +51,7 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.region.RegionManager; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.apache.commons.collections.CollectionUtils; @@ -161,6 +162,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom private ResourceLimitService resourceLimitService; @Inject private AffinityGroupDomainMapDao affinityGroupDomainMapDao; + @Inject + private RoutedIpv4Manager routedIpv4Manager; @Inject MessageBus _messageBus; @@ -393,6 +396,12 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom removeDomainWithNoAccountsForCleanupNetworksOrDedicatedResources(domain); } + // remove dedicated IPv4 subnets + routedIpv4Manager.removeIpv4SubnetsForZoneByDomainId(domain.getId()); + + // remove dedicated BGP peers + routedIpv4Manager.removeBgpPeersByDomainId(domain.getId()); + if (!_configMgr.releaseDomainSpecificVirtualRanges(domain.getId())) { CloudRuntimeException e = new CloudRuntimeException("Can't delete the domain yet because failed to release domain specific virtual ip ranges"); e.addProxyObject(domain.getUuid(), "domainId"); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 9502a960f2e..17596163c37 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -3982,7 +3982,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of deployVM process"); Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", null, null, null, false, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null, true, null, null, - null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, null); if (newNetwork != null) { defaultNetwork = _networkDao.findById(newNetwork.getId()); } @@ -7810,7 +7810,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), newAccount.getAccountName() + "-network", newAccount.getAccountName() + "-network", null, null, null, false, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, - null, null, true, null, null, null, null, null, null, null, null, null, null); + null, null, true, null, null, null, null, null, null, null, null, null, null, null); // if the network offering has persistent set to true, implement the network if (requiredOfferings.get(0).isPersistent()) { DeployDestination dest = new DeployDestination(zone, null, null, null); diff --git a/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java b/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java new file mode 100644 index 00000000000..bbad93737f1 --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java @@ -0,0 +1,1621 @@ +// 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.network; + +import com.cloud.api.ApiDBUtils; +import com.cloud.bgp.BGPService; +import com.cloud.dc.DataCenter; +import com.cloud.domain.Domain; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkModel; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.firewall.FirewallService; +import com.cloud.network.rules.FirewallManager; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcOffering; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.network.vpc.dao.VpcOfferingDao; +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; +import com.cloud.projects.Project; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentLifecycleBase; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackWithException; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; + +import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.DedicateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteIpv4SubnetForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.ListIpv4SubnetsForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.ListIpv4SubnetsForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.ReleaseDedicatedIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.UpdateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ChangeBgpPeersForNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ChangeBgpPeersForVpcCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.CreateBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.DedicateBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.DeleteBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ListBgpPeersCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ReleaseDedicatedBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.UpdateBgpPeerCmd; +import org.apache.cloudstack.api.command.user.network.routing.CreateRoutingFirewallRuleCmd; +import org.apache.cloudstack.api.command.user.network.routing.DeleteRoutingFirewallRuleCmd; +import org.apache.cloudstack.api.command.user.network.routing.ListRoutingFirewallRulesCmd; +import org.apache.cloudstack.api.command.user.network.routing.UpdateRoutingFirewallRuleCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnetVO; +import org.apache.cloudstack.datacenter.dao.DataCenterIpv4GuestSubnetDao; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap.State; +import org.apache.cloudstack.network.dao.BgpPeerDao; +import org.apache.cloudstack.network.dao.BgpPeerDetailsDao; +import org.apache.cloudstack.network.dao.BgpPeerNetworkMapDao; +import org.apache.cloudstack.network.dao.Ipv4GuestSubnetNetworkMapDao; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements RoutedIpv4Manager { + + @Inject + DataCenterIpv4GuestSubnetDao dataCenterIpv4GuestSubnetDao; + @Inject + Ipv4GuestSubnetNetworkMapDao ipv4GuestSubnetNetworkMapDao; + @Inject + FirewallService firewallService; + @Inject + FirewallManager firewallManager; + @Inject + FirewallRulesDao firewallDao; + @Inject + NetworkServiceMapDao networkServiceMapDao; + @Inject + NetworkOfferingServiceMapDao networkOfferingServiceMapDao; + @Inject + NetworkOfferingDao networkOfferingDao; + @Inject + NetworkModel networkModel; + @Inject + AccountManager accountManager; + @Inject + VpcOfferingDao vpcOfferingDao; + @Inject + VpcOfferingServiceMapDao vpcOfferingServiceMapDao; + @Inject + VpcDao vpcDao; + @Inject + BgpPeerDao bgpPeerDao; + @Inject + BgpPeerDetailsDao bgpPeerDetailsDao; + @Inject + BgpPeerNetworkMapDao bgpPeerNetworkMapDao; + @Inject + NetworkDao networkDao; + @Inject + BGPService bgpService; + + @Override + public String getConfigComponentName() { + return RoutedIpv4Manager.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] { + RoutedNetworkIPv4MaxCidrSize, RoutedNetworkIPv4MinCidrSize, RoutedIPv4NetworkCidrAutoAllocationEnabled, + RoutedVpcIPv4MaxCidrSize, RoutedVpcIPv4MinCidrSize, UseSystemBgpPeers + }; + } + + @Override + public List> getCommands() { + final List> cmdList = new ArrayList>(); + cmdList.add(CreateIpv4SubnetForZoneCmd.class); + cmdList.add(DeleteIpv4SubnetForZoneCmd.class); + cmdList.add(ListIpv4SubnetsForZoneCmd.class); + cmdList.add(UpdateIpv4SubnetForZoneCmd.class); + cmdList.add(DedicateIpv4SubnetForZoneCmd.class); + cmdList.add(ReleaseDedicatedIpv4SubnetForZoneCmd.class); + cmdList.add(CreateIpv4SubnetForGuestNetworkCmd.class); + cmdList.add(ListIpv4SubnetsForGuestNetworkCmd.class); + cmdList.add(DeleteIpv4SubnetForGuestNetworkCmd.class); + cmdList.add(CreateRoutingFirewallRuleCmd.class); + cmdList.add(ListRoutingFirewallRulesCmd.class); + cmdList.add(UpdateRoutingFirewallRuleCmd.class); + cmdList.add(DeleteRoutingFirewallRuleCmd.class); + cmdList.add(CreateBgpPeerCmd.class); + cmdList.add(DeleteBgpPeerCmd.class); + cmdList.add(ListBgpPeersCmd.class); + cmdList.add(UpdateBgpPeerCmd.class); + cmdList.add(DedicateBgpPeerCmd.class); + cmdList.add(ReleaseDedicatedBgpPeerCmd.class); + cmdList.add(ChangeBgpPeersForNetworkCmd.class); + cmdList.add(ChangeBgpPeersForVpcCmd.class); + return cmdList; + } + + + @Override + @ActionEvent(eventType = EventTypes.EVENT_ZONE_IP4_SUBNET_CREATE, + eventDescription = "Creating IPv4 subnet for a zone", + async = true, create = true) + public DataCenterIpv4GuestSubnet createDataCenterIpv4GuestSubnet(CreateIpv4SubnetForZoneCmd cmd) { + Long zoneId = cmd.getZoneId(); + String subnet = cmd.getSubnet(); + if (!NetUtils.isValidIp4Cidr(subnet)) { + throw new InvalidParameterValueException("Invalid IPv4 subnet: " + subnet); + } + + // check conflicts + List existingSubnets = dataCenterIpv4GuestSubnetDao.listByDataCenterId(zoneId); + checkConflicts(existingSubnets, subnet, null); + + Long domainId = cmd.getDomainId(); + final Long projectId = cmd.getProjectId(); + final String accountName = cmd.getAccountName(); + + Long accountId = null; + if (accountName != null || (projectId != null && projectId != -1L)) { + accountId = accountManager.finalyzeAccountId(accountName, domainId, projectId, false); + } + if (accountId != null) { + Account account = accountManager.getAccount(accountId); + domainId = account.getDomainId(); + } + + DataCenterIpv4GuestSubnetVO subnetVO = new DataCenterIpv4GuestSubnetVO(zoneId, NetUtils.transformCidr(subnet)); + if (domainId != null) { + subnetVO.setDomainId(domainId); + } + if (accountId != null) { + subnetVO.setAccountId(accountId); + } + subnetVO = dataCenterIpv4GuestSubnetDao.persist(subnetVO); + return subnetVO; + } + + @Override + public DataCenterIpv4SubnetResponse createDataCenterIpv4SubnetResponse(DataCenterIpv4GuestSubnet subnet) { + DataCenterIpv4SubnetResponse response = new DataCenterIpv4SubnetResponse(); + response.setCreated(subnet.getCreated()); + response.setSubnet(subnet.getSubnet()); + response.setId(subnet.getUuid()); + + DataCenter zone = ApiDBUtils.findZoneById(subnet.getDataCenterId()); + if (zone != null) { + response.setZoneId(zone.getUuid()); + response.setZoneName(zone.getName()); + } + + if (subnet.getDomainId() != null) { + Domain domain = ApiDBUtils.findDomainById(subnet.getDomainId()); + if (domain != null) { + response.setDomainId(domain.getUuid()); + response.setDomainName(domain.getName()); + } + } + + if (subnet.getAccountId() != null) { + Account account = ApiDBUtils.findAccountById(subnet.getAccountId()); + if (account != null) { + if (account.getType() == Account.Type.PROJECT) { + // find the project + Project project = ApiDBUtils.findProjectByProjectAccountId(account.getId()); + response.setProjectId(project.getUuid()); + response.setProjectName(project.getName()); + } else { + response.setAccountName(account.getAccountName()); + } + } + } + + response.setObjectName("zoneipv4subnet"); + return response; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_ZONE_IP4_SUBNET_DELETE, + eventDescription = "Deleting IPv4 subnet for a zone", + async = true) + public boolean deleteDataCenterIpv4GuestSubnet(DeleteIpv4SubnetForZoneCmd cmd) { + // check if subnet is in use + Long subnetId = cmd.getId(); + List usedNetworks = ipv4GuestSubnetNetworkMapDao.listUsedByParent(subnetId); + if (CollectionUtils.isNotEmpty(usedNetworks)) { + throw new InvalidParameterValueException(String.format("The subnet is being used by %s guest networks.", usedNetworks.size())); + } + + // remove via dataCenterIpv4GuestSubnetDao and ipv4GuestSubnetNetworkMapDao + ipv4GuestSubnetNetworkMapDao.deleteByParentId(subnetId); + dataCenterIpv4GuestSubnetDao.remove(subnetId); + return true; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_ZONE_IP4_SUBNET_UPDATE, + eventDescription = "Updating IPv4 subnet for a zone", + async = true) + public DataCenterIpv4GuestSubnet updateDataCenterIpv4GuestSubnet(UpdateIpv4SubnetForZoneCmd cmd) { + Long subnetId = cmd.getId(); + String newSubnet = cmd.getSubnet(); + DataCenterIpv4GuestSubnetVO subnetVO = dataCenterIpv4GuestSubnetDao.findById(subnetId); + if (subnetVO == null) { + throw new InvalidParameterValueException(String.format("Invalid subnet ID: %s", subnetId)); + } + + if (!NetUtils.isValidIp4Cidr(newSubnet)) { + throw new InvalidParameterValueException(String.format("Invalid IPv4 cidr: %s", newSubnet)); + } + + // check conflicts + List existingSubnets = dataCenterIpv4GuestSubnetDao.listByDataCenterId(subnetVO.getDataCenterId()); + checkConflicts(existingSubnets, newSubnet, subnetId); + + // check if subnet can be updated + List usedSubnets = ipv4GuestSubnetNetworkMapDao.listByParent(subnetId); + for (Ipv4GuestSubnetNetworkMap used : usedSubnets) { + if (!NetUtils.isNetworkAWithinNetworkB(used.getSubnet(), newSubnet)) { + throw new InvalidParameterValueException(String.format("Used subnet for guest network %s is not within new cidr: %s", used.getSubnet(), newSubnet)); + } + } + + // update via dataCenterIpv4GuestSubnetDao + DataCenterIpv4GuestSubnetVO subnet = dataCenterIpv4GuestSubnetDao.findById(subnetId); + subnet.setSubnet(NetUtils.transformCidr(newSubnet)); + dataCenterIpv4GuestSubnetDao.update(subnetId, subnet); + + return dataCenterIpv4GuestSubnetDao.findById(subnetId); + } + + private void checkConflicts(List existingSubnets, String newSubnet, Long ignoreSubnetId) { + for (DataCenterIpv4GuestSubnetVO existing : existingSubnets) { + if ((ignoreSubnetId == null || existing.getId() != ignoreSubnetId) && NetUtils.isNetworksOverlap(existing.getSubnet(), newSubnet)) { + throw new InvalidParameterValueException(String.format("Existing zone subnet %s has overlap with: %s", existing.getSubnet(), newSubnet)); + } + } + } + + @Override + public List listDataCenterIpv4GuestSubnets(ListIpv4SubnetsForZoneCmd cmd) { + Long id = cmd.getId(); + Long zoneId = cmd.getZoneId(); + String subnet = cmd.getSubnet(); + Long domainId = cmd.getDomainId(); + Long projectId = cmd.getProjectId(); + String accountName = cmd.getAccountName(); + + SearchCriteria sc = dataCenterIpv4GuestSubnetDao.createSearchCriteria(); + if (id != null) { + sc.addAnd("id", SearchCriteria.Op.EQ, id); + } + if (zoneId != null) { + sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId); + } + if (subnet != null) { + sc.addAnd("subnet", SearchCriteria.Op.EQ, subnet); + } + if (domainId != null) { + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + } + if (accountName != null || (projectId != null && projectId != -1L)) { + Long accountId= accountManager.finalyzeAccountId(accountName, domainId, projectId, false); + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + } + // search via dataCenterIpv4GuestSubnetDao + return dataCenterIpv4GuestSubnetDao.search(sc, null); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_ZONE_IP4_SUBNET_DEDICATE, + eventDescription = "Dedicating IPv4 subnet for a zone to a domain or an account", + async = true) + public DataCenterIpv4GuestSubnet dedicateDataCenterIpv4GuestSubnet(DedicateIpv4SubnetForZoneCmd cmd) { + final Long id = cmd.getId(); + Long domainId = cmd.getDomainId(); + final Long projectId = cmd.getProjectId(); + final String accountName = cmd.getAccountName(); + + DataCenterIpv4GuestSubnetVO subnetVO = dataCenterIpv4GuestSubnetDao.findById(id); + if (subnetVO == null) { + throw new InvalidParameterValueException(String.format("Cannot find subnet with id: ", id)); + } + Long accountId = null; + if (accountName != null || (projectId != null && projectId != -1L)) { + accountId = accountManager.finalyzeAccountId(accountName, domainId, projectId, false); + } + if (accountId != null) { + Account account = accountManager.getAccount(accountId); + domainId = account.getDomainId(); + } + + // Check if the guest subnet is used by other domain or account + if (domainId != null) { + List createdSubnets = ipv4GuestSubnetNetworkMapDao.listUsedByOtherDomains(id, domainId); + if (CollectionUtils.isNotEmpty(createdSubnets)) { + throw new InvalidParameterValueException(String.format("The subnet is being used by %s guest networks of other domains.", createdSubnets.size())); + } + } + if (accountId != null) { + List createdSubnets = ipv4GuestSubnetNetworkMapDao.listUsedByOtherAccounts(id, accountId); + if (CollectionUtils.isNotEmpty(createdSubnets)) { + throw new InvalidParameterValueException(String.format("The subnet is being used by %s guest networks of other accounts.", createdSubnets.size())); + } + } + + // update domain_id or account_id via dataCenterIpv4GuestSubnetDao to Mark the subnet as dedicated + subnetVO.setDomainId(domainId); + subnetVO.setAccountId(accountId); + dataCenterIpv4GuestSubnetDao.update(id, subnetVO); + return dataCenterIpv4GuestSubnetDao.findById(id); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_ZONE_IP4_SUBNET_RELEASE, + eventDescription = "Releasing dedicated IPv4 subnet for a zone from a domain or an account", + async = true) + public DataCenterIpv4GuestSubnet releaseDedicatedDataCenterIpv4GuestSubnet(ReleaseDedicatedIpv4SubnetForZoneCmd cmd) { + final Long id = cmd.getId(); + DataCenterIpv4GuestSubnetVO subnetVO = dataCenterIpv4GuestSubnetDao.findById(id); + if (subnetVO == null) { + throw new InvalidParameterValueException(String.format("Cannot find subnet with id: ", id)); + } + + // update domain_id and account_id to null via dataCenterIpv4GuestSubnetDao, to release the dedication + subnetVO.setDomainId(null); + subnetVO.setAccountId(null); + dataCenterIpv4GuestSubnetDao.update(id, subnetVO); + return dataCenterIpv4GuestSubnetDao.findById(id); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_IP4_GUEST_SUBNET_CREATE, + eventDescription = "Creating IPv4 subnet for guest network", + async = true, create = true) + public Ipv4GuestSubnetNetworkMap createIpv4SubnetForGuestNetwork(CreateIpv4SubnetForGuestNetworkCmd cmd) { + if (ObjectUtils.allNull(cmd.getSubnet(), cmd.getCidrSize())) { + throw new InvalidParameterValueException("One of subnet and cidrsize must be specified"); + } + if (ObjectUtils.allNotNull(cmd.getSubnet(), cmd.getCidrSize())) { + throw new InvalidParameterValueException("subnet and cidrsize are mutually exclusive"); + } + DataCenterIpv4GuestSubnet parent = dataCenterIpv4GuestSubnetDao.findById(cmd.getParentId()); + if (parent == null) { + throw new InvalidParameterValueException("the parent subnet is invalid"); + } + if (cmd.getSubnet() != null) { + return createIpv4SubnetFromParentSubnet(parent, cmd.getSubnet()); + } else if (cmd.getCidrSize() != null) { + return createIpv4SubnetFromParentSubnet(parent, cmd.getCidrSize()); + } + return null; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_IP4_GUEST_SUBNET_DELETE, + eventDescription = "Deleting IPv4 subnet for guest network", + async = true) + public boolean deleteIpv4SubnetForGuestNetwork(DeleteIpv4SubnetForGuestNetworkCmd cmd) { + Long mapId = cmd.getId(); + Ipv4GuestSubnetNetworkMapVO mapVO = ipv4GuestSubnetNetworkMapDao.findById(mapId); + if (mapVO == null) { + return true; + } + // check if the subnet is not in use + if (!State.Free.equals(mapVO.getState()) || mapVO.getNetworkId() != null) { + throw new InvalidParameterValueException("Cannot delete the subnet which is in use"); + } + return ipv4GuestSubnetNetworkMapDao.remove(mapId); + } + + @Override + public void releaseIpv4SubnetForGuestNetwork(long networkId) { + // check if the network has corresponding subnet + Ipv4GuestSubnetNetworkMapVO mapVO = ipv4GuestSubnetNetworkMapDao.findByNetworkId(networkId); + if (mapVO == null) { + return; + } + releaseIpv4SubnetForGuestNetworkOrVpcInternal(mapVO); + } + + @Override + public void releaseIpv4SubnetForVpc(long vpcId) { + // check if the network has corresponding subnet + Ipv4GuestSubnetNetworkMapVO mapVO = ipv4GuestSubnetNetworkMapDao.findByVpcId(vpcId); + if (mapVO == null) { + return; + } + releaseIpv4SubnetForGuestNetworkOrVpcInternal(mapVO); + } + + private void releaseIpv4SubnetForGuestNetworkOrVpcInternal(Ipv4GuestSubnetNetworkMapVO mapVO) { + ipv4GuestSubnetNetworkMapDao.remove(mapVO.getId()); + } + + @Override + public List listIpv4GuestSubnetsForGuestNetwork(ListIpv4SubnetsForGuestNetworkCmd cmd) { + Long id = cmd.getId(); + Long zoneId = cmd.getZoneId(); + Long parentId = cmd.getParentId(); + String subnet = cmd.getSubnet(); + String keyword = cmd.getKeyword(); + Long networkId = cmd.getNetworkId(); + Long vpcId = cmd.getVpcId(); + + SearchCriteria sc = ipv4GuestSubnetNetworkMapDao.createSearchCriteria(); + if (id != null) { + sc.addAnd("id", SearchCriteria.Op.EQ, id); + } + if (zoneId != null) { + List subnets = dataCenterIpv4GuestSubnetDao.listByDataCenterId(zoneId); + if (CollectionUtils.isEmpty(subnets)) { + return new ArrayList<>(); + } + List parentIds = subnets.stream().map(DataCenterIpv4GuestSubnetVO::getId).collect(Collectors.toList()); + sc.addAnd("parentId", SearchCriteria.Op.IN, parentIds.toArray()); + } + if (parentId != null) { + sc.addAnd("parentId", SearchCriteria.Op.EQ, parentId); + } + if (subnet != null) { + sc.addAnd("subnet", SearchCriteria.Op.EQ, subnet); + } + if (networkId != null) { + sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId); + } + if (vpcId != null) { + sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId); + } + if (keyword != null) { + sc.addAnd("subnet", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + } + + return ipv4GuestSubnetNetworkMapDao.search(sc, null); + } + + @Override + public Ipv4SubnetForGuestNetworkResponse createIpv4SubnetForGuestNetworkResponse(Ipv4GuestSubnetNetworkMap subnet) { + Ipv4SubnetForGuestNetworkResponse response = new Ipv4SubnetForGuestNetworkResponse(); + + response.setCreated(subnet.getCreated()); + response.setSubnet(subnet.getSubnet()); + response.setState(subnet.getState().name()); + response.setId(subnet.getUuid()); + response.setAllocatedTime(subnet.getAllocated()); + Long zoneId = null; + if (subnet.getNetworkId() != null) { + Network network = ApiDBUtils.findNetworkById(subnet.getNetworkId()); + response.setNetworkId(network.getUuid()); + response.setNetworkName(network.getName()); + zoneId = network.getDataCenterId(); + } + if (subnet.getVpcId() != null) { + Vpc vpc = ApiDBUtils.findVpcById(subnet.getVpcId()); + response.setVpcId(vpc.getUuid()); + response.setVpcName(vpc.getName()); + zoneId = vpc.getZoneId(); + } + if (subnet.getParentId() != null) { + DataCenterIpv4GuestSubnet parent = dataCenterIpv4GuestSubnetDao.findById(subnet.getParentId()); + if (parent != null) { + response.setParentId(parent.getUuid()); + response.setParentSubnet(parent.getSubnet()); + zoneId = parent.getDataCenterId(); + } + } else if (subnet.getNetworkId() != null) { + Network network = ApiDBUtils.findNetworkById(subnet.getNetworkId()); + if (network != null) { + zoneId = network.getDataCenterId(); + } + } + if (zoneId != null) { + DataCenter zone = ApiDBUtils.findZoneById(zoneId); + if (zone != null) { + response.setZoneId(zone.getUuid()); + response.setZoneName(zone.getName()); + } + } + response.setObjectName("ipv4subnetforguestnetwork"); + return response; + } + + @Override + public void getOrCreateIpv4SubnetForGuestNetwork(Network network, String networkCidr) { + getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(networkCidr, network.getDomainId(), network.getAccountId(), network.getDataCenterId()); + } + + @Override + public Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetwork(Long domainId, Long accountId, Long zoneId, Integer networkCidrSize) { + return getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(networkCidrSize, domainId, accountId, zoneId); + } + + @Override + public void getOrCreateIpv4SubnetForVpc(Vpc vpc, String networkCidr) { + getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(networkCidr, vpc.getDomainId(), vpc.getAccountId(), vpc.getZoneId()); + } + + @Override + public Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForVpc(Vpc vpc, Integer vpcCidrSize) { + return getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(vpcCidrSize, vpc.getDomainId(), vpc.getAccountId(), vpc.getZoneId()); + } + + private Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, Long ownerDomainId, Long ownerAccountId, Long zoneId) { + validateNetworkCidrSize(ownerAccountId, cidrSize); + List subnets = getZoneSubnetsForAccount(ownerDomainId, ownerAccountId, zoneId); + for (DataCenterIpv4GuestSubnetVO subnet : subnets) { + Ipv4GuestSubnetNetworkMap result = getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnet); + if (result != null) { + return result; + } + } + return null; + } + + private Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, DataCenterIpv4GuestSubnetVO subnet) { + Ipv4GuestSubnetNetworkMap map = ipv4GuestSubnetNetworkMapDao.findFirstAvailable(subnet.getId(), cidrSize); + if (map != null) { + return map; + } + try { + return createIpv4SubnetFromParentSubnet(subnet, cidrSize); + } catch (Exception ex) { + logger.debug("Failed to create Ipv4 subnet from parent subnet {}: {}", subnet.getSubnet(), ex.getMessage()); + } + return null; + } + + private void getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(String networkCidr, Long ownerDomainId, Long ownerAccountId, Long zoneId) { + Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(networkCidr); + if (subnetMap != null) { + // check if the subnet is in use + if (subnetMap.getNetworkId() != null || subnetMap.getVpcId() != null) { + throw new InvalidParameterValueException("The subnet is in use"); + } + // check if the subnet accessible by the owner + if (subnetMap.getParentId() != null) { + DataCenterIpv4GuestSubnetVO parent = dataCenterIpv4GuestSubnetDao.findById(subnetMap.getParentId()); + checkIfNetworkOwnerCanAccessIpv4Subnet(parent, ownerDomainId, ownerAccountId); + } + return; + } + + DataCenterIpv4GuestSubnet parent = getParentOfNetworkCidr(zoneId, networkCidr); + if (parent != null) { + // check if the parent subnet is accessible by the owner + checkIfNetworkOwnerCanAccessIpv4Subnet(parent, ownerDomainId, ownerAccountId); + } + + // Create new record without networkId + final Long parentId = parent != null ? parent.getId() : null; + subnetMap = new Ipv4GuestSubnetNetworkMapVO(parentId, NetUtils.transformCidr(networkCidr), null, State.Free); + ipv4GuestSubnetNetworkMapDao.persist(subnetMap); + } + + private void checkIfNetworkOwnerCanAccessIpv4Subnet(DataCenterIpv4GuestSubnet parent, Long ownerDomainId, Long ownerAccountId) { + if (parent != null + && ((parent.getDomainId() != null && !parent.getDomainId().equals(ownerDomainId)) + || (parent.getAccountId() != null && !parent.getAccountId().equals(ownerAccountId)))) { + throw new InvalidParameterValueException("The owner of the network has no permission to access the subnet"); + } + } + + private DataCenterIpv4GuestSubnet getParentOfNetworkCidr(Long zoneId, String networkCidr) { + List existingSubnets = dataCenterIpv4GuestSubnetDao.listByDataCenterId(zoneId); + for (DataCenterIpv4GuestSubnetVO existing : existingSubnets) { + if (NetUtils.isNetworkAWithinNetworkB(networkCidr, existing.getSubnet())) { + // check conflicts + List subnetsForNetwork = ipv4GuestSubnetNetworkMapDao.listByParent(existing.getId()); + checkConflicts(subnetsForNetwork, networkCidr); + return existing; + } + if (NetUtils.isNetworksOverlap(existing.getSubnet(), networkCidr)) { + throw new InvalidParameterValueException(String.format("Existing zone subnet %s has overlap with: %s", existing.getSubnet(), networkCidr)); + } + } + // check conflicts + List subnetsForNetworkNoParents = ipv4GuestSubnetNetworkMapDao.listAllNoParent(); + checkConflicts(subnetsForNetworkNoParents, networkCidr); + return null; + } + + private void checkConflicts(List subnetsForNetwork, String networkCidr) { + for (Ipv4GuestSubnetNetworkMapVO subnetForNetwork : subnetsForNetwork) { + if (NetUtils.isNetworksOverlap(subnetForNetwork.getSubnet(), networkCidr)) { + throw new InvalidParameterValueException(String.format("Existing subnet %s has overlap with: %s", subnetForNetwork.getSubnet(), networkCidr)); + } + } + } + + private void validateNetworkCidrSize(long accountId, Integer networkCidrSize) { + if (networkCidrSize == null) { + throw new CloudRuntimeException("network/vpc CidrSize is null"); + } + Boolean isAutoAllocationEnabled = RoutedIPv4NetworkCidrAutoAllocationEnabled.valueIn(accountId); + if (!Boolean.TRUE.equals(isAutoAllocationEnabled)) { + throw new CloudRuntimeException("CIDR auto-allocation is disabled for this account"); + } + } + + private List getZoneSubnetsForAccount(long domainId, long accountId, long zoneId) { + // Get dedicated guest subnets for the account + List subnets = dataCenterIpv4GuestSubnetDao.listByDataCenterIdAndAccountId(zoneId, accountId); + subnets.addAll(dataCenterIpv4GuestSubnetDao.listByDataCenterIdAndDomainId(zoneId, domainId)); + // Get non-dedicated zone guest subnets for the account + subnets.addAll(dataCenterIpv4GuestSubnetDao.listNonDedicatedByDataCenterId(zoneId)); + return subnets; + } + + private Ipv4GuestSubnetNetworkMap createIpv4SubnetFromParentSubnet(DataCenterIpv4GuestSubnet parent, Integer networkCidrSize) { + DataCenterIpv4GuestSubnetVO subnetVO = dataCenterIpv4GuestSubnetDao.findById(parent.getId()); + if (subnetVO == null) { + throw new InvalidParameterValueException(String.format("Invalid subnet ID: %s", parent.getId())); + } + // Order subnets by start IP + List existingSubnets = ipv4GuestSubnetNetworkMapDao.listByParent(parent.getId()); + Collections.sort(existingSubnets, (subnet1, subnet2) -> { + Long ip1 = NetUtils.ip2Long(subnet1.getSubnet().split("/")[0]); + Long ip2 = NetUtils.ip2Long(subnet2.getSubnet().split("/")[0]); + return ip1.compareTo(ip2); + }); + // get all free IP ranges + final List> freeIpranges = new ArrayList<>(); + final long[] parentSubnetIpRange = NetUtils.getIpRangeStartIpAndEndIpFromCidr(parent.getSubnet()); + long startIp = parentSubnetIpRange[0]; + for (Ipv4GuestSubnetNetworkMapVO subnet : existingSubnets) { + long[] subnetIpRange = NetUtils.getIpRangeStartIpAndEndIpFromCidr(subnet.getSubnet()); + if (startIp < subnetIpRange[0]) { + freeIpranges.add(new Pair<>(startIp, subnetIpRange[0] -1)); + } + startIp = subnetIpRange[1] + 1; + } + if (startIp <= parentSubnetIpRange[1]) { + freeIpranges.add(new Pair<>(startIp, parentSubnetIpRange[1])); + } + // split the IP ranges into list of subnet + final List> subnetsInFreeIpRanges = new ArrayList<>(); + for (Pair freeIpRange : freeIpranges) { + subnetsInFreeIpRanges.addAll(NetUtils.splitIpRangeIntoSubnets(freeIpRange.first(), freeIpRange.second())); + } + + // Allocate a subnet automatically + String networkCidr = getFreeNetworkCidr(subnetsInFreeIpRanges, networkCidrSize); + if (networkCidr == null) { + throw new CloudRuntimeException("Failed to automatically allocate a subnet with specified cidrsize"); + } + // create DB record + Ipv4GuestSubnetNetworkMapVO subnetMap = new Ipv4GuestSubnetNetworkMapVO(parent.getId(), NetUtils.transformCidr(networkCidr), null, State.Free); + return ipv4GuestSubnetNetworkMapDao.persist(subnetMap); + } + + private String getFreeNetworkCidr(List> subnetsInFreeIpRanges, int networkCidrSize) { + for (int cidrSize = networkCidrSize; cidrSize >= 1; cidrSize--) { + for (Pair freeSubnet : subnetsInFreeIpRanges) { + if (freeSubnet.second().equals(cidrSize)) { + String networkCidr = String.format("%s/%s", NetUtils.long2Ip(freeSubnet.first()), networkCidrSize); + if (ipv4GuestSubnetNetworkMapDao.findBySubnet(networkCidr) == null) { + return networkCidr; + } + } + } + } + return null; + } + + private Ipv4GuestSubnetNetworkMap createIpv4SubnetFromParentSubnet(DataCenterIpv4GuestSubnet parent, String networkCidr) { + // Validate the network cidr + if (!NetUtils.isNetworkAWithinNetworkB(networkCidr, parent.getSubnet())) { + throw new InvalidParameterValueException(String.format("networkCidr %s is not within parent cidr: %s", networkCidr, parent.getSubnet())); + } + // check conflicts + List existingSubnets = ipv4GuestSubnetNetworkMapDao.listByParent(parent.getId()); + checkConflicts(existingSubnets, networkCidr); + + // create DB record + Ipv4GuestSubnetNetworkMapVO subnetMap = new Ipv4GuestSubnetNetworkMapVO(parent.getId(), NetUtils.transformCidr(networkCidr), null, State.Free); + return ipv4GuestSubnetNetworkMapDao.persist(subnetMap); + } + + @Override + public void assignIpv4SubnetToNetwork(Network network) { + if (network == null || network.getCidr() == null) { + return; + } + Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(network.getCidr()); + if (subnetMap != null) { + if (network.getId() > 0L) { + subnetMap.setNetworkId(network.getId()); + } + subnetMap.setState(State.Allocated); + subnetMap.setAllocated(new Date()); + ipv4GuestSubnetNetworkMapDao.update(subnetMap.getId(), subnetMap); + } + } + + @Override + public void assignIpv4SubnetToVpc(Vpc vpc) { + if (vpc == null || vpc.getCidr() == null) { + return; + } + Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(vpc.getCidr()); + if (subnetMap != null) { + if (vpc != null && vpc.getId() > 0L) { + subnetMap.setVpcId(vpc.getId()); + } + subnetMap.setState(State.Allocated); + subnetMap.setAllocated(new Date()); + ipv4GuestSubnetNetworkMapDao.update(subnetMap.getId(), subnetMap); + } + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE, + eventDescription = "Creating routing firewall rule", async = true) + public FirewallRule createRoutingFirewallRule(CreateRoutingFirewallRuleCmd createRoutingFirewallRuleCmd) throws NetworkRuleConflictException { + final Account caller = CallContext.current().getCallingAccount(); + final long networkId = createRoutingFirewallRuleCmd.getNetworkId(); + final Integer portStart = createRoutingFirewallRuleCmd.getSourcePortStart(); + final Integer portEnd = createRoutingFirewallRuleCmd.getSourcePortEnd(); + final FirewallRule.TrafficType trafficType = createRoutingFirewallRuleCmd.getTrafficType(); + final String protocol = createRoutingFirewallRuleCmd.getProtocol(); + final Integer icmpCode = createRoutingFirewallRuleCmd.getIcmpCode(); + final Integer icmpType = createRoutingFirewallRuleCmd.getIcmpType(); + final boolean forDisplay = createRoutingFirewallRuleCmd.isDisplay(); + final FirewallRule.FirewallRuleType type = FirewallRule.FirewallRuleType.User; + final List sourceCidrList = createRoutingFirewallRuleCmd.getSourceCidrList(); + final List destinationCidrList = createRoutingFirewallRuleCmd.getDestinationCidrList(); + + for (String cidr : sourceCidrList) { + if (!NetUtils.isValidIp4Cidr(cidr)) { + throw new InvalidParameterValueException(String.format("Invalid source IPv4 CIDR: %s", cidr)); + } + } + for (String cidr : destinationCidrList) { + if (!NetUtils.isValidIp4Cidr(cidr)) { + throw new InvalidParameterValueException(String.format("Invalid destination IPv4 CIDR: %s", cidr)); + } + } + if (portStart != null && !NetUtils.isValidPort(portStart)) { + throw new InvalidParameterValueException("publicPort is an invalid value: " + portStart); + } + if (portEnd != null && !NetUtils.isValidPort(portEnd)) { + throw new InvalidParameterValueException("Public port range is an invalid value: " + portEnd); + } + if (ObjectUtils.allNotNull(portStart, portEnd) && portStart > portEnd) { + throw new InvalidParameterValueException("Start port can't be bigger than end port"); + } + + Network network = networkModel.getNetwork(networkId); + assert network != null : "Can't create rule as network is null?"; + + final long accountId = network.getAccountId(); + final long domainId = network.getDomainId(); + + accountManager.checkAccess(caller, null, true, network); + + // Verify that the network guru supports the protocol specified + Map caps = networkModel.getNetworkServiceCapabilities(network.getId(), Network.Service.Firewall); + + if (caps != null) { + String supportedProtocols; + String supportedTrafficTypes = null; + supportedTrafficTypes = caps.get(Network.Capability.SupportedTrafficDirection).toLowerCase(); + + if (trafficType == FirewallRule.TrafficType.Egress) { + supportedProtocols = caps.get(Network.Capability.SupportedEgressProtocols).toLowerCase(); + } else { + supportedProtocols = caps.get(Network.Capability.SupportedProtocols).toLowerCase(); + } + + if (!supportedProtocols.contains(protocol.toLowerCase())) { + throw new InvalidParameterValueException(String.format("Protocol %s is not supported in zone", protocol)); + } else if (!supportedTrafficTypes.contains(trafficType.toString().toLowerCase())) { + throw new InvalidParameterValueException("Traffic Type " + trafficType + " is currently supported by Firewall in network " + networkId); + } + } + + // icmp code and icmp type can't be passed in for any other protocol rather than icmp + if (!protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (icmpCode != null || icmpType != null)) { + throw new InvalidParameterValueException("Can specify icmpCode and icmpType for ICMP protocol only"); + } + + if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (portStart != null || portEnd != null)) { + throw new InvalidParameterValueException("Can't specify start/end port when protocol is ICMP"); + } + + return Transaction.execute(new TransactionCallbackWithException() { + @Override + public FirewallRuleVO doInTransaction(TransactionStatus status) throws NetworkRuleConflictException { + FirewallRuleVO newRule = + new FirewallRuleVO(null, null, portStart, portEnd, protocol.toLowerCase(), networkId, accountId, domainId, FirewallRule.Purpose.Firewall, + sourceCidrList, destinationCidrList, icmpCode, icmpType, null, trafficType); + newRule.setType(type); + newRule.setDisplay(forDisplay); + newRule = firewallDao.persist(newRule); + + if (FirewallRule.FirewallRuleType.User.equals(type)) { + firewallManager.detectRulesConflict(newRule); + } + + if (!firewallDao.setStateToAdd(newRule)) { + throw new CloudRuntimeException("Unable to update the state to add for " + newRule); + } + CallContext.current().setEventDetails("Rule Id: " + newRule.getId()); + + return newRule; + } + }); + } + + @Override + public Pair, Integer> listRoutingFirewallRules(ListRoutingFirewallRulesCmd listRoutingFirewallRulesCmd) { + return firewallService.listFirewallRules(listRoutingFirewallRulesCmd); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_UPDATE, + eventDescription = "Updating routing firewall rule", async = true) + public FirewallRule updateRoutingFirewallRule(UpdateRoutingFirewallRuleCmd updateRoutingFirewallRuleCmd) { + final long id = updateRoutingFirewallRuleCmd.getId(); + final boolean forDisplay = updateRoutingFirewallRuleCmd.isDisplay(); + FirewallRuleVO rule = firewallDao.findById(id); + if (rule == null) { + throw new InvalidParameterValueException(String.format("Unable to find routing firewall rule with id %d", id)); + } + if (FirewallRule.TrafficType.Ingress.equals(rule.getTrafficType())) { + return firewallManager.updateIngressFirewallRule(rule.getId(), null, forDisplay); + } + return firewallManager.updateEgressFirewallRule(rule.getId(), null, forDisplay); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE, + eventDescription = "revoking routing firewall rule", async = true) + public boolean revokeRoutingFirewallRule(Long id) { + FirewallRuleVO rule = firewallDao.findById(id); + if (rule == null) { + throw new InvalidParameterValueException(String.format("Unable to find routing firewall rule with id %d", id)); + } + if (FirewallRule.TrafficType.Ingress.equals(rule.getTrafficType())) { + return firewallManager.revokeIngressFirewallRule(rule.getId(), true); + } + return firewallManager.revokeEgressFirewallRule(rule.getId(), true); + } + + @Override + public boolean applyRoutingFirewallRule(long id) { + FirewallRuleVO rule = firewallDao.findById(id); + if (rule == null) { + logger.error(String.format("Unable to find routing firewall rule with ID: %d", id)); + return false; + } + if (!FirewallRule.Purpose.Firewall.equals(rule.getPurpose())) { + logger.error(String.format("Cannot apply routing firewall rule with ID: %d as purpose %s is not %s", id, rule.getPurpose(), FirewallRule.Purpose.Firewall)); + } + logger.debug(String.format("Applying routing firewall rules for rule with ID: %s", rule.getUuid())); + List rules = firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), rule.getPurpose(), FirewallRule.TrafficType.Egress); + rules.addAll(firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), rule.getPurpose(), FirewallRule.TrafficType.Ingress)); + return firewallManager.applyFirewallRules(rules, false, CallContext.current().getCallingAccount()); + } + + @Override + public boolean isVirtualRouterGateway(Network network) { + return isRoutedNetwork(network) + && (networkServiceMapDao.canProviderSupportServiceInNetwork(network.getId(), Service.Gateway, Provider.VirtualRouter)) + || networkServiceMapDao.canProviderSupportServiceInNetwork(network.getId(), Service.Gateway, Provider.VPCVirtualRouter); + } + + @Override + public boolean isVirtualRouterGateway(NetworkOffering networkOffering) { + return NetworkOffering.NetworkMode.ROUTED.equals(networkOffering.getNetworkMode()) + && networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(networkOffering.getId(), Service.Gateway, Provider.VirtualRouter) + || networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(networkOffering.getId(), Service.Gateway, Provider.VPCVirtualRouter); + } + + @Override + public boolean isRoutedNetwork(Network network) { + return NetworkOffering.NetworkMode.ROUTED.equals(networkOfferingDao.findById(network.getNetworkOfferingId()).getNetworkMode()); + } + + @Override + public boolean isDynamicRoutedNetwork(Network network) { + NetworkOffering networkOffering = networkOfferingDao.findById(network.getNetworkOfferingId()); + return isDynamicRoutedNetwork(networkOffering); + } + + @Override + public boolean isDynamicRoutedNetwork(NetworkOffering networkOffering) { + return NetworkOffering.NetworkMode.ROUTED.equals(networkOffering.getNetworkMode()) + && NetworkOffering.RoutingMode.Dynamic.equals(networkOffering.getRoutingMode()); + } + + @Override + public boolean isRoutedVpc(Vpc vpc) { + return NetworkOffering.NetworkMode.ROUTED.equals(vpcOfferingDao.findById(vpc.getVpcOfferingId()).getNetworkMode()); + } + + @Override + public boolean isDynamicRoutedVpc(Vpc vpc) { + VpcOffering vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId()); + return isDynamicRoutedVpc(vpcOffering); + } + + @Override + public boolean isDynamicRoutedVpc(VpcOffering vpcOffering) { + return NetworkOffering.NetworkMode.ROUTED.equals(vpcOffering.getNetworkMode()) + && NetworkOffering.RoutingMode.Dynamic.equals(vpcOffering.getRoutingMode()); + } + + @Override + public boolean isVpcVirtualRouterGateway(VpcOffering vpcOffering) { + return NetworkOffering.NetworkMode.ROUTED.equals(vpcOffering.getNetworkMode()) + && vpcOfferingServiceMapDao.findByServiceProviderAndOfferingId(Service.Gateway.getName(), Provider.VPCVirtualRouter.getName(), vpcOffering.getId()) != null; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_BGP_PEER_CREATE, + eventDescription = "Creating BGP Peer", + async = true, create = true) + public BgpPeer createBgpPeer(CreateBgpPeerCmd createBgpPeerCmd) { + Long zoneId = createBgpPeerCmd.getZoneId(); + Long asNumber = createBgpPeerCmd.getAsNumber(); + String ip4Address = createBgpPeerCmd.getIp4Address(); + String ip6Address = createBgpPeerCmd.getIp6Address(); + String password = createBgpPeerCmd.getPassword(); + Map detailsStr = createBgpPeerCmd.getDetails(); + + if (ObjectUtils.allNull(ip4Address, ip6Address)) { + throw new InvalidParameterValueException("At least one of IPv4 and IPv6 address must be specified."); + } + + if (ip4Address != null) { + if (!NetUtils.isValidIp4(ip4Address)) { + throw new InvalidParameterValueException("IPv4 address is not valid."); + } + if (bgpPeerDao.findByZoneAndAsNumberAndAddress(zoneId, asNumber, ip4Address, null) != null) { + throw new InvalidParameterValueException("There is already a BGP peer with same IPv4 address and AS number in the zone."); + } + } + + if (ip6Address != null) { + if (!NetUtils.isValidIp6(ip6Address)) { + throw new InvalidParameterValueException("IPv6 address is not valid."); + } + if (bgpPeerDao.findByZoneAndAsNumberAndAddress(zoneId, asNumber, null, ip6Address) != null) { + throw new InvalidParameterValueException("There is already a BGP peer with same IPv6 address and AS number in the zone."); + } + } + + final Map details = new HashMap<>(); + if (detailsStr != null) { + for (final String detailStr : detailsStr.keySet()) { + BgpPeer.Detail bgpPeerDetail = EnumUtils.getEnumIgnoreCase(BgpPeer.Detail.class, detailStr); + if (bgpPeerDetail == null) { + throw new InvalidParameterValueException("Unsupported BGP peer detail " + detailStr); + } + details.put(bgpPeerDetail, detailsStr.get(detailStr)); + } + } + + Long domainId = createBgpPeerCmd.getDomainId(); + final Long projectId = createBgpPeerCmd.getProjectId(); + final String accountName = createBgpPeerCmd.getAccountName(); + + Long accountId = null; + if (accountName != null || (projectId != null && projectId != -1L)) { + accountId = accountManager.finalyzeAccountId(accountName, domainId, projectId, false); + } + if (accountId != null) { + Account account = accountManager.getAccount(accountId); + domainId = account.getDomainId(); + } + + BgpPeerVO bgpPeerVO = new BgpPeerVO(zoneId, ip4Address, ip6Address, asNumber, password); + if (domainId != null) { + bgpPeerVO.setDomainId(domainId); + } + if (accountId != null) { + bgpPeerVO.setAccountId(accountId); + } + bgpPeerVO = bgpPeerDao.persist(bgpPeerVO, details); + return bgpPeerVO; + } + + @Override + public BgpPeerResponse createBgpPeerResponse(BgpPeer bgpPeer) { + BgpPeerResponse response = new BgpPeerResponse(); + response.setCreated(bgpPeer.getCreated()); + response.setAsNumber(bgpPeer.getAsNumber()); + response.setId(bgpPeer.getUuid()); + response.setIp4Address(bgpPeer.getIp4Address()); + response.setIp6Address(bgpPeer.getIp6Address()); + + DataCenter zone = ApiDBUtils.findZoneById(bgpPeer.getDataCenterId()); + if (zone != null) { + response.setZoneId(zone.getUuid()); + response.setZoneName(zone.getName()); + } + + if (bgpPeer.getDomainId() != null) { + Domain domain = ApiDBUtils.findDomainById(bgpPeer.getDomainId()); + if (domain != null) { + response.setDomainId(domain.getUuid()); + response.setDomainName(domain.getName()); + } + } + + if (bgpPeer.getAccountId() != null) { + Account account = ApiDBUtils.findAccountById(bgpPeer.getAccountId()); + if (account != null) { + if (account.getType() == Account.Type.PROJECT) { + // find the project + Project project = ApiDBUtils.findProjectByProjectAccountId(account.getId()); + response.setProjectId(project.getUuid()); + response.setProjectName(project.getName()); + } else { + response.setAccountName(account.getAccountName()); + } + } + } + + Map detailsMap = bgpPeerDetailsDao.getBgpPeerDetails(bgpPeer.getId()); + if (MapUtils.isNotEmpty(detailsMap)) { + response.setDetails(detailsMap); + } + + response.setObjectName("bgppeer"); + return response; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_BGP_PEER_DELETE, + eventDescription = "Deleting BGP Peer", + async = true) + public boolean deleteBgpPeer(DeleteBgpPeerCmd deleteBgpPeerCmd) { + // check if BGP peer is in use + Long bgpPeerId = deleteBgpPeerCmd.getId(); + List usedBgpPeers = bgpPeerNetworkMapDao.listByBgpPeerId(bgpPeerId); + if (CollectionUtils.isNotEmpty(usedBgpPeers)) { + throw new InvalidParameterValueException(String.format("The BGP peer is being used by %s guest networks.", usedBgpPeers.size())); + } + + bgpPeerDao.remove(bgpPeerId); + return true; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_BGP_PEER_UPDATE, + eventDescription = "Updating a BGP Peer", + async = true) + public BgpPeer updateBgpPeer(UpdateBgpPeerCmd updateBgpPeerCmd) { + Long bgpPeerId = updateBgpPeerCmd.getId(); + Long newAsNumber = updateBgpPeerCmd.getAsNumber(); + String newIp4Address = updateBgpPeerCmd.getIp4Address(); + String newIp6Address = updateBgpPeerCmd.getIp6Address(); + String password = updateBgpPeerCmd.getPassword(); + Map detailsStr = updateBgpPeerCmd.getDetails(); + + BgpPeerVO bgpPeerVO = bgpPeerDao.findById(bgpPeerId); + if (bgpPeerVO == null) { + throw new InvalidParameterValueException(String.format("Invalid BGP peer ID: %s", bgpPeerId)); + } + + Long zoneId = bgpPeerVO.getDataCenterId(); + + boolean isAsNumberChanged = (newAsNumber != null) && !newAsNumber.equals(bgpPeerVO.getAsNumber()); + boolean isIp4AddressChanged = StringUtils.isNotBlank(newIp4Address) && !newIp4Address.equals(bgpPeerVO.getIp4Address()); + boolean isIp6AddressChanged = StringUtils.isNotBlank(newIp6Address) && !newIp6Address.equals(bgpPeerVO.getIp6Address()); + + if (newAsNumber == null) { + newAsNumber = bgpPeerVO.getAsNumber(); + } + if (newIp4Address == null) { + newIp4Address = bgpPeerVO.getIp4Address(); + } else if (StringUtils.isBlank(newIp4Address)) { + newIp4Address = null; + } else if (!NetUtils.isValidIp4(newIp4Address)) { + throw new InvalidParameterValueException("new IPv4 address is not valid."); + } + + if (newIp6Address == null) { + newIp6Address = bgpPeerVO.getIp6Address(); + } else if (StringUtils.isBlank(newIp6Address)) { + newIp6Address = null; + } else if (!NetUtils.isValidIp6(newIp6Address)) { + throw new InvalidParameterValueException("new IPv6 address is not valid."); + } + + if (ObjectUtils.allNull(newIp4Address, newIp6Address)) { + throw new InvalidParameterValueException("At least one of IPv4 and IPv6 address must be specified."); + } + + if ((isAsNumberChanged || isIp4AddressChanged) && newIp4Address != null) { + if (bgpPeerDao.findByZoneAndAsNumberAndAddress(zoneId, newAsNumber, newIp4Address, null) != null) { + throw new InvalidParameterValueException("There is already a BGP peer with same IPv4 address and AS number in the zone."); + } + } + if ((isAsNumberChanged || isIp6AddressChanged) && newIp6Address != null) { + if (bgpPeerDao.findByZoneAndAsNumberAndAddress(zoneId, newAsNumber, null, newIp6Address) != null) { + throw new InvalidParameterValueException("There is already a BGP peer with same IPv6 address and AS number in the zone."); + } + } + + final Map details = new HashMap<>(); + if (detailsStr != null) { + for (final String detailStr : detailsStr.keySet()) { + BgpPeer.Detail bgpPeerDetail = EnumUtils.getEnumIgnoreCase(BgpPeer.Detail.class, detailStr); + if (bgpPeerDetail == null) { + throw new InvalidParameterValueException("Unsupported BGP peer detail " + detailStr); + } + details.put(bgpPeerDetail, detailsStr.get(detailStr)); + } + } + + // update via bgpPeerDao + bgpPeerVO.setAsNumber(newAsNumber); + bgpPeerVO.setIp4Address(newIp4Address); + bgpPeerVO.setIp6Address(newIp6Address); + if (password != null) { + bgpPeerVO.setPassword(password); + } + bgpPeerDao.update(bgpPeerId, bgpPeerVO); + + boolean cleanupDetails = updateBgpPeerCmd.isCleanupDetails(); + if (cleanupDetails){ + bgpPeerDetailsDao.removeByBgpPeerId(bgpPeerId); + } else if (MapUtils.isNotEmpty(details)) { + bgpPeerDetailsDao.removeByBgpPeerId(bgpPeerId); + List bgpPeerDetails = new ArrayList<>(); + for (BgpPeer.Detail key : details.keySet()) { + BgpPeerDetailsVO detail = new BgpPeerDetailsVO(bgpPeerVO.getId(), key, details.get(key), true); + bgpPeerDetails.add(detail); + } + bgpPeerDetailsDao.saveDetails(bgpPeerDetails); + } + + return bgpPeerDao.findById(bgpPeerId); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_BGP_PEER_DEDICATE, + eventDescription = "Dedicating BGP Peer to a domain or an account", + async = true) + public BgpPeer dedicateBgpPeer(DedicateBgpPeerCmd dedicateBgpPeerCmd) { + final Long id = dedicateBgpPeerCmd.getId(); + Long domainId = dedicateBgpPeerCmd.getDomainId(); + final Long projectId = dedicateBgpPeerCmd.getProjectId(); + final String accountName = dedicateBgpPeerCmd.getAccountName(); + + BgpPeerVO bgpPeerVO = bgpPeerDao.findById(id); + if (bgpPeerVO == null) { + throw new InvalidParameterValueException(String.format("Cannot find BGP peer with id: ", id)); + } + Long accountId = null; + if (accountName != null || (projectId != null && projectId != -1L)) { + accountId = accountManager.finalyzeAccountId(accountName, domainId, projectId, false); + } + if (accountId != null) { + Account account = accountManager.getAccount(accountId); + domainId = account.getDomainId(); + } + + // Check if the BGP peer is used by other domain or account + if (domainId != null) { + List usedBgpPeers = bgpPeerNetworkMapDao.listUsedNetworksByOtherDomains(id, domainId); + if (CollectionUtils.isNotEmpty(usedBgpPeers)) { + throw new InvalidParameterValueException(String.format("The subnet is being used by %s guest networks of other domains.", usedBgpPeers.size())); + } + usedBgpPeers = bgpPeerNetworkMapDao.listUsedVpcsByOtherDomains(id, domainId); + if (CollectionUtils.isNotEmpty(usedBgpPeers)) { + throw new InvalidParameterValueException(String.format("The subnet is being used by %s vpcs of other domains.", usedBgpPeers.size())); + } + } + if (accountId != null) { + List usedBgpPeers = bgpPeerNetworkMapDao.listUsedNetworksByOtherAccounts(id, accountId); + if (CollectionUtils.isNotEmpty(usedBgpPeers)) { + throw new InvalidParameterValueException(String.format("The subnet is being used by %s guest networks of other accounts.", usedBgpPeers.size())); + } + usedBgpPeers = bgpPeerNetworkMapDao.listUsedVpcsByOtherAccounts(id, accountId); + if (CollectionUtils.isNotEmpty(usedBgpPeers)) { + throw new InvalidParameterValueException(String.format("The subnet is being used by %s guest networks of other accounts.", usedBgpPeers.size())); + } + } + + // update domain_id or account_id via dataCenterIpv4GuestSubnetDao to Mark the subnet as dedicated + bgpPeerVO.setDomainId(domainId); + bgpPeerVO.setAccountId(accountId); + bgpPeerDao.update(id, bgpPeerVO); + return bgpPeerDao.findById(id); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_BGP_PEER_RELEASE, + eventDescription = "Releasing a dedicated BGP Peer from a domain or an account", + async = true) + public BgpPeer releaseDedicatedBgpPeer(ReleaseDedicatedBgpPeerCmd releaseDedicatedBgpPeerCmd) { + final Long id = releaseDedicatedBgpPeerCmd.getId(); + BgpPeerVO bgpPeerVO = bgpPeerDao.findById(id); + if (bgpPeerVO == null) { + throw new InvalidParameterValueException(String.format("Cannot find BGP peer with id: ", id)); + } + + // update domain_id and account_id to null via bgpPeerDao, to release the dedication + bgpPeerVO.setDomainId(null); + bgpPeerVO.setAccountId(null); + bgpPeerDao.update(id, bgpPeerVO); + return bgpPeerDao.findById(id); + } + + @Override + public List listBgpPeers(ListBgpPeersCmd listBgpPeersCmd) { + Long id = listBgpPeersCmd.getId(); + Long zoneId = listBgpPeersCmd.getZoneId(); + Long asNumber = listBgpPeersCmd.getAsNumber(); + Long domainId = listBgpPeersCmd.getDomainId(); + Long projectId = listBgpPeersCmd.getProjectId(); + String accountName = listBgpPeersCmd.getAccountName(); + Boolean isDedicated = listBgpPeersCmd.getDedicated(); + String keyword = listBgpPeersCmd.getKeyword(); + + Long accountId = null; + if (accountName != null || (projectId != null && projectId != -1L)) { + accountId = accountManager.finalyzeAccountId(accountName, domainId, projectId, false); + } + if (isDedicated != null) { + SearchCriteria sc1 = createSearchCriteriaForListBgpPeersCmd(id, zoneId, asNumber, keyword); + if (Boolean.TRUE.equals(isDedicated)) { + sc1.addAnd("domainId", SearchCriteria.Op.NNULL); + } else { + sc1.addAnd("domainId", SearchCriteria.Op.NULL); + } + if (domainId != null) { + sc1.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + } + if (accountId != null) { + sc1.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + } + // search via bgpPeerDao + return bgpPeerDao.search(sc1, null); + } else if (accountId != null) { + if (zoneId == null) { + throw new InvalidParameterValueException("zoneId is required when list BGP peers for an account."); + } + Account account = accountManager.getAccount(accountId); + List bgpPeerIds = getBgpPeerIdsForAccount(account, zoneId); + if (CollectionUtils.isEmpty(bgpPeerIds)) { + return new ArrayList<>(); + } + SearchCriteria sc2 = createSearchCriteriaForListBgpPeersCmd(id, zoneId, asNumber, keyword); + sc2.addAnd("id", SearchCriteria.Op.IN, bgpPeerIds.toArray()); + return bgpPeerDao.search(sc2, null); + } else { + SearchCriteria sc3 = createSearchCriteriaForListBgpPeersCmd(id, zoneId, asNumber, keyword); + if (domainId != null) { + sc3.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + } + return bgpPeerDao.search(sc3, null); + } + } + + private SearchCriteria createSearchCriteriaForListBgpPeersCmd(Long id, Long zoneId, Long asNumber, String keyword) { + SearchCriteria sc = bgpPeerDao.createSearchCriteria(); + if (id != null) { + sc.addAnd("id", SearchCriteria.Op.EQ, id); + } + if (zoneId != null) { + sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId); + } + if (asNumber != null) { + sc.addAnd("asNumber", SearchCriteria.Op.EQ, asNumber); + } + if (StringUtils.isNotBlank(keyword)) { + SearchCriteria ssc = bgpPeerDao.createSearchCriteria(); + ssc.addOr("asNumber", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("ip4Address", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("ip6Address", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + sc.addAnd("ip4Address", SearchCriteria.Op.SC, ssc); + } + return sc; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_NETWORK_BGP_PEER_UPDATE, + eventDescription = "Changing BGP peers for network", async = true) + public Network changeBgpPeersForNetwork(ChangeBgpPeersForNetworkCmd changeBgpPeersForNetworkCmd) { + Long networkId = changeBgpPeersForNetworkCmd.getNetworkId(); + List bgpPeerIds = changeBgpPeersForNetworkCmd.getBgpPeerIds(); + + Network network = networkDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException(String.format("Invalid network ID: %s", networkId)); + } + if (network.getVpcId() != null) { + throw new InvalidParameterValueException("The BGP peers of VPC tiers will inherit from the VPC, do not add separately."); + } + + Account owner = accountManager.getAccount(network.getAccountId()); + NetworkOffering networkOffering = networkOfferingDao.findById(network.getNetworkOfferingId()); + if (CollectionUtils.isNotEmpty(bgpPeerIds) && !isDynamicRoutedNetwork(networkOffering)) { + throw new InvalidParameterValueException("The network does not support Dynamic routing"); + } + validateBgpPeers(owner, network.getDataCenterId(), bgpPeerIds); + + return changeBgpPeersForNetworkInternal(network, bgpPeerIds); + } + + @Override + public Network removeBgpPeersFromNetwork(Network network) { + return changeBgpPeersForNetworkInternal(network, null); + } + + private Network changeBgpPeersForNetworkInternal(Network network, List bgpPeerIds) { + final List bgpPeerIdsToBeAdded; + if (CollectionUtils.isNotEmpty(bgpPeerIds)) { + bgpPeerIdsToBeAdded = new ArrayList<>(bgpPeerIds); + } else { + bgpPeerIdsToBeAdded = new ArrayList<>(); + } + List bgpPeerNetworkMapVOS = bgpPeerNetworkMapDao.listByNetworkId(network.getId()); + for (BgpPeerNetworkMapVO bgpPeerNetworkMapVO : bgpPeerNetworkMapVOS) { + Long bgpPeerId = bgpPeerNetworkMapVO.getBgpPeerId(); + if (bgpPeerIdsToBeAdded.contains(bgpPeerId)) { + bgpPeerIdsToBeAdded.remove(bgpPeerId); + } else { + bgpPeerNetworkMapVO.setState(BgpPeer.State.Revoke); + bgpPeerNetworkMapDao.update(bgpPeerNetworkMapVO.getId(), bgpPeerNetworkMapVO); + } + } + + for (Long bgpPeedId : bgpPeerIdsToBeAdded) { + bgpPeerNetworkMapDao.persist(new BgpPeerNetworkMapVO(bgpPeedId, network.getId(), null, BgpPeer.State.Add)); + } + + boolean result = true; + try { + result = bgpService.applyBgpPeers(network, false); + } catch (ResourceUnavailableException ex) { + logger.error("Unable to apply BGP peers due to : " + ex.getMessage()); + result = false; + } + if (result) { + logger.info("Succeed to apply BGP peers, updating state"); + bgpPeerNetworkMapVOS = bgpPeerNetworkMapDao.listByNetworkId(network.getId()); + for (BgpPeerNetworkMapVO bgpPeerNetworkMapVO : bgpPeerNetworkMapVOS) { + if (BgpPeer.State.Add.equals(bgpPeerNetworkMapVO.getState())) { + bgpPeerNetworkMapVO.setState(BgpPeer.State.Active); + bgpPeerNetworkMapDao.update(bgpPeerNetworkMapVO.getId(), bgpPeerNetworkMapVO); + } else if (BgpPeer.State.Revoke.equals(bgpPeerNetworkMapVO.getState())) { + bgpPeerNetworkMapDao.remove(bgpPeerNetworkMapVO.getId()); + } + } + } else { + logger.info("Failed to apply BGP peers, rolling back to original state"); + bgpPeerNetworkMapVOS = bgpPeerNetworkMapDao.listByNetworkId(network.getId()); + for (BgpPeerNetworkMapVO bgpPeerNetworkMapVO : bgpPeerNetworkMapVOS) { + if (BgpPeer.State.Add.equals(bgpPeerNetworkMapVO.getState())) { + bgpPeerNetworkMapDao.remove(bgpPeerNetworkMapVO.getId()); + } else if (BgpPeer.State.Revoke.equals(bgpPeerNetworkMapVO.getState())) { + bgpPeerNetworkMapVO.setState(BgpPeer.State.Add); + bgpPeerNetworkMapDao.update(bgpPeerNetworkMapVO.getId(), bgpPeerNetworkMapVO); + } + } + try { + bgpService.applyBgpPeers(network, false); + } catch (ResourceUnavailableException ex) { + logger.error("Unable to apply BGP peers after rollback due to : " + ex.getMessage()); + } + return null; + } + + return networkDao.findById(network.getId()); + } + + @Override + public void validateBgpPeers(Account owner, Long zoneId, List bgpPeerIds) { + if (CollectionUtils.isEmpty(bgpPeerIds)) { + return; + } + for (Long bgpPeerId : bgpPeerIds) { + BgpPeerVO bgpPeerVO = bgpPeerDao.findById(bgpPeerId); + if (bgpPeerVO == null) { + throw new InvalidParameterValueException(String.format("Invalid BGP peer ID: %s", bgpPeerId)); + } + if (bgpPeerVO.getDataCenterId() != zoneId) { + throw new InvalidParameterValueException(String.format("BGP peer (ID: %s) belongs to a different zone", bgpPeerVO.getUuid())); + } + if (bgpPeerVO.getDomainId() != null && !bgpPeerVO.getDomainId().equals(owner.getDomainId())) { + throw new InvalidParameterValueException(String.format("BGP peer (ID: %s) belongs to a different domain", bgpPeerVO.getUuid())); + } + if (bgpPeerVO.getAccountId() != null && !bgpPeerVO.getAccountId().equals(owner.getAccountId())) { + throw new InvalidParameterValueException(String.format("BGP peer (ID: %s) belongs to a different account", bgpPeerVO.getUuid())); + } + } + } + + @Override + public void persistBgpPeersForGuestNetwork(long networkId, List bgpPeerIds) { + bgpPeerNetworkMapDao.persistForNetwork(networkId, bgpPeerIds); + } + + @Override + public void releaseBgpPeersForGuestNetwork(long networkId) { + bgpPeerNetworkMapDao.removeByNetworkId(networkId); + } + + @Override + public void persistBgpPeersForVpc(long vpcId, List bgpPeerIds) { + bgpPeerNetworkMapDao.persistForVpc(vpcId, bgpPeerIds); + } + + @Override + public void releaseBgpPeersForVpc(long vpcId) { + bgpPeerNetworkMapDao.removeByVpcId(vpcId); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VPC_BGP_PEER_UPDATE, + eventDescription = "Changing BGP peers for VPC", async = true) + public Vpc changeBgpPeersForVpc(ChangeBgpPeersForVpcCmd changeBgpPeersForVpcCmd) { + Long vpcId = changeBgpPeersForVpcCmd.getVpcId(); + List bgpPeerIds = changeBgpPeersForVpcCmd.getBgpPeerIds(); + + Vpc vpc = vpcDao.findById(vpcId); + if (vpc == null) { + throw new InvalidParameterValueException(String.format("Invalid VPC ID: %s", vpcId)); + } + + Account owner = accountManager.getAccount(vpc.getAccountId()); + VpcOffering vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId()); + if (CollectionUtils.isNotEmpty(bgpPeerIds) && !isDynamicRoutedVpc(vpcOffering)) { + throw new InvalidParameterValueException("The VPC does not support Dynamic routing"); + } + validateBgpPeers(owner, vpc.getZoneId(), bgpPeerIds); + + return changeBgpPeersForVpcInternal(vpc, bgpPeerIds); + } + + @Override + public List getBgpPeerIdsForAccount(Account owner, long zoneId) { + return bgpPeerDao.listAvailableBgpPeerIdsForAccount(zoneId, owner.getDomainId(), owner.getId(), UseSystemBgpPeers.valueIn(owner.getId())); + } + + private Vpc changeBgpPeersForVpcInternal(Vpc vpc, List bgpPeerIds) { + final List bgpPeerIdsToBeAdded; + if (CollectionUtils.isNotEmpty(bgpPeerIds)) { + bgpPeerIdsToBeAdded = new ArrayList<>(bgpPeerIds); + } else { + bgpPeerIdsToBeAdded = new ArrayList<>(); + } + List bgpPeerNetworkMapVOS = bgpPeerNetworkMapDao.listByVpcId(vpc.getId()); + for (BgpPeerNetworkMapVO bgpPeerNetworkMapVO : bgpPeerNetworkMapVOS) { + Long bgpPeerId = bgpPeerNetworkMapVO.getBgpPeerId(); + if (bgpPeerIdsToBeAdded.contains(bgpPeerId)) { + bgpPeerIdsToBeAdded.remove(bgpPeerId); + } else { + bgpPeerNetworkMapVO.setState(BgpPeer.State.Revoke); + bgpPeerNetworkMapDao.update(bgpPeerNetworkMapVO.getId(), bgpPeerNetworkMapVO); + } + } + + for (Long bgpPeedId : bgpPeerIdsToBeAdded) { + bgpPeerNetworkMapDao.persist(new BgpPeerNetworkMapVO(bgpPeedId, null, vpc.getId(), BgpPeer.State.Add)); + } + + boolean result = true; + try { + result = bgpService.applyBgpPeers(vpc, false); + } catch (ResourceUnavailableException ex) { + logger.error("Unable to apply BGP peers due to : " + ex.getMessage()); + result = false; + } + if (result) { + logger.info("Succeed to apply BGP peers, updating state"); + bgpPeerNetworkMapVOS = bgpPeerNetworkMapDao.listByVpcId(vpc.getId()); + for (BgpPeerNetworkMapVO bgpPeerNetworkMapVO : bgpPeerNetworkMapVOS) { + if (BgpPeer.State.Add.equals(bgpPeerNetworkMapVO.getState())) { + bgpPeerNetworkMapVO.setState(BgpPeer.State.Active); + bgpPeerNetworkMapDao.update(bgpPeerNetworkMapVO.getId(), bgpPeerNetworkMapVO); + } else if (BgpPeer.State.Revoke.equals(bgpPeerNetworkMapVO.getState())) { + bgpPeerNetworkMapDao.remove(bgpPeerNetworkMapVO.getId()); + } + } + } else { + logger.info("Failed to apply BGP peers, rolling back to original state"); + bgpPeerNetworkMapVOS = bgpPeerNetworkMapDao.listByVpcId(vpc.getId()); + for (BgpPeerNetworkMapVO bgpPeerNetworkMapVO : bgpPeerNetworkMapVOS) { + if (BgpPeer.State.Add.equals(bgpPeerNetworkMapVO.getState())) { + bgpPeerNetworkMapDao.remove(bgpPeerNetworkMapVO.getId()); + } else if (BgpPeer.State.Revoke.equals(bgpPeerNetworkMapVO.getState())) { + bgpPeerNetworkMapVO.setState(BgpPeer.State.Add); + bgpPeerNetworkMapDao.update(bgpPeerNetworkMapVO.getId(), bgpPeerNetworkMapVO); + } + } + try { + bgpService.applyBgpPeers(vpc, false); + } catch (ResourceUnavailableException ex) { + logger.error("Unable to apply BGP peers after rollback due to : " + ex.getMessage()); + } + return null; + } + + return vpcDao.findById(vpc.getId()); + } + + @Override + public void removeIpv4SubnetsForZoneByAccountId(long accountId) { + List existingSubnets = dataCenterIpv4GuestSubnetDao.listByAccountId(accountId); + for (DataCenterIpv4GuestSubnetVO subnet : existingSubnets) { + ipv4GuestSubnetNetworkMapDao.deleteByParentId(subnet.getId()); + dataCenterIpv4GuestSubnetDao.remove(subnet.getId()); + } + } + + @Override + public void removeIpv4SubnetsForZoneByDomainId(long domainId) { + List existingSubnets = dataCenterIpv4GuestSubnetDao.listByDomainId(domainId); + for (DataCenterIpv4GuestSubnetVO subnet : existingSubnets) { + ipv4GuestSubnetNetworkMapDao.deleteByParentId(subnet.getId()); + dataCenterIpv4GuestSubnetDao.remove(subnet.getId()); + } + } + + @Override + public void removeBgpPeersByAccountId(long accountId) { + bgpPeerDao.removeByAccountId(accountId); + } + + @Override + public void removeBgpPeersByDomainId(long domainId) { + bgpPeerDao.removeByDomainId(domainId); + } +} diff --git a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java index 2dea5a4223f..817cfe07e58 100644 --- a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java +++ b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java @@ -353,7 +353,7 @@ public class RouterDeploymentDefinition { setupAccountOwner(); // Check if public network has to be set on VR - isPublicNetwork = networkModel.isProviderSupportServiceInNetwork(guestNetwork.getId(), Service.SourceNat, Provider.VirtualRouter); + isPublicNetwork = networkModel.isAnyServiceSupportedInNetwork(guestNetwork.getId(), Provider.VirtualRouter, Service.SourceNat, Service.Gateway); boolean canProceed = true; if (isRedundant() && !isPublicNetwork) { diff --git a/server/src/main/java/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java b/server/src/main/java/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java index e777e959b84..5c1fc5e9ac6 100644 --- a/server/src/main/java/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java +++ b/server/src/main/java/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java @@ -29,6 +29,7 @@ import com.cloud.network.RemoteAccessVpn; import com.cloud.network.VpnUser; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.AdvancedVpnRules; +import com.cloud.network.rules.BgpPeersRules; import com.cloud.network.rules.DhcpEntryRules; import com.cloud.network.rules.DhcpPvlanRules; import com.cloud.network.rules.NetworkAclsRules; @@ -47,6 +48,8 @@ import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -243,4 +246,27 @@ public class AdvancedNetworkTopology extends BasicNetworkTopology { final boolean result = applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper(aclsRules)); return result; } + + @Override + public boolean applyBgpPeers(Network network, List bpgPeers, VirtualRouter router) throws ResourceUnavailableException { + logger.debug("APPLYING BGP Peers"); + + if (CollectionUtils.isEmpty(bpgPeers)) { + logger.debug("No bgp peers to apply. However, apply BGP peers to clear the existing configuration in the VRs."); + } + + final BgpPeersRules bgpPeersRules = new BgpPeersRules(bpgPeers, network); + + boolean result = true; + if (router.getState() == State.Running) { + result = bgpPeersRules.accept(_advancedVisitor, router); + } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) { + logger.debug("Router " + router.getInstanceName() + " is in " + router.getState() + ", so not sending BgpPeer command to the backend"); + } else { + logger.warn("Unable to apply BgpPeer, virtual router is not in the right state " + router.getState()); + throw new ResourceUnavailableException("Unable to apply BgpPeer on the backend," + " virtual router is not in the right state", DataCenter.class, + router.getDataCenterId()); + } + return result; + } } diff --git a/server/src/main/java/org/apache/cloudstack/network/topology/AdvancedNetworkVisitor.java b/server/src/main/java/org/apache/cloudstack/network/topology/AdvancedNetworkVisitor.java index 4db46ac36bb..bc2271b0163 100644 --- a/server/src/main/java/org/apache/cloudstack/network/topology/AdvancedNetworkVisitor.java +++ b/server/src/main/java/org/apache/cloudstack/network/topology/AdvancedNetworkVisitor.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import com.cloud.network.rules.BgpPeersRules; +import org.apache.cloudstack.network.BgpPeer; import org.springframework.stereotype.Component; import com.cloud.agent.api.Command; @@ -211,4 +213,20 @@ public class AdvancedNetworkVisitor extends BasicNetworkVisitor { // results accordingly return _networkGeneralHelper.sendCommandsToRouter(router, cmds); } + + @Override + public boolean visit(final BgpPeersRules bgpPeersRules) throws ResourceUnavailableException { + final VirtualRouter router = bgpPeersRules.getRouter(); + final List bgpPeers = bgpPeersRules.getBgpPeers(); + final Network network = bgpPeersRules.getNetwork(); + + final Commands cmds = new Commands(Command.OnError.Continue); + + _commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, network); + if (cmds.size() == 0) { + return true; + } + + return _networkGeneralHelper.sendCommandsToRouter(router, cmds); + } } diff --git a/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkTopology.java b/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkTopology.java index 77519c500b2..65d702b7138 100644 --- a/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkTopology.java +++ b/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkTopology.java @@ -22,6 +22,7 @@ import java.util.List; import javax.inject.Inject; +import org.apache.cloudstack.network.BgpPeer; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.springframework.beans.factory.annotation.Autowired; @@ -463,4 +464,9 @@ public class BasicNetworkTopology implements NetworkTopology { return applyRules(network, virtualRouter, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper(dhcpRules)); } + + @Override + public boolean applyBgpPeers(Network network, List bpgPeers, VirtualRouter virtualRouter) throws ResourceUnavailableException { + throw new CloudRuntimeException("applyBgpPeers not implemented in Basic Network Topology."); + } } diff --git a/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkVisitor.java b/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkVisitor.java index 78f281f32cf..8702a58ad69 100644 --- a/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkVisitor.java +++ b/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkVisitor.java @@ -22,6 +22,7 @@ import java.util.List; import javax.inject.Inject; +import com.cloud.network.rules.BgpPeersRules; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -323,4 +324,9 @@ public class BasicNetworkVisitor extends NetworkTopologyVisitor { public boolean visit(final AdvancedVpnRules vpnRules) throws ResourceUnavailableException { throw new CloudRuntimeException("AdvancedVpnRules not implemented in Basic Network Topology."); } + + @Override + public boolean visit(final BgpPeersRules bgpPeersRules) throws ResourceUnavailableException { + throw new CloudRuntimeException("BgpPeersRules not implemented in Basic Network Topology."); + } } diff --git a/server/src/main/java/org/apache/cloudstack/network/topology/NetworkTopology.java b/server/src/main/java/org/apache/cloudstack/network/topology/NetworkTopology.java index aff40ce69ac..176584780fe 100644 --- a/server/src/main/java/org/apache/cloudstack/network/topology/NetworkTopology.java +++ b/server/src/main/java/org/apache/cloudstack/network/topology/NetworkTopology.java @@ -38,6 +38,7 @@ import com.cloud.network.vpc.StaticRouteProfile; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.network.BgpPeer; public interface NetworkTopology { @@ -89,4 +90,6 @@ public interface NetworkTopology { final boolean failWhenDisconnect, RuleApplierWrapper ruleApplier) throws ResourceUnavailableException; boolean removeDhcpEntry(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final VirtualRouter virtualRouter) throws ResourceUnavailableException; + + boolean applyBgpPeers(final Network network, final List bpgPeers, final VirtualRouter virtualRouter) throws ResourceUnavailableException; } diff --git a/server/src/main/java/org/apache/cloudstack/network/topology/NetworkTopologyVisitor.java b/server/src/main/java/org/apache/cloudstack/network/topology/NetworkTopologyVisitor.java index 035c67457e5..07bbcc19160 100644 --- a/server/src/main/java/org/apache/cloudstack/network/topology/NetworkTopologyVisitor.java +++ b/server/src/main/java/org/apache/cloudstack/network/topology/NetworkTopologyVisitor.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.network.topology; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.rules.AdvancedVpnRules; import com.cloud.network.rules.BasicVpnRules; +import com.cloud.network.rules.BgpPeersRules; import com.cloud.network.rules.DhcpEntryRules; import com.cloud.network.rules.DhcpPvlanRules; import com.cloud.network.rules.DhcpSubNetRules; @@ -64,4 +65,5 @@ public abstract class NetworkTopologyVisitor { public abstract boolean visit(DhcpSubNetRules dhcpRules) throws ResourceUnavailableException; public abstract boolean visit(NicPlugInOutRules nicPlugInOutRules) throws ResourceUnavailableException; public abstract boolean visit(StaticRoutesRules staticRoutesRules) throws ResourceUnavailableException; + public abstract boolean visit(BgpPeersRules bgpPeersRules) throws ResourceUnavailableException; } diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 12584e296f2..2504cabf809 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -2607,7 +2607,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of deployVM process"); Network newNetwork = networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", null, null, null, false, null, owner, null, physicalNetwork, zone.getId(), ControlledEntity.ACLType.Account, null, null, null, null, true, null, null, - null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, null); if (newNetwork != null) { defaultNetwork = networkDao.findById(newNetwork.getId()); } diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index 3237d11d1c7..e9b1cad78d7 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -363,6 +363,7 @@ + @@ -373,6 +374,8 @@ + + diff --git a/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java b/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java new file mode 100644 index 00000000000..ace7bc85691 --- /dev/null +++ b/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java @@ -0,0 +1,45 @@ +// 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.bgp; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; + +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class BGPServiceImplTest { + + @Spy + @InjectMocks + BGPServiceImpl bGPServiceImplSpy = new BGPServiceImpl(); + + @Test + public void testASNumbersOverlap() { + Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(1,2,3,4), false); + Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(1,2,2,4), true); + Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(1,3,2,4), true); + Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(2,4,1,3), true); + Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(1,4,2,3), true); + Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(3,4,1,2), false); + } +} diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java index a4ffa04a411..9499f5e8345 100644 --- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java +++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java @@ -82,8 +82,6 @@ import java.util.List; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.anyMap; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doNothing; @@ -442,17 +440,12 @@ public class ConfigurationManagerImplTest { NetworkOfferingVO offeringVO = Mockito.mock(NetworkOfferingVO.class); when(createNetworkOfferingCmd.isForNsx()).thenReturn(true); - when(createNetworkOfferingCmd.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name()); + when(createNetworkOfferingCmd.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.NATTED.name()); when(createNetworkOfferingCmd.getTraffictype()).thenReturn(Networks.TrafficType.Guest.name()); when(createNetworkOfferingCmd.getGuestIpType()).thenReturn(Network.GuestType.Isolated.name()); when(createNetworkOfferingCmd.getAvailability()).thenReturn(NetworkOffering.Availability.Optional.name()); - lenient().when(configurationManagerImplSpy.createNetworkOffering(anyString(), anyString(), any(Networks.TrafficType.class), anyString(), - anyBoolean(), any(NetworkOffering.Availability.class), anyInt(), anyMap(), anyBoolean(), any(Network.GuestType.class), - anyBoolean(), anyLong(), anyBoolean(), anyMap(), anyBoolean(), anyBoolean(), anyMap(), anyBoolean(), anyInt(), - anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyString(), anyList(), anyList(), anyBoolean(), any(NetUtils.InternetProtocol.class))) - .thenReturn(offeringVO); when(configDao.getValue(anyString())).thenReturn("1000"); - lenient().when(networkOfferingDao.persist(any(NetworkOfferingVO.class), anyMap())).thenReturn(offeringVO); + when(networkOfferingDao.persist(any(NetworkOfferingVO.class), anyMap())).thenReturn(offeringVO); doNothing().when(networkService).validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(anyLong()); doNothing().when(networkModel).canProviderSupportServices(anyMap()); diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java index 312719eb850..4ae871e1ba5 100644 --- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java +++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java @@ -116,6 +116,7 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; @@ -629,11 +630,11 @@ public class ConfigurationManagerTest { @Test public void isRedundantRouter() { - Map> serviceCapabilityMap = new HashMap<>(); + Set providers = new HashSet<>(); Map sourceNatServiceCapabilityMap = new HashMap<>(); sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "peraccount"); sourceNatServiceCapabilityMap.put(Capability.RedundantRouter, "true"); - Assert.assertTrue(configurationMgr.isRedundantRouter(serviceCapabilityMap, sourceNatServiceCapabilityMap)); + Assert.assertTrue(configurationMgr.isRedundantRouter(providers, Network.Service.SourceNat, sourceNatServiceCapabilityMap)); } @Test diff --git a/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java b/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java index 1160bf2ac8e..924d7df8896 100644 --- a/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java +++ b/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java @@ -132,7 +132,7 @@ public class CreatePrivateNetworkTest { ACLType.Account, false, 1L, false); when(networkService._networkMgr.createGuestNetwork(eq(ntwkOff.getId()), eq("bla"), eq("fake"), eq("10.1.1.1"), eq("10.1.1.0/24"), nullable(String.class), nullable(Boolean.class), nullable(String.class), eq(account), nullable(Long.class), eq(physicalNetwork), eq(physicalNetwork.getDataCenterId()), eq(ACLType.Account), nullable(Boolean.class), eq(1L), nullable(String.class), nullable(String.class), - nullable(Boolean.class), nullable(String.class), nullable(Network.PVlanType.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(Pair.class))).thenReturn(net); + nullable(Boolean.class), nullable(String.class), nullable(Network.PVlanType.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(Pair.class), nullable(Integer.class))).thenReturn(net); when( networkService._networkMgr.createPrivateNetwork(eq(ntwkOff.getId()), eq("bla"), eq("fake"), eq("10.1.1.1"), eq("10.1.1.0/24"), anyString(), anyBoolean(), eq(account), eq(physicalNetwork), eq(1L))).thenReturn(net); diff --git a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java index ec594f185c4..d125291657a 100644 --- a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java @@ -341,6 +341,11 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { return false; } + @Override + public boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services) { + return false; + } + /* (non-Javadoc) * @see com.cloud.network.NetworkModel#isProviderEnabledInPhysicalNetwork(long, java.lang.String) */ diff --git a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java index 7832537fe71..64d813c9ba8 100644 --- a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java +++ b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java @@ -19,10 +19,9 @@ package com.cloud.network; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @@ -53,6 +52,7 @@ import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; @@ -67,6 +67,7 @@ import org.springframework.test.util.ReflectionTestUtils; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.alert.AlertManager; +import com.cloud.bgp.BGPService; import com.cloud.configuration.ConfigurationManager; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; @@ -211,6 +212,12 @@ public class NetworkServiceImplTest { @Mock private IpAddressManager ipAddressManagerMock; + @Mock + private RoutedIpv4Manager routedIpv4Manager; + + @Mock + BGPService bgpService; + @Mock private Ip ipMock; @Mock @@ -248,6 +255,10 @@ public class NetworkServiceImplTest { private AutoCloseable closeable; + private NetworkOfferingVO networkOfferingVO; + private Long zoneId = 10L; + private Long networkId = 11L; + @BeforeClass public static void setUpBeforeClass() { Date date = new Date(); @@ -310,7 +321,6 @@ public class NetworkServiceImplTest { Mockito.when(networkOfferingDao.findById(1L)).thenReturn(offering); Mockito.when(physicalNetworkDao.findById(Mockito.anyLong())).thenReturn(phyNet); Mockito.when(dcDao.findById(Mockito.anyLong())).thenReturn(dc); - Mockito.lenient().doNothing().when(accountManager).checkAccess(accountMock, networkOffering, dc); Mockito.when(accountManager.isRootAdmin(accountMock.getId())).thenReturn(true); } @@ -430,7 +440,6 @@ public class NetworkServiceImplTest { Mockito.when(dc.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled); Map networkProvidersMap = new HashMap(); Mockito.when(networkManager.finalizeServicesAndProvidersForNetwork(ArgumentMatchers.any(NetworkOffering.class), anyLong())).thenReturn(networkProvidersMap); - lenient().doNothing().when(alertManager).sendAlert(any(AlertService.AlertType.class), anyLong(), anyLong(), anyString(), anyString()); Mockito.when(configMgr.isOfferingForVpc(offering)).thenReturn(false); Mockito.when(offering.isInternalLb()).thenReturn(false); @@ -439,7 +448,7 @@ public class NetworkServiceImplTest { null, null, false, null, accountMock, null, phyNet, 1L, null, null, null, null, null, true, null, null, null, null, null, - null, null, null, null, new Pair<>(1500, privateMtu)); + null, null, null, null, new Pair<>(1500, privateMtu), null); } @Test public void testValidateMtuConfigWhenMtusExceedThreshold() { @@ -488,7 +497,7 @@ public class NetworkServiceImplTest { Mockito.verify(vpcMgr, times(1)).createVpcGuestNetwork(1L, "testNetwork", "Test Network", null, null, null, null, accountMock, null, phyNet, 1L, null, null, 1L, null, accountMock, - true, null, null, null, null, null, null, null, new Pair<>(0, 1000)); + true, null, null, null, null, null, null, null, new Pair<>(0, 1000), null); } @@ -551,7 +560,7 @@ public class NetworkServiceImplTest { private void prepareCreateNetworkDnsMocks(CreateNetworkCmd cmd, Network.GuestType guestType, boolean ipv6, boolean isVpc, boolean dnsServiceSupported) { long networkOfferingId = 1L; Mockito.when(cmd.getNetworkOfferingId()).thenReturn(networkOfferingId); - NetworkOfferingVO networkOfferingVO = Mockito.mock(NetworkOfferingVO.class); + networkOfferingVO = Mockito.mock(NetworkOfferingVO.class); Mockito.when(networkOfferingVO.getId()).thenReturn(networkOfferingId); Mockito.when(networkOfferingVO.getGuestType()).thenReturn(guestType); Mockito.when(networkOfferingDao.findById(networkOfferingId)).thenReturn(networkOfferingVO); @@ -598,6 +607,7 @@ public class NetworkServiceImplTest { CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class); prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, false, true); Mockito.when(cmd.getIp4Dns1()).thenReturn(ip4Dns[0]); + Mockito.when(cmd.getCidrSize()).thenReturn(null); try { service.createGuestNetwork(cmd); } catch (InsufficientCapacityException | ResourceAllocationException e) { @@ -742,6 +752,31 @@ public class NetworkServiceImplTest { Assert.assertNull(networkVO.getIp6Dns2()); } + @Test + public void testCreateIpv4RoutedNetwork() throws InsufficientCapacityException, ResourceAllocationException { + registerCallContext(); + CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class); + Mockito.when(cmd.getCidrSize()).thenReturn(24); + prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, false, true); + when(networkOfferingVO.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED); + when(networkOfferingVO.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Static); + when(routedIpv4Manager.isVirtualRouterGateway(networkOfferingVO)).thenReturn(true); + doNothing().when(routedIpv4Manager).assignIpv4SubnetToNetwork(nullable(Network.class)); + + DataCenterVO zone = Mockito.mock(DataCenterVO.class); + when(cmd.getZoneId()).thenReturn(zoneId); + when(dcDao.findById(zoneId)).thenReturn(zone); + when(zone.getId()).thenReturn(zoneId); + + try { + service.createGuestNetwork(cmd); + } catch (InsufficientCapacityException | ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + + Mockito.verify(routedIpv4Manager).assignIpv4SubnetToNetwork(nullable(Network.class)); + } + @Test public void testCheckAndUpdateNetworkResetSuccess() { NetworkVO networkVO = new NetworkVO(); diff --git a/server/src/test/java/com/cloud/network/rules/BgpPeersRulesTest.java b/server/src/test/java/com/cloud/network/rules/BgpPeersRulesTest.java new file mode 100644 index 00000000000..49067f265c9 --- /dev/null +++ b/server/src/test/java/com/cloud/network/rules/BgpPeersRulesTest.java @@ -0,0 +1,98 @@ +// 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.rules; + +import com.cloud.dc.DataCenter; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.router.VirtualRouter; + +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.topology.NetworkTopologyVisitor; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class BgpPeersRulesTest { + + private BgpPeersRules bgpPeersRules; + private Network mockNetwork; + private List mockBgpPeers; + private NetworkTopologyVisitor mockVisitor; + private VirtualRouter mockRouter; + + @Before + public void setUp() { + mockNetwork = mock(Network.class); + BgpPeer peer1 = mock(BgpPeer.class); + BgpPeer peer2 = mock(BgpPeer.class); + mockBgpPeers = Arrays.asList(peer1, peer2); + + mockVisitor = mock(NetworkTopologyVisitor.class); + mockRouter = mock(VirtualRouter.class); + + bgpPeersRules = new BgpPeersRules(mockBgpPeers, mockNetwork); + } + + @Test + public void testGetBgpPeers() { + List bgpPeers = bgpPeersRules.getBgpPeers(); + assertNotNull(bgpPeers); + assertEquals(2, bgpPeers.size()); + assertTrue(bgpPeers.containsAll(mockBgpPeers)); + } + + @Test + public void testAccept() throws ResourceUnavailableException { + when(mockVisitor.visit(bgpPeersRules)).thenReturn(true); + + boolean result = bgpPeersRules.accept(mockVisitor, mockRouter); + + assertTrue(result); + verify(mockVisitor, times(1)).visit(bgpPeersRules); + } + + @Test + public void testAcceptThrowsResourceUnavailableException() throws ResourceUnavailableException { + when(mockVisitor.visit(bgpPeersRules)).thenThrow(new ResourceUnavailableException("Resource Unavailable", DataCenter.class, 1L)); + + ResourceUnavailableException thrown = assertThrows(ResourceUnavailableException.class, () -> { + bgpPeersRules.accept(mockVisitor, mockRouter); + }); + + assertEquals("Resource [DataCenter:1] is unreachable: Resource Unavailable", thrown.getMessage()); + assertEquals(DataCenter.class, thrown.getScope()); + assertEquals(1L, thrown.getResourceId()); + + verify(mockVisitor, times(1)).visit(bgpPeersRules); + } +} diff --git a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java index deffb165f29..54acaa58acc 100644 --- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java @@ -73,6 +73,7 @@ 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.framework.config.ConfigKey; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -98,10 +99,14 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class VpcManagerImplTest { @@ -155,6 +160,8 @@ public class VpcManagerImplTest { FirewallRulesDao firewallDao; @Mock NetworkACLVO networkACLVOMock; + @Mock + RoutedIpv4Manager routedIpv4Manager; public static final long ACCOUNT_ID = 1; private AccountVO account; @@ -179,6 +186,8 @@ public class VpcManagerImplTest { private AutoCloseable closeable; + private VpcOfferingVO vpcOfferingVO; + private void registerCallContext() { account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); account.setId(ACCOUNT_ID); @@ -212,6 +221,7 @@ public class VpcManagerImplTest { manager._ntwkSvc = networkServiceMock; manager._firewallDao = firewallDao; manager._networkAclDao = networkACLDaoMock; + manager.routedIpv4Manager = routedIpv4Manager; CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class)); registerCallContext(); overrideDefaultConfigValue(NetworkService.AllowUsersToSpecifyVRMtu, "_defaultValue", "false"); @@ -365,13 +375,13 @@ public class VpcManagerImplTest { manager.createVpcGuestNetwork(1L, "vpcNet1", "vpc tier 1", null, "10.10.10.0/24", null, null, accountMock, null, physicalNetwork, 1L, null, null, 1L, null, accountMock, - true, null, null, null, null, null, null, null, new Pair<>(1000, 1000)); + true, null, null, null, null, null, null, null, new Pair<>(1000, 1000), null); Mockito.verify(networkMgr, times(1)).createGuestNetwork(1L, "vpcNet1", "vpc tier 1", null, "10.10.10.0/24", null, false, "cs1cloud.internal", accountMock, null, physicalNetwork, zoneId, null, null, 1L, null, null, true, null, null, null, null, - null, null, null, null, null, new Pair<>(1000, 1000)); + null, null, null, null, null, new Pair<>(1000, 1000), null); } @Test @@ -462,7 +472,7 @@ public class VpcManagerImplTest { private void mockVpcDnsResources(boolean supportDnsService, boolean isIpv6) { Mockito.when(accountManager.getAccount(vpcOwnerId)).thenReturn(account); - VpcOfferingVO vpcOfferingVO = Mockito.mock(VpcOfferingVO.class); + vpcOfferingVO = Mockito.mock(VpcOfferingVO.class); Mockito.when(vpcOfferingVO.getId()).thenReturn(vpcOfferingId); Mockito.when(vpcOfferingVO.getState()).thenReturn(VpcOffering.State.Enabled); Mockito.when(vpcOfferingDao.findById(vpcOfferingId)).thenReturn(vpcOfferingVO); @@ -479,7 +489,7 @@ public class VpcManagerImplTest { try { doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain, - ip4Dns[0], null, null, null, true, 1500); + ip4Dns[0], null, null, null, true, 1500, null, null, null); } catch (ResourceAllocationException e) { Assert.fail(String.format("failure with exception: %s", e.getMessage())); } @@ -491,12 +501,46 @@ public class VpcManagerImplTest { try { doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain, - ip4Dns[0], ip4Dns[1], ip6Dns[0], null, true, 1500); + ip4Dns[0], ip4Dns[1], ip6Dns[0], null, true, 1500, null, null, null); } catch (ResourceAllocationException e) { Assert.fail(String.format("failure with exception: %s", e.getMessage())); } } + @Test + public void testCreateVpc() { + mockVpcDnsResources(true, false); + VpcVO vpc = Mockito.mock(VpcVO.class); + Mockito.when(vpcDao.persist(any(), anyMap())).thenReturn(vpc); + Mockito.when(vpc.getUuid()).thenReturn("uuid"); + try { + doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); + manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain, + ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null, null); + } catch (ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + } + + @Test + public void testCreateRoutedVpc() { + mockVpcDnsResources(true, false); + VpcVO vpc = Mockito.mock(VpcVO.class); + Mockito.when(vpcDao.persist(any(), anyMap())).thenReturn(vpc); + Mockito.when(vpc.getUuid()).thenReturn("uuid"); + doReturn(true).when(routedIpv4Manager).isRoutedVpc(any()); + doNothing().when(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(), anyString()); + try { + doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); + manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain, + ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null, null); + } catch (ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + + verify(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(), anyString()); + } + @Test public void validateVpcPrivateGatewayAclIdTestNullAclVoThrowsInvalidParameterValueException() { Mockito.doReturn(null).when(networkACLDaoMock).findById(aclId); diff --git a/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java b/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java index 7f9fa488471..e97fddc0262 100644 --- a/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java +++ b/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java @@ -65,6 +65,7 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationSe import org.apache.cloudstack.engine.service.api.OrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleDao; import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao; import org.junit.After; @@ -203,6 +204,8 @@ public class AccountManagetImplTestBase { UsageEventDao _usageEventDao; @Mock AccountService _accountService; + @Mock + RoutedIpv4Manager routedIpv4Manager; @Before public void setup() { diff --git a/server/src/test/java/com/cloud/user/DomainManagerImplTest.java b/server/src/test/java/com/cloud/user/DomainManagerImplTest.java index 829f0c9cb20..39155986941 100644 --- a/server/src/test/java/com/cloud/user/DomainManagerImplTest.java +++ b/server/src/test/java/com/cloud/user/DomainManagerImplTest.java @@ -49,6 +49,7 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.region.RegionManager; import org.junit.Assert; import org.junit.Before; @@ -108,6 +109,8 @@ public class DomainManagerImplTest { DomainDetailsDao _domainDetailsDao; @Mock AnnotationDao annotationDao; + @Mock + RoutedIpv4Manager routedIpv4Manager; @Spy @InjectMocks diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java index 2a1f4fffbf8..8f05b716725 100644 --- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -547,7 +547,8 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu Integer networkRate, Map> serviceProviderMap, boolean isDefault, GuestType type, boolean systemOnly, Long serviceOfferingId, boolean conserveMode, Map> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent, Map details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive, Boolean forVpc, - Boolean forTungsten, boolean forNsx, String mode, List domainIds, List zoneIds, boolean enableOffering, NetUtils.InternetProtocol internetProtocol) { + Boolean forTungsten, boolean forNsx, NetworkOffering.NetworkMode networkMode, List domainIds, List zoneIds, boolean enableOffering, NetUtils.InternetProtocol internetProtocol, + NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber) { // TODO Auto-generated method stub return null; } diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index 68ad250a95e..7f4344f30e4 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -696,7 +696,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches public Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, boolean bypassVlanOverlapCheck, String networkDomain, Account owner, Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String gatewayv6, String cidrv6, Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6, - String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs) throws ConcurrentOperationException, ResourceAllocationException { + String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Pair vrIfaceMTUs, Integer networkCidrSize) throws ConcurrentOperationException, ResourceAllocationException { // TODO Auto-generated method stub return null; } diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java index 5802b88a6cf..7f94405ffdc 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java @@ -352,6 +352,11 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { return false; } + @Override + public boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services) { + return false; + } + /* (non-Javadoc) * @see com.cloud.network.NetworkModel#isProviderEnabledInPhysicalNetwork(long, java.lang.String) */ diff --git a/server/src/test/java/com/cloud/vpc/dao/MockNetworkServiceMapDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockNetworkServiceMapDaoImpl.java index d5192644e86..6746c5ecbf8 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockNetworkServiceMapDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockNetworkServiceMapDaoImpl.java @@ -39,6 +39,11 @@ public class MockNetworkServiceMapDaoImpl extends GenericDaoBase public boolean isIpv6Supported(long offeringId) { return false; } + + @Override + public boolean isRoutedVpc(long offeringId) { + return false; + } } diff --git a/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java b/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java index 679324fed2f..b5c842b8806 100644 --- a/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java +++ b/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java @@ -773,8 +773,8 @@ public class RouterDeploymentDefinitionTest extends RouterDeploymentDefinitionTe protected void driveTestPrepareDeployment(final boolean isRedundant, final boolean isPublicNw) { // Prepare when(mockNw.isRedundant()).thenReturn(isRedundant); - when(mockNetworkModel.isProviderSupportServiceInNetwork( - NW_ID_1, Service.SourceNat, Provider.VirtualRouter)).thenReturn(isPublicNw); + when(mockNetworkModel.isAnyServiceSupportedInNetwork( + NW_ID_1, Provider.VirtualRouter, Service.SourceNat, Service.Gateway)).thenReturn(isPublicNw); // Execute final boolean canProceedDeployment = deployment.prepareDeployment(); // Assert diff --git a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java index 838bb3dadcb..25b4bdda45f 100644 --- a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java +++ b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java @@ -138,7 +138,7 @@ public class CreateNetworkOfferingTest extends TestCase { public void createSharedNtwkOffWithVlan() { NetworkOfferingVO off = configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared, false, - null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null); + null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false); assertNotNull("Shared network offering with specifyVlan=true failed to create ", off); } @@ -146,7 +146,7 @@ public class CreateNetworkOfferingTest extends TestCase { public void createSharedNtwkOffWithNoVlan() { NetworkOfferingVO off = configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, false, Availability.Optional, 200, null, false, Network.GuestType.Shared, - false, null, false, null, true, false, null, false, null, true, false, false, false, null, null,null, false, null); + false, null, false, null, true, false, null, false, null, true, false, false, false, null, null,null, false, null, null, false); assertNotNull("Shared network offering with specifyVlan=false was created", off); } @@ -154,7 +154,7 @@ public class CreateNetworkOfferingTest extends TestCase { public void createSharedNtwkOffWithSpecifyIpRanges() { NetworkOfferingVO off = configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared, false, - null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null); + null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false); assertNotNull("Shared network offering with specifyIpRanges=true failed to create ", off); } @@ -163,7 +163,7 @@ public class CreateNetworkOfferingTest extends TestCase { public void createSharedNtwkOffWithoutSpecifyIpRanges() { NetworkOfferingVO off = configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared, - false, null, false, null, false, false, null, false, null, true, false, false, false, null,null, null, false, null); + false, null, false, null, false, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false); assertNull("Shared network offering with specifyIpRanges=false was created", off); } @@ -176,7 +176,7 @@ public class CreateNetworkOfferingTest extends TestCase { serviceProviderMap.put(Network.Service.SourceNat, vrProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, null, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, null, null, null, false, null, null, false); assertNotNull("Isolated network offering with specifyIpRanges=false failed to create ", off); } @@ -189,7 +189,7 @@ public class CreateNetworkOfferingTest extends TestCase { serviceProviderMap.put(Network.Service.SourceNat, vrProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, null,null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false); assertNotNull("Isolated network offering with specifyVlan=true wasn't created", off); } @@ -202,7 +202,7 @@ public class CreateNetworkOfferingTest extends TestCase { serviceProviderMap.put(Network.Service.SourceNat, vrProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false); assertNull("Isolated network offering with specifyIpRanges=true and source nat service enabled, was created", off); } @@ -213,7 +213,7 @@ public class CreateNetworkOfferingTest extends TestCase { Set vrProvider = new HashSet(); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false); assertNotNull("Isolated network offering with specifyIpRanges=true and with no sourceNatService, failed to create", off); } @@ -231,7 +231,7 @@ public class CreateNetworkOfferingTest extends TestCase { serviceProviderMap.put(Network.Service.Lb, vrProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, false, null, null, false); // System.out.println("Creating Vpc Network Offering"); assertNotNull("Vpc Isolated network offering with Vpc provider ", off); } @@ -251,7 +251,7 @@ public class CreateNetworkOfferingTest extends TestCase { serviceProviderMap.put(Network.Service.Lb, lbProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, false, null, null, false); // System.out.println("Creating Vpc Network Offering"); assertNotNull("Vpc Isolated network offering with Vpc and Netscaler provider ", off); } diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index 5215c2c6315..21e125e3bb6 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -26,6 +26,7 @@ import urllib.parse import urllib.error import time import copy +import ipaddress from collections import OrderedDict from fcntl import flock, LOCK_EX, LOCK_UN @@ -41,7 +42,11 @@ from cs.CsConfig import CsConfig from cs.CsProcess import CsProcess from cs.CsStaticRoutes import CsStaticRoutes from cs.CsVpcGuestNetwork import CsVpcGuestNetwork +from cs.CsBgpPeers import CsBgpPeers +ICMP_TYPE_ANY = "{ echo-reply, destination-unreachable, source-quench, redirect, echo-request, time-exceeded, \ + parameter-problem, timestamp-request, timestamp-reply, info-request, info-reply, address-mask-request, \ + address-mask-reply, router-advertisement, router-solicitation }" ICMPV6_TYPE_ANY = "{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, \ echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, \ nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, router-renumbering }" @@ -272,6 +277,120 @@ class CsAcl(CsDataBag): self.fw.append(["filter", "", "%s -j %s" % (fwr, rule['action'])]) logging.debug("EGRESS rule configured for protocol ==> %s, action ==> %s", rule['protocol'], rule['action']) + def add_routing_rules(self): + fw = self.config.get_nft_ipv4_fw() + logging.info("Processing routing firewall rules %s: %s" % (self.dbag, fw)) + chains_added = False + egress_policy = None + for item in self.dbag: + if item == "id": + continue + rule = self.dbag[item] + + network = ipaddress.ip_network(self.config.cmdline().get_eth0_ip() + "/" + self.config.cmdline().get_cidr_size(), False) + guest_cidr = network.with_prefixlen + if chains_added is False: + parent_chain = "FORWARD" + chain = "fw_chain_egress" + parent_chain_rule = "ip saddr %s jump %s" % (guest_cidr, chain) + fw.append({'type': "chain", 'chain': chain}) + fw.append({'type': "", 'chain': parent_chain, 'rule': parent_chain_rule}) + chain = "fw_chain_ingress" + parent_chain_rule = "ip daddr %s jump %s" % (guest_cidr, chain) + fw.append({'type': "chain", 'chain': chain}) + fw.append({'type': "", 'chain': parent_chain, 'rule': parent_chain_rule}) + if rule['default_egress_policy']: + egress_policy = "accept" + else: + egress_policy = "drop" + chains_added = True + + rstr = "" + + chain = "fw_chain_ingress" + if 'traffic_type' in rule and rule['traffic_type'].lower() == "egress": + chain = "fw_chain_egress" + + saddr = "" + if 'source_cidr_list' in rule and len(rule['source_cidr_list']) > 0: + source_cidrs = rule['source_cidr_list'] + if len(source_cidrs) == 1: + source_cidrs = source_cidrs[0] + else: + source_cidrs = "{" + (",".join(source_cidrs)) + "}" + saddr = "ip saddr " + source_cidrs + daddr = "" + if 'dest_cidr_list' in rule and len(rule['dest_cidr_list']) > 0: + dest_cidrs = rule['dest_cidr_list'] + if len(dest_cidrs) == 1: + dest_cidrs = dest_cidrs[0] + else: + dest_cidrs = "{" + (",".join(dest_cidrs)) + "}" + daddr = "ip daddr " + dest_cidrs + + proto = "" + protocol = rule['protocol'] + if protocol != "all": + icmp_type = "" + proto = protocol + if proto == "icmp": + proto = proto_str = "icmp" + icmp_type = ICMP_TYPE_ANY + if 'icmp_type' in rule and rule['icmp_type'] != -1: + icmp_type = str(rule['icmp_type']) + proto = "%s type %s" % (proto_str, icmp_type) + if 'icmp_code' in rule and rule['icmp_code'] != -1: + proto = "%s %s code %d" % (proto, proto_str, rule['icmp_code']) + first_port = "" + last_port = "" + if 'src_port_range' in rule: + first_port = rule['src_port_range'][0] + last_port = rule['src_port_range'][1] + port = "" + if first_port: + port = first_port + if last_port and port and \ + last_port != first_port: + port = "{%s-%s}" % (port, last_port) + if (protocol == "tcp" or protocol == "udp") and not port: + port = TCP_UDP_PORT_ANY + if port: + proto = "%s dport %s" % (proto, port) + + action = "accept" + if chain == "fw_chain_egress": + # In case we have a default rule (accept all or drop all), we have to evaluate the action again. + if protocol == 'all' and not rule['source_cidr_list']: + # For default egress ALLOW or DENY, the logic is inverted. + # Having default_egress_policy == True, means that the default rule should have ACCEPT, + # otherwise DROP. The rule should be appended, not inserted. + if rule['default_egress_policy']: + action = "accept" + else: + action = "drop" + else: + # For other rules added, if default_egress_policy == True, following rules should be DROP, + # otherwise ACCEPT + if rule['default_egress_policy']: + action = "drop" + else: + action = "accept" + + rstr = saddr + type = "" + rstr = appendStringIfNotEmpty(rstr, daddr) + rstr = appendStringIfNotEmpty(rstr, proto) + if rstr and action: + rstr = rstr + " " + action + logging.debug("Process routing firewall rule %s" % rstr) + fw.append({'type': type, 'chain': chain, 'rule': rstr}) + if chains_added: + base_rstr = "counter packets 0 bytes 0" + rstr = "%s drop" % base_rstr + fw.append({'type': "", 'chain': "fw_chain_ingress", 'rule': rstr}) + rstr = "%s %s" % (base_rstr, egress_policy) + fw.append({'type': "", 'chain': "fw_chain_egress", 'rule': rstr}) + class AclDevice(): """ A little class for each list of acls per device """ @@ -295,10 +414,89 @@ class CsAcl(CsDataBag): self.egress = obj['egress_rules'] self.fw = config.get_fw() self.ipv6_acl = config.get_ipv6_acl() + self.nft_ipv4_acl = config.get_nft_ipv4_acl() def create(self): - self.process("ingress", self.ingress, self.FIXED_RULES_INGRESS) - self.process("egress", self.egress, self.FIXED_RULES_EGRESS) + self.process("ingress", self.ingress, self.FIXED_RULES_INGRESS, self.config.is_routed()) + self.process("egress", self.egress, self.FIXED_RULES_EGRESS, self.config.is_routed()) + + def __process_routing_ip4(self, direction, rule_list): + if not self.cidr: + return + tier_cidr = self.cidr + chain = "%s_%s_policy" % (self.device, direction) + parent_chain = "FORWARD" + cidr_key = "saddr" + if direction == "ingress": + cidr_key = "daddr" + parent_chain_rule = "ip %s %s jump %s" % (cidr_key, tier_cidr, chain) + self.nft_ipv4_acl.append({'type': "", 'chain': parent_chain, 'rule': parent_chain_rule}) + self.nft_ipv4_acl.insert(0, {'type': "chain", 'chain': chain}) + for rule in rule_list: + cidr = rule['cidr'] + if cidr is not None and cidr != "": + cidr = removeUndesiredCidrs(cidr, 6) + if cidr is None or cidr == "": + continue + addr = "" + if cidr: + addr = "ip daddr " + cidr + if direction == "ingress": + addr = "ip saddr " + cidr + + proto = "" + protocol = rule['type'] + if protocol != "all": + icmp_type = "" + if protocol == "protocol": + protocol = "ip nexthdr %d" % rule['protocol'] + proto = protocol + if proto == "icmp": + proto = proto_str = "icmp" + icmp_type = ICMP_TYPE_ANY + if 'icmp_type' in rule and rule['icmp_type'] != -1: + icmp_type = str(rule['icmp_type']) + proto = "%s type %s" % (proto_str, icmp_type) + if 'icmp_code' in rule and rule['icmp_code'] != -1: + proto = "%s %s code %d" % (proto, proto_str, rule['icmp_code']) + + first_port = "" + last_port = "" + if 'first_port' in rule: + first_port = rule['first_port'] + if 'last_port' in rule: + last_port = rule['last_port'] + port = "" + if first_port: + port = first_port + if last_port and port and \ + last_port != first_port: + port = "{%s-%s}" % (port, last_port) + if (protocol == "tcp" or protocol == "udp") and not port: + port = TCP_UDP_PORT_ANY + if port: + proto = "%s dport %s" % (proto, port) + + action = "drop" + if 'allowed' in list(rule.keys()) and rule['allowed']: + action = "accept" + + rstr = addr + type = "" + rstr = appendStringIfNotEmpty(rstr, proto) + if rstr and action: + rstr = rstr + " " + action + else: + type = "chain" + rstr = action + logging.debug("Process routing ACL rule %s" % rstr) + if type == "chain": + self.nft_ipv4_acl.insert(0, {'type': type, 'chain': chain, 'rule': rstr}) + else: + self.nft_ipv4_acl.append({'type': type, 'chain': chain, 'rule': rstr}) + + rstr = "counter packets 0 bytes 0 drop" + self.nft_ipv4_acl.append({'type': "", 'chain': chain, 'rule': rstr}) def __process_ip6(self, direction, rule_list): if not self.ip6_cidr: @@ -377,7 +575,12 @@ class CsAcl(CsDataBag): rstr = "counter packets 0 bytes 0 drop" self.ipv6_acl.append({'type': "", 'chain': chain, 'rule': rstr}) - def process(self, direction, rule_list, base): + def process(self, direction, rule_list, base, is_routed): + if is_routed: + self.__process_routing_ip4(direction, rule_list) + self.__process_ip6(direction, rule_list) + return + count = base for i in rule_list: ruleData = copy.copy(i) @@ -398,7 +601,7 @@ class CsAcl(CsDataBag): def __init__(self, direction, acl, rule, config, count): self.count = count - if config.is_vpc(): + if config.is_vpc() and not config.is_routed(): self.init_vpc(direction, acl, rule, config) def init_vpc(self, direction, acl, rule, config): @@ -444,7 +647,21 @@ class CsAcl(CsDataBag): rstr = rstr.replace(" ", " ").lstrip() self.fw.append([self.table, self.count, rstr]) + def flushAllIptablesRules(self): + if not self.config.is_routed(): + return + # Flush all iptables rules for routing networks, which are replaced by nftables rules + logging.info("Flush all iptables rules") + CsHelper.execute("iptables -F filter") + CsHelper.execute("iptables -F nat") + CsHelper.execute("iptables -F mangle") + CsHelper.execute("nft delete table ip filter") + CsHelper.execute("nft delete table ip nat") + CsHelper.execute("nft delete table ip mangle") + def flushAllowAllEgressRules(self): + if self.config.is_routed(): + return logging.debug("Flush allow 'all' egress firewall rule") # Ensure that FW_EGRESS_RULES chain exists CsHelper.execute("iptables-save | grep '^:FW_EGRESS_RULES' || iptables -t filter -N FW_EGRESS_RULES") @@ -453,6 +670,26 @@ class CsAcl(CsDataBag): CsHelper.execute("ipset -L | grep Name: | awk {'print $2'} | ipset flush") CsHelper.execute("ipset -L | grep Name: | awk {'print $2'} | ipset destroy") + def flushAllIpv4RoutingRules(self): + if not self.config.is_routed(): + return + logging.info("Flush all Routing firewall rules") + address_family = 'ip' + table = 'ip4_firewall' + tables = CsHelper.execute("nft list tables %s | grep %s" % (address_family, table)) + if any(table in t for t in tables): + CsHelper.execute("nft delete table %s %s" % (address_family, table)) + + def flushAllIpv4RoutingACLRules(self): + if not self.config.is_routed(): + return + logging.info("Flush all ACL rules for routing network") + address_family = 'ip' + table = 'ip4_acl' + tables = CsHelper.execute("nft list tables %s | grep %s" % (address_family, table)) + if any(table in t for t in tables): + CsHelper.execute("nft delete table %s %s" % (address_family, table)) + def flushAllIpv6Rules(self): logging.info("Flush all IPv6 ACL rules") address_family = 'ip6' @@ -462,6 +699,10 @@ class CsAcl(CsDataBag): CsHelper.execute("nft delete table %s %s" % (address_family, table)) def process(self): + if self.config.is_routed() and not self.config.is_vpc(): + self.add_routing_rules() + return + for item in self.dbag: if item == "id": continue @@ -470,7 +711,6 @@ class CsAcl(CsDataBag): else: self.AclIP(self.dbag[item], self.config).create() - class CsIpv6Firewall(CsDataBag): """ Deal with IPv6 Firewall @@ -1335,11 +1575,22 @@ class IpTablesExecutor: nf = CsNetfilters() nf.compare(self.config.get_fw()) - logging.info("Configuring nftables ACL rules %s" % self.config.get_ipv6_acl()) + logging.info("Configuring nftables IPv4 firewall rules %s" % self.config.get_nft_ipv4_fw()) + acls.flushAllIpv4RoutingRules() + nf = CsNetfilters() + nf.apply_nft_ipv4_rules(self.config.get_nft_ipv4_fw(), "firewall") + acls.flushAllIptablesRules() + + logging.info("Configuring nftables IPv4 ACL rules %s" % self.config.get_nft_ipv4_acl()) + acls.flushAllIpv4RoutingACLRules() + nf = CsNetfilters() + nf.apply_nft_ipv4_rules(self.config.get_nft_ipv4_acl(), "acl") + + logging.info("Configuring nftables IPv6 ACL rules %s" % self.config.get_ipv6_acl()) nf = CsNetfilters() nf.apply_ip6_rules(self.config.get_ipv6_acl(), "acl") - logging.info("Configuring nftables IPv6 rules %s" % self.config.get_ipv6_fw()) + logging.info("Configuring nftables IPv6 firewall rules %s" % self.config.get_ipv6_fw()) nf = CsNetfilters() nf.apply_ip6_rules(self.config.get_ipv6_fw(), "firewall") @@ -1349,6 +1600,8 @@ class IpTablesExecutor: CsHelper.save_iptables("iptables-save", "/etc/iptables/rules.v4") CsHelper.save_iptables("ip6tables-save", "/etc/iptables/rules.v6") + # Save nftables configuration + CsHelper.save_iptables("nft list ruleset", "/etc/iptables/rules.nftables") def main(argv): # The file we are currently processing, if it is "cmd_line.json" everything will be processed. @@ -1371,6 +1624,7 @@ def main(argv): config.address().process() databag_map = OrderedDict([("guest_network", {"process_iptables": True, "executor": [CsVpcGuestNetwork("guestnetwork", config)]}), + ("bgp_peers", {"process_iptables": False, "executor": [CsBgpPeers("bgppeers", config)]}), ("ip_aliases", {"process_iptables": True, "executor": []}), ("vm_password", {"process_iptables": False, "executor": [CsPassword("vmpassword", config)]}), ("vm_metadata", {"process_iptables": False, "executor": [CsVmMetadata('vmdata', config)]}), diff --git a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py index 1b3d1a76387..0d1c31ac14b 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py @@ -307,6 +307,8 @@ class CsIP: self.fw = config.get_fw() self.cl = config.cmdline() self.config = config + self.nft_ipv4_fw = config.get_nft_ipv4_fw() + self.nft_ipv4_acl = config.get_nft_ipv4_acl() def setAddress(self, address): self.address = address @@ -341,7 +343,7 @@ class CsIP: interfaces = [CsInterface(address, self.config)] CsHelper.reconfigure_interfaces(self.cl, interfaces) - if self.get_type() in ['public']: + if self.get_type() in ['public'] and not self.config.is_routed(): self.set_mark() if 'gateway' in self.address: @@ -351,7 +353,7 @@ class CsIP: self.post_config_change("add") '''For isolated/redundant and dhcpsrvr routers, call this method after the post_config is complete ''' - if not self.config.is_vpc(): + if self.get_type() in ["control"]: self.setup_router_control() if self.config.is_vpc() or self.cl.is_redundant(): @@ -399,7 +401,19 @@ class CsIP: return self.address['mtu'] return CsIP.DEFAULT_MTU + def setup_router_control_routing(self): + if self.config.is_vpc(): + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': 'iifname "eth0" tcp dport 3922 ct state established,new counter accept'}) + else: + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': 'iifname "eth1" tcp dport 3922 ct state established,new counter accept'}) + def setup_router_control(self): + if self.config.is_routed(): + self.setup_router_control_routing() + return + if self.config.is_vpc(): return @@ -412,7 +426,7 @@ class CsIP: self.fw.append(["filter", "", "-P FORWARD DROP"]) def fw_router(self): - if self.config.is_vpc(): + if self.config.is_vpc() or self.config.is_routed(): return self.fw.append(["mangle", "front", "-A PREROUTING " + @@ -500,7 +514,7 @@ class CsIP: self.fw.append(['', '', '-A NETWORK_STATS -i eth2 ! -o eth0 -p tcp']) def fw_vpcrouter(self): - if not self.config.is_vpc(): + if not self.config.is_vpc() or self.config.is_routed(): return self.fw.append(["filter", "", "-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT"]) @@ -603,6 +617,76 @@ class CsIP: self.fw.append(["filter", "", "-P INPUT DROP"]) self.fw.append(["filter", "", "-P FORWARD DROP"]) + def fw_router_routing(self): + if self.config.is_vpc() or not self.config.is_routed(): + return + + # Add default rules for INPUT chain + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname lo counter accept"}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname eth2 ct state related,established counter accept"}) + # Add default rules for FORWARD chain + self.nft_ipv4_fw.append({'type': "", 'chain': 'FORWARD', + 'rule': 'iifname "eth2" oifname "eth0" ct state related,established counter accept'}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'FORWARD', + 'rule': 'iifname "eth0" oifname "eth0" ct state new counter accept'}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'FORWARD', + 'rule': 'iifname "eth0" oifname "eth0" ct state related,established counter accept'}) + + if self.get_type() in ["guest"]: + guestNetworkCidr = self.address['network'] + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ct state related,established counter accept" % self.dev}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s udp dport 67 counter accept" % self.dev}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s tcp dport 53 counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s udp dport 53 counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s tcp dport 80 ct state new counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s tcp dport 443 ct state new counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s tcp dport 8080 ct state new counter accept" % (self.dev, guestNetworkCidr)}) + + def fw_vpcrouter_routing(self): + if not self.config.is_vpc() or not self.config.is_routed(): + return + + # Add default rules for INPUT chain for VPC + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname lo counter accept"}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname eth1 ct state related,established counter accept"}) + + if self.get_type() in ["guest"]: + guestNetworkCidr = self.address['network'] + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ct state related,established counter accept" % self.dev}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s udp dport 67 counter accept" % self.dev}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s tcp dport 53 counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s udp dport 53 counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s tcp dport 80 ct state new counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s tcp dport 443 ct state new counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', + 'rule': "iifname %s ip saddr %s tcp dport 8080 ct state new counter accept" % (self.dev, guestNetworkCidr)}) + + # Add default rules for FORWARD chain for VPC tiers + self.nft_ipv4_acl.append({'type': "", 'chain': 'FORWARD', + 'rule': "oifname %s ip daddr %s ct state related,established counter accept" % (self.dev, guestNetworkCidr)}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'FORWARD', + 'rule': "iifname %s oifname %s ct state new counter accept" % (self.dev, self.dev)}) + self.nft_ipv4_acl.append({'type': "", 'chain': 'FORWARD', + 'rule': "iifname %s oifname %s ct state related,established counter accept" % (self.dev, self.dev)}) + + def post_config_change(self, method): route = CsRoute() tableName = "Table_" + self.dev @@ -649,6 +733,8 @@ class CsIP: self.fw_router() self.fw_vpcrouter() + self.fw_router_routing() + self.fw_vpcrouter_routing() cmdline = self.config.cmdline() @@ -688,7 +774,7 @@ class CsIP: elif method == "delete": CsPasswdSvc(self.get_gateway() + "," + self.address['public_ip']).stop() - if self.get_type() == "public" and self.config.is_vpc() and method == "add": + if self.get_type() == "public" and self.config.is_vpc() and method == "add" and not self.config.is_routed(): if self.address["source_nat"]: vpccidr = cmdline.get_vpccidr() self.fw.append( diff --git a/systemvm/debian/opt/cloud/bin/cs/CsBgpPeers.py b/systemvm/debian/opt/cloud/bin/cs/CsBgpPeers.py new file mode 100755 index 00000000000..137279b74a9 --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/cs/CsBgpPeers.py @@ -0,0 +1,119 @@ +#!/usr/bin/python +# -- coding: utf-8 -- +# 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. + +import logging +from . import CsHelper +from .CsDatabag import CsDataBag +from .CsFile import CsFile + +FRR_DIR = "/etc/frr/" +FRR_DAEMONS = "/etc/frr/daemons" +FRR_CONFIG = "/etc/frr/frr.conf" + + +class CsBgpPeers(CsDataBag): + + def process(self): + logging.info("Processing CsBgpPeers file ==> %s" % self.dbag) + + if self.config.is_vpc(): + self.public_ip = self.cl.get_source_nat_ip() + else: + self.public_ip = self.cl.get_eth2_ip() + + self.peers = {} + for item in self.dbag: + if item == "id": + continue + self._process_dbag_item(self.dbag[item]) + + restart_frr = False + + CsHelper.mkdir(FRR_DIR, 0o755, False) + self.frr_daemon = CsFile(FRR_DAEMONS) + self.frr_daemon.replaceIfFound("bgpd=no", "bgpd=yes") + if self.frr_daemon.commit(): + restart_frr = True + + self.frr_conf = CsFile(FRR_CONFIG) + self.frr_conf.repopulate() + self._pre_set() + self._process_peers() + self._post_set() + if self.frr_conf.commit(): + restart_frr = True + + if restart_frr: + CsHelper.execute("systemctl enable frr") + CsHelper.execute("systemctl restart frr") + + def _process_dbag_item(self, item): + as_number = item['network_as_number'] + if as_number not in self.peers.keys(): + self.peers[as_number] = {} + self.peers[as_number]['ip4_peers'] = [] + self.peers[as_number]['ip6_peers'] = [] + if 'ip4_address' in item and 'guest_ip4_cidr' in item: + self.peers[as_number]['ip4_peers'].append(item) + if 'ip6_address' in item and 'guest_ip6_cidr' in item: + self.peers[as_number]['ip6_peers'].append(item) + + def _pre_set(self): + self.frr_conf.add("frr version 6.0") + self.frr_conf.add("frr defaults traditional") + self.frr_conf.add("hostname {}".format(CsHelper.get_hostname())) + self.frr_conf.add("service integrated-vtysh-config") + self.frr_conf.add("ip nht resolve-via-default") + return + + def _process_peers(self): + for as_number in self.peers.keys(): + self.frr_conf.add("router bgp {}".format(as_number)) + self.frr_conf.add(" bgp router-id {}".format(self.public_ip)) + if self.peers[as_number]['ip6_peers']: + self.frr_conf.add(" bgp default ipv6-unicast") + for ip4_peer in self.peers[as_number]['ip4_peers']: + self.frr_conf.add(" neighbor {} remote-as {}".format(ip4_peer['ip4_address'], ip4_peer['peer_as_number'])) + if 'peer_password' in ip4_peer: + self.frr_conf.add(" neighbor {} password {}".format(ip4_peer['ip4_address'], ip4_peer['peer_password'])) + if 'details' in ip4_peer: + if 'EBGP_MultiHop' in ip4_peer['details']: + self.frr_conf.add(" neighbor {} ebgp-multihop {}".format(ip4_peer['ip4_address'], ip4_peer['details']['EBGP_MultiHop'])) + for ip6_peer in self.peers[as_number]['ip6_peers']: + self.frr_conf.add(" neighbor {} remote-as {}".format(ip6_peer['ip6_address'], ip6_peer['peer_as_number'])) + if 'peer_password' in ip6_peer: + self.frr_conf.add(" neighbor {} password {}".format(ip6_peer['ip6_address'], ip6_peer['peer_password'])) + if 'details' in ip6_peer: + if 'EBGP_MultiHop' in ip6_peer['details']: + self.frr_conf.add(" neighbor {} ebgp-multihop {}".format(ip6_peer['ip6_address'], ip6_peer['details']['EBGP_MultiHop'])) + if self.peers[as_number]['ip4_peers']: + self.frr_conf.add(" address-family ipv4 unicast") + ip4_cidrs = set({ip4_peer['guest_ip4_cidr'] for ip4_peer in self.peers[as_number]['ip4_peers']}) + for ip4_cidr in ip4_cidrs: + self.frr_conf.add(" network {}".format(ip4_cidr)) + self.frr_conf.add(" exit-address-family") + if self.peers[as_number]['ip6_peers']: + self.frr_conf.add(" address-family ipv6 unicast") + ip6_cidrs = set({ip6_peer['guest_ip6_cidr'] for ip6_peer in self.peers[as_number]['ip6_peers']}) + for ip6_cidr in ip6_cidrs: + self.frr_conf.add(" network {}".format(ip6_cidr)) + self.frr_conf.add(" exit-address-family") + + def _post_set(self): + self.frr_conf.add("line vty") diff --git a/systemvm/debian/opt/cloud/bin/cs/CsConfig.py b/systemvm/debian/opt/cloud/bin/cs/CsConfig.py index bfc5c134998..a17f6ac4aa5 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsConfig.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsConfig.py @@ -33,8 +33,14 @@ class CsConfig(object): def __init__(self): self.fw = [] - self.ipv6_acl = [] - self.ipv6_fw = [] + # Each nftables rule contains + # 1. type. If not set, it is a rule. Another valid option is "chain". + # 2. chain. The chain of the rule (if type is not set), or the name of chain (if type is "chain"). + # 3. rule. The configuration of the rule or chain. + self.nft_ipv4_acl = [] + self.nft_ipv4_fw = [] + self.nft_ipv6_acl = [] + self.nft_ipv6_fw = [] def set_address(self): self.ips = CsAddress("ips", self) @@ -63,11 +69,17 @@ class CsConfig(object): def get_fw(self): return self.fw + def get_nft_ipv4_acl(self): + return self.nft_ipv4_acl + + def get_nft_ipv4_fw(self): + return self.nft_ipv4_fw + def get_ipv6_acl(self): - return self.ipv6_acl + return self.nft_ipv6_acl def get_ipv6_fw(self): - return self.ipv6_fw + return self.nft_ipv6_fw def get_logger(self): return self.__LOG_FILE @@ -81,6 +93,9 @@ class CsConfig(object): def is_router(self): return self.cl.get_type() == 'router' + def is_routed(self): + return self.cmdline().idata().get('is_routed', 'false') == 'true' + def is_dhcp(self): return self.cl.get_type() == 'dhcpsrvr' diff --git a/systemvm/debian/opt/cloud/bin/cs/CsDatabag.py b/systemvm/debian/opt/cloud/bin/cs/CsDatabag.py index a6e84bb0b8c..abbf23b4cdf 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsDatabag.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsDatabag.py @@ -107,6 +107,18 @@ class CsCmdLine(CsDataBag): else: return "unknown" + def get_eth0_ip(self): + if "eth0ip" in self.idata(): + return self.idata()['eth0ip'] + else: + return False + + def get_cidr_size(self): + if "cidrsize" in self.idata(): + return self.idata()['cidrsize'] + else: + return False + def get_eth2_ip(self): if "eth2ip" in self.idata(): return self.idata()['eth2ip'] diff --git a/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py b/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py index 41b8b644186..615c61d98e3 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py @@ -40,7 +40,7 @@ class CsGuestNetwork: return self.config.get_dns() dns = [] - if 'router_guest_gateway' in self.data and not self.config.use_extdns() and 'is_vr_guest_gateway' not in self.data: + if 'router_guest_gateway' in self.data and not self.config.use_extdns() and ('is_vr_guest_gateway' not in self.data or not self.data['is_vr_guest_gateway']): dns.append(self.data['router_guest_gateway']) if 'dns' in self.data: diff --git a/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py b/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py index c753350eaf5..d9950c9cd02 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py @@ -233,6 +233,57 @@ class CsNetfilters(object): CsHelper.execute("nft add rule %s %s %s icmpv6 type { echo-request, echo-reply, \ nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept" % (address_family, table, chain)) + def add_ip4_chain(self, address_family, table, chain, hook, action): + chain_policy = "" + if hook: + chain_policy = "type filter hook %s priority 0;" % hook + if chain_policy and action: + chain_policy = "%s policy %s;" % (chain_policy, action) + CsHelper.execute("nft add chain %s %s %s '{ %s }'" % (address_family, table, chain, chain_policy)) + if hook == "input" or hook == "output": + CsHelper.execute("nft add rule %s %s %s icmp type { echo-request, echo-reply } accept" % (address_family, table, chain)) + + def apply_nft_ipv4_rules(self, rules, type): + if len(rules) == 0: + return + + address_family = 'ip' + table = 'ip4_firewall' + default_chains = [ + {"chain": "INPUT", "hook": "input", "action": "drop"}, + {"chain": "FORWARD", "hook": "forward", "action": "accept"}, + {"chain": "OUTPUT", "hook": "output", "action": "accept"} + ] + if type == "acl": + table = 'ip4_acl' + default_chains = [ + {"chain": "INPUT", "hook": "input", "action": "drop"}, + {"chain": "FORWARD", "hook": "forward", "action": "accept"}, + {"chain": "OUTPUT", "hook": "output", "action": "accept"} + ] + tables = CsHelper.execute("nft list tables %s | grep %s" % (address_family, table)) + if any(table in t for t in tables): + CsHelper.execute("nft delete table %s %s" % (address_family, table)) + CsHelper.execute("nft add table %s %s" % (address_family, table)) + for chain in default_chains: + self.add_ip4_chain(address_family, table, chain['chain'], chain['hook'], chain['action']) + for fw in rules: + chain = fw['chain'] + type = fw['type'] + rule = None + if 'rule' in fw: + rule = fw['rule'] + if type == "chain": + hook = "" + if "output" in chain: + hook = "output" + elif "input" in chain: + hook = "input" + self.add_ip4_chain(address_family, table, chain, hook, rule) + else: + logging.info("Add: rule=%s in address_family=%s table=%s, chain=%s", rule, address_family, table, chain) + CsHelper.execute("nft add rule %s %s %s %s" % (address_family, table, chain, rule)) + def apply_ip6_rules(self, rules, type): if len(rules) == 0: return diff --git a/systemvm/debian/opt/cloud/bin/cs_bgppeers.py b/systemvm/debian/opt/cloud/bin/cs_bgppeers.py new file mode 100755 index 00000000000..b284a74f870 --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/cs_bgppeers.py @@ -0,0 +1,26 @@ +# -- coding: utf-8 -- +# 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. + + +def merge(dbag, peers): + dbag.clear() + for peer in peers['peers']: + if 'peer_id' in peer: + key = "{}-{}".format(peer['peer_id'], peer['network_id']) + dbag[key] = peer + return dbag diff --git a/systemvm/debian/opt/cloud/bin/merge.py b/systemvm/debian/opt/cloud/bin/merge.py index 1d320395d74..712d15f1c06 100755 --- a/systemvm/debian/opt/cloud/bin/merge.py +++ b/systemvm/debian/opt/cloud/bin/merge.py @@ -37,6 +37,7 @@ import cs_site2sitevpn import cs_remoteaccessvpn import cs_vpnusers import cs_staticroutes +import cs_bgppeers class DataBag: @@ -132,6 +133,8 @@ class updateDataBag: dbag = self.process_vpnusers(self.db.getDataBag()) elif self.qFile.type == 'staticroutes': dbag = self.process_staticroutes(self.db.getDataBag()) + elif self.qFile.type == 'bgppeers': + dbag = self.process_bgppeers(self.db.getDataBag()) elif self.qFile.type == 'ipaliases': self.db.setKey('ips') self.db.load() @@ -192,6 +195,9 @@ class updateDataBag: def process_staticroutes(self, dbag): return cs_staticroutes.merge(dbag, self.qFile.data) + def process_bgppeers(self, dbag): + return cs_bgppeers.merge(dbag, self.qFile.data) + def processVMpassword(self, dbag): return cs_vmp.merge(dbag, self.qFile.data) diff --git a/systemvm/debian/opt/cloud/bin/setup/cloud-early-config b/systemvm/debian/opt/cloud/bin/setup/cloud-early-config index 47035920645..6e5cba93e5b 100755 --- a/systemvm/debian/opt/cloud/bin/setup/cloud-early-config +++ b/systemvm/debian/opt/cloud/bin/setup/cloud-early-config @@ -99,6 +99,12 @@ patch() { cleanup() { rm -rf /var/cache/cloud/agent.zip mv /var/cache/cloud/cloud-scripts.tgz /usr/share/cloud/cloud-scripts.tgz + + CMDLINE=/var/cache/cloud/cmdline + export TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE) + if [ "$TYPE" != "consoleproxy" ] && [ "$TYPE" != "secstorage" ]; then + rm -rf /usr/local/cloud/systemvm/ + fi } start() { diff --git a/systemvm/debian/opt/cloud/bin/setup/common.sh b/systemvm/debian/opt/cloud/bin/setup/common.sh index 77297f02c2c..5156d77a6a6 100755 --- a/systemvm/debian/opt/cloud/bin/setup/common.sh +++ b/systemvm/debian/opt/cloud/bin/setup/common.sh @@ -701,7 +701,7 @@ setup_ntp() { } routing_svcs() { - echo "haproxy apache2" > /var/cache/cloud/enabled_svcs + echo "haproxy apache2 frr" > /var/cache/cloud/enabled_svcs echo "cloud nfs-common portmap" > /var/cache/cloud/disabled_svcs if [ "$RROUTER" -eq "1" ] then @@ -801,6 +801,9 @@ parse_cmd_line() { ip6firewall) export IP6_FIREWALL=$VALUE ;; + is_routed) + export IS_ROUTED=$VALUE + ;; domain) export DOMAIN=$VALUE ;; diff --git a/systemvm/debian/opt/cloud/bin/setup/postinit.sh b/systemvm/debian/opt/cloud/bin/setup/postinit.sh index f713ca42086..801770fcc83 100755 --- a/systemvm/debian/opt/cloud/bin/setup/postinit.sh +++ b/systemvm/debian/opt/cloud/bin/setup/postinit.sh @@ -27,6 +27,12 @@ log_it() { systemctl restart systemd-journald # Restore the persistent iptables nat, rules and filters for IPv4 and IPv6 if they exist +nftables="/etc/iptables/rules.nftables" +if [ -e $nftables ] +then + nft -f $nftables +fi + ipv4="/etc/iptables/rules.v4" if [ -e $ipv4 ] then diff --git a/test/integration/smoke/test_ipv4_routing.py b/test/integration/smoke/test_ipv4_routing.py new file mode 100644 index 00000000000..124be678965 --- /dev/null +++ b/test/integration/smoke/test_ipv4_routing.py @@ -0,0 +1,1673 @@ +# 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. + +""" Test for IPv4 Routed mode""" +import datetime +import logging +import random +import time + +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.base import ZoneIpv4Subnet, Domain, Account, ServiceOffering, NetworkOffering, VpcOffering, Network, \ + Ipv4SubnetForGuestNetwork, VirtualMachine, VPC, NetworkACLList, NetworkACL, RoutingFirewallRule, Template, ASNRange, \ + BgpPeer, Router +from marvin.lib.common import get_domain, get_zone, list_routers, list_hosts +from marvin.lib.utils import get_host_credentials, get_process_status + +from nose.plugins.attrib import attr + +ICMPv4_ALL_TYPES = ("{ echo-reply, destination-unreachable, source-quench, redirect, echo-request, " + "router-advertisement, router-solicitation, time-exceeded, parameter-problem, timestamp-request, " + "timestamp-reply, info-request, info-reply, address-mask-request, address-mask-reply }") +SUBNET_PREFIX = "172.30." +SUBNET_1_PREFIX = SUBNET_PREFIX + str(random.randrange(100, 150)) +SUBNET_2_PREFIX = SUBNET_PREFIX + str(random.randrange(151, 199)) + +VPC_CIDR_PREFIX = "172.31" # .0 to .16 +NETWORK_CIDR_PREFIX = VPC_CIDR_PREFIX + ".100" +NETWORK_CIDR_PREFIX_DYNAMIC = VPC_CIDR_PREFIX + ".101" + +MAX_RETRIES = 30 +WAIT_INTERVAL = 5 + +test_network = None +test_network_vm = None +test_vpc = None +test_vpc_tier = None +test_vpc_vm = None +test_network_acl = None + +START_ASN = 888800 +END_ASN = 888888 +ASN_1 = 900100 + random.randrange(1, 200) +ASN_2 = 900301 + random.randrange(0, 200) +IP4_ADDR_1 = "10.0.53.10" +IP4_ADDR_2 = "10.0.53.11" +PASSWORD_1 = "testpassword1" +PASSWORD_2 = "testpassword2" + +NETWORK_OFFERING = { + "name": "Test Network offering - Routed mode", + "displaytext": "Test Network offering - Routed mode", + "networkmode": "ROUTED", + "guestiptype": "Isolated", + "supportedservices": + "Dhcp,Dns,UserData,Firewall", + "traffictype": "GUEST", + "availability": "Optional", + "egress_policy": "true", + "serviceProviderList": { + "Dhcp": "VirtualRouter", + "Dns": "VirtualRouter", + "UserData": "VirtualRouter", + "Firewall": "VirtualRouter" + } +} + +VPC_OFFERING = { + "name": "Test VPC offering - Routed mode", + "displaytext": "Test VPC offering - Routed mode", + "networkmode": "ROUTED", + "supportedservices": + "Dhcp,Dns,UserData,NetworkACL" +} + +VPC_NETWORK_OFFERING = { + "name": "Test VPC Network offering - Routed mode", + "displaytext": "Test VPC Network offering - Routed mode", + "networkmode": "ROUTED", + "guestiptype": "Isolated", + "supportedservices": + "Dhcp,Dns,UserData,NetworkACL", + "traffictype": "GUEST", + "availability": "Optional", + "serviceProviderList": { + "Dhcp": "VpcVirtualRouter", + "Dns": "VpcVirtualRouter", + "UserData": "VpcVirtualRouter", + "NetworkACL": "VpcVirtualRouter" + } +} + +NETWORK_OFFERING_DYNAMIC = { + "name": "Test Network offering - Dynamic Routed mode", + "displaytext": "Test Network offering - Dynamic Routed mode", + "networkmode": "ROUTED", + "routingmode": "Dynamic", + "guestiptype": "Isolated", + "supportedservices": + "Dhcp,Dns,UserData,Firewall", + "traffictype": "GUEST", + "availability": "Optional", + "egress_policy": "true", + "serviceProviderList": { + "Dhcp": "VirtualRouter", + "Dns": "VirtualRouter", + "UserData": "VirtualRouter", + "Firewall": "VirtualRouter" + } +} + +VPC_OFFERING_DYNAMIC = { + "name": "Test VPC offering - Routed mode", + "displaytext": "Test VPC offering - Routed mode", + "networkmode": "ROUTED", + "routingmode": "Dynamic", + "supportedservices": + "Dhcp,Dns,UserData,NetworkACL" +} + +VPC_NETWORK_OFFERING_DYNAMIC = { + "name": "Test VPC Network offering - Dynamic Routed mode", + "displaytext": "Test VPC Network offering - Dynamic Routed mode", + "networkmode": "ROUTED", + "routingmode": "Dynamic", + "guestiptype": "Isolated", + "supportedservices": + "Dhcp,Dns,UserData,NetworkACL", + "traffictype": "GUEST", + "availability": "Optional", + "serviceProviderList": { + "Dhcp": "VpcVirtualRouter", + "Dns": "VpcVirtualRouter", + "UserData": "VpcVirtualRouter", + "NetworkACL": "VpcVirtualRouter" + } +} +class TestIpv4Routing(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testdata = super(TestIpv4Routing, cls).getClsTestClient() + cls.services = testdata.getParsedTestDataConfig() + cls.apiclient = testdata.getApiClient() + cls.dbclient = testdata.getDbConnection() + cls.hypervisor = testdata.getHypervisorInfo() + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient) + + cls._cleanup = [] + + cls.logger = logging.getLogger("TestIpv4Routing") + cls.stream_handler = logging.StreamHandler() + cls.logger.setLevel(logging.DEBUG) + cls.logger.addHandler(cls.stream_handler) + + # 0. register template + cls.template = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()], + zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower()) + cls.template.download(cls.apiclient) + cls._cleanup.append(cls.template) + + # 1.1 create subnet for zone + cls.subnet_1 = ZoneIpv4Subnet.create( + cls.apiclient, + zoneid=cls.zone.id, + subnet=SUBNET_1_PREFIX + ".0/24" + ) + cls._cleanup.append(cls.subnet_1) + + # 1.2 create ASN range for zone + cls.asnrange = ASNRange.create( + cls.apiclient, + zoneid=cls.zone.id, + startasn=START_ASN, + endasn=END_ASN + ) + cls._cleanup.append(cls.asnrange) + + # 2. Create small service offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"] + ) + cls._cleanup.append(cls.service_offering) + + # 3. Create network and vpc offering with routed mode + # 3.1 Network offering for static routing + cls.network_offering_isolated = NetworkOffering.create( + cls.apiclient, + NETWORK_OFFERING + ) + cls._cleanup.append(cls.network_offering_isolated) + cls.network_offering_isolated.update(cls.apiclient, state='Enabled') + + # 3.2 VPC offering for static routing + cls.vpc_offering = VpcOffering.create( + cls.apiclient, + VPC_OFFERING + ) + cls._cleanup.append(cls.vpc_offering) + cls.vpc_offering.update(cls.apiclient, state='Enabled') + + # 3.3 VPC tier offering for static routing + cls.vpc_network_offering = NetworkOffering.create( + cls.apiclient, + VPC_NETWORK_OFFERING + ) + cls._cleanup.append(cls.vpc_network_offering) + cls.vpc_network_offering.update(cls.apiclient, state='Enabled') + + # 3.4 Network offering for dynamic routing + cls.network_offering_dynamic = NetworkOffering.create( + cls.apiclient, + NETWORK_OFFERING_DYNAMIC + ) + cls._cleanup.append(cls.network_offering_dynamic) + cls.network_offering_dynamic.update(cls.apiclient, state='Enabled') + + # 3.5 VPC Network offering for dynamic routing + cls.vpc_network_offering_dynamic = NetworkOffering.create( + cls.apiclient, + VPC_NETWORK_OFFERING_DYNAMIC + ) + cls._cleanup.append(cls.vpc_network_offering_dynamic) + cls.vpc_network_offering_dynamic.update(cls.apiclient, state='Enabled') + + # 4. Create sub-domain + cls.sub_domain = Domain.create( + cls.apiclient, + cls.services["acl"]["domain1"] + ) + cls._cleanup.append(cls.sub_domain) + + # 5. Create regular user + cls.regular_user = Account.create( + cls.apiclient, + cls.services["acl"]["accountD11A"], + domainid=cls.sub_domain.id + ) + cls._cleanup.append(cls.regular_user) + + # 6. Create api clients for regular user + cls.regular_user_user = cls.regular_user.user[0] + cls.regular_user_apiclient = cls.testClient.getUserApiClient( + cls.regular_user_user.username, cls.sub_domain.name + ) + + @classmethod + def tearDownClass(cls): + super(TestIpv4Routing, cls).tearDownClass() + + @classmethod + def message(cls, msg): + cls.logger.debug("====== " + str(datetime.datetime.now()) + " " + msg + " ======") + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.cleanup = [] + + def tearDown(self): + super(TestIpv4Routing, self).tearDown() + + def get_router(self, networkid=None, vpcid=None): + # list router + if vpcid: + list_router_response = list_routers( + self.apiclient, + vpcid=vpcid, + listall="true" + ) + else: + list_router_response = list_routers( + self.apiclient, + networkid=networkid, + listall="true" + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "list routers response should return a valid list" + ) + router = list_router_response[0] + return router + + def run_command_in_router(self, router, command): + # get host of router + hosts = list_hosts( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + id=router.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list host returns a valid list" + ) + host = hosts[0] + + # run command + result = '' + if router.hypervisor.lower() in ('vmware', 'hyperv'): + result = get_process_status( + self.apiclient.connection.mgtSvr, + 22, + self.apiclient.connection.user, + self.apiclient.connection.passwd, + router.linklocalip, + command, + hypervisor=router.hypervisor + ) + else: + try: + host.user, host.passwd = get_host_credentials(self.config, host.ipaddress) + result = get_process_status( + host.ipaddress, + 22, + host.user, + host.passwd, + router.linklocalip, + command + ) + except KeyError: + self.skipTest("Marvin configuration has no host credentials to check router services") + res = str(result) + self.message("VR command (%s) result: (%s)" % (command, res)) + return res + + def rebootRouter(self, router): + try: + Router.reboot( + self.apiclient, + id=router.id + ) + except Exception as e: + self.fail("Failed to reboot the virtual router: %s, %s" % (router.id, e)) + + def createNetworkAclRule(self, rule): + return NetworkACL.create(self.apiclient, + services=rule, + aclid=test_network_acl.id) + + def createIpv4RoutingFirewallRule(self, rule): + return RoutingFirewallRule.create(self.apiclient, + services=rule, + networkid=test_network.id) + + def verifyNftablesRulesInRouter(self, router, rules): + if router.vpcid: + table = "ip4_acl" + else: + table = "ip4_firewall" + for rule in rules: + cmd = "nft list chain ip %s %s" % (table, rule["chain"]) + res = self.run_command_in_router(router, cmd) + if "exists" not in rule or rule["exists"]: + exists = True + else: + exists = False + if exists and not rule["rule"] in res: + self.fail("The nftables rule (%s) should exist but is not found in the VR !!!" % rule["rule"]) + if not exists and rule["rule"] in res: + self.fail("The nftables rule (%s) should not exist but is found in the VR !!!" % rule["rule"]) + self.message("The nftables rules look good so far.") + + def verifyPingFromRouter(self, router, vm, expected=True, retries=2): + while retries > 0: + cmd_ping_vm = "ping -c1 -W1 %s" % vm.ipaddress + try: + result = self.run_command_in_router(router, cmd_ping_vm) + if "0 packets received" in result: + retries = retries - 1 + self.message("No packets received, remaining retries %s" % retries) + if retries > 0: + time.sleep(WAIT_INTERVAL) + else: + self.message("packets are received, looks good") + return + except Exception as ex: + self.fail("Failed to ping vm %s from router %s: %s" % (vm.ipaddress, router.name, ex)) + if retries == 0 and expected: + self.fail("Failed to ping vm %s from router %s, which is expected to work !!!" % (vm.ipaddress, router.name)) + if retries > 0 and not expected: + self.fail("ping vm %s from router %s works, however it is unexpected !!!" % (vm.ipaddress, router.name)) + + def verifyFrrConf(self, router, configs): + cmd = "cat /etc/frr/frr.conf" + res = self.run_command_in_router(router, cmd) + for config in configs: + if "exists" not in config or config["exists"]: + exists = True + else: + exists = False + if exists and not config["config"] in res: + self.fail("The frr config (%s) should exist but is not found in the VR !!!" % config["config"]) + if not exists and config["config"] in res: + self.fail("The frr config (%s) should not exist but is found in the VR !!!" % config["config"]) + self.message("The frr config look good so far.") + + @attr(tags=['advanced'], required_hardware=False) + def test_01_zone_subnet(self): + """ Test for subnet for zone""" + """ + # 1. Create subnet + # 2. List subnet + # 3. Update subnet + # 4. dedicate subnet to domain + # 5. released dedicated subnet + # 6. dedicate subnet to sub-domain/account + # 7. released dedicated subnet + # 8. delete subnet + """ + self.message("Running test_01_zone_subnet") + # 1. Create subnet + self.subnet_2 = ZoneIpv4Subnet.create( + self.apiclient, + zoneid=self.zone.id, + subnet=SUBNET_2_PREFIX + ".0/24" + ) + self.cleanup.append(self.subnet_2) + # 2. List subnet + subnets = ZoneIpv4Subnet.list( + self.apiclient, + id=self.subnet_2.id + ) + self.assertEqual( + isinstance(subnets, list), + True, + "List subnets for zone should return a valid list" + ) + self.assertEqual( + len(subnets) == 1, + True, + "The number of subnets for zone (%s) should be equal to 1" % (len(subnets)) + ) + self.assertEqual( + subnets[0].subnet == SUBNET_2_PREFIX + ".0/24", + True, + "The subnet of subnet for zone (%s) should be equal to %s" % (subnets[0].subnet, SUBNET_2_PREFIX + ".0/24") + ) + # 3. Update subnet + self.subnet_2.update( + self.apiclient, + subnet=SUBNET_2_PREFIX + ".0/25" + ) + subnets = ZoneIpv4Subnet.list( + self.apiclient, + id=self.subnet_2.id + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1 and subnets[0].subnet == SUBNET_2_PREFIX + ".0/25", + True, + "The subnet of subnet for zone should be equal to %s" % (SUBNET_2_PREFIX + ".0/25") + ) + # 4. dedicate subnet to domain + ZoneIpv4Subnet.dedicate( + self.apiclient, + id=self.subnet_2.id, + domainid=self.domain.id + ) + subnets = ZoneIpv4Subnet.list( + self.apiclient, + id=self.subnet_2.id + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1 and subnets[0].domainid == self.domain.id, + True, + "The subnet should be dedicated to domain %s" % self.domain.id + ) + # 5. released dedicated subnet + self.subnet_2.release( + self.apiclient + ) + subnets = ZoneIpv4Subnet.list( + self.apiclient, + id=self.subnet_2.id + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1 and not subnets[0].domainid, + True, + "The subnet should not be dedicated to domain %s" % self.domain.id + ) + # 6. dedicate subnet to sub-domain/account + ZoneIpv4Subnet.dedicate( + self.apiclient, + id=self.subnet_2.id, + domainid=self.sub_domain.id, + account=self.regular_user.name + ) + subnets = ZoneIpv4Subnet.list( + self.apiclient, + id=self.subnet_2.id + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1 + and subnets[0].domainid == self.sub_domain.id and subnets[0].account == self.regular_user.name, + True, + "The subnet should be dedicated to account %s" % self.regular_user.name + ) + # 7. released dedicated subnet + self.subnet_2.release( + self.apiclient + ) + subnets = ZoneIpv4Subnet.list( + self.apiclient, + id=self.subnet_2.id + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1 and not subnets[0].domainid, + True, + "The subnet should not be dedicated to account %s" % self.regular_user.name + ) + # 8. delete subnet + self.subnet_2.delete( + self.apiclient + ) + self.cleanup.remove(self.subnet_2) + + @attr(tags=['advanced'], required_hardware=False) + def test_02_create_network_routed_mode_with_specified_cidr(self): + """ Test for guest network with specified cidr""" + """ + # 1. Create Isolated network + # 2. List subnet for network by subnet + # 3. Delete the network + # 4. List subnet for network by subnet. the subnet should be gone as well + """ + self.message("Running test_02_create_network_routed_mode_with_specified_cidr") + + # 1. Create Isolated network + isolated_network = Network.create( + self.apiclient, + self.services["network"], + gateway=NETWORK_CIDR_PREFIX + ".1", + netmask="255.255.255.0", + networkofferingid=self.network_offering_isolated.id, + zoneid=self.zone.id + ) + self.cleanup.append(isolated_network) + + # 2. List subnet for network by subnet + subnets = Ipv4SubnetForGuestNetwork.list( + self.apiclient, + subnet=NETWORK_CIDR_PREFIX + ".0/24" + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1 + and subnets[0].subnet == NETWORK_CIDR_PREFIX + ".0/24" and subnets[0].state == "Allocated", + True, + "The subnet should be added for network %s" % isolated_network.name + ) + + # 3. Delete the network + isolated_network.delete(self.apiclient) + self.cleanup.remove(isolated_network) + + # 4. List subnet for network by subnet. the subnet should be gone as well + network_cidr = subnets[0].subnet + subnets = Ipv4SubnetForGuestNetwork.list( + self.apiclient, + subnet=network_cidr + ) + self.assertEqual( + not isinstance(subnets, list) or len(subnets) == 0, + True, + "The subnet %s should be removed for network %s" % (network_cidr, isolated_network.name) + ) + + @attr(tags=['advanced'], required_hardware=False) + def test_03_create_subnets_for_guest_network(self): + """ Test for subnets for guest network with cidr/cidrsize""" + """ + # 1. Create subnet with cidr for guest network + # 2. List subnets for network + # 3. delete subnet for network + + # 4. Create subnet with cidrsize + # 5. List subnet for network + # 6. delete subnet for network + """ + self.message("Running test_03_create_subnets_for_guest_network") + + # 1. Create subnet with cidr for guest network + subnet_network_1 = Ipv4SubnetForGuestNetwork.create( + self.apiclient, + parentid=self.subnet_1.id, + subnet=SUBNET_1_PREFIX + ".0/26" + ) + self.cleanup.append(subnet_network_1) + + # 2. List subnets for network + subnets = Ipv4SubnetForGuestNetwork.list( + self.apiclient, + subnet=subnet_network_1.subnet + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1, + True, + "The subnet should be created for subnet_network_1 %s" % subnet_network_1.subnet + ) + + # 3. delete subnet for network + subnet_network_1.delete(self.apiclient) + self.cleanup.remove(subnet_network_1) + + # 4. Create subnet with cidrsize + subnet_network_2 = Ipv4SubnetForGuestNetwork.create( + self.apiclient, + parentid=self.subnet_1.id, + cidrsize=26 + ) + self.cleanup.append(subnet_network_2) + # 5. List subnet for network + subnets = Ipv4SubnetForGuestNetwork.list( + self.apiclient, + subnet=subnet_network_2.subnet + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1, + True, + "The subnet should be created for subnet_network_2 %s" % subnet_network_2.subnet + ) + + # 6. delete subnet for network + subnet_network_2.delete(self.apiclient) + self.cleanup.remove(subnet_network_2) + + @attr(tags=['advanced'], required_hardware=False) + def test_04_create_isolated_network_routed_mode_with_cidrsize(self): + """ Test for subnet and guest network with cidrsize""" + """ + # 1. Create Isolated network with cidrsize + # 2. List subnet for network by networkid + # 3. Delete the network + # 4. List subnet for network by networkid, it should be removed + """ + self.message("Running test_04_create_isolated_network_routed_mode_with_cidrsize") + + # 1. Create Isolated network with cidrsize + isolated_network = Network.create( + self.apiclient, + self.services["network"], + networkofferingid=self.network_offering_isolated.id, + zoneid=self.zone.id, + cidrsize=26 + ) + self.cleanup.append(isolated_network) + + # 2. List subnet for network by networkid + subnets = Ipv4SubnetForGuestNetwork.list( + self.apiclient, + networkid=isolated_network.id + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1 + and subnets[0].networkid == isolated_network.id and subnets[0].state == "Allocated", + True, + "The subnet should be created for isolated_network %s" % isolated_network.name + ) + + # 3. Delete the network + isolated_network.delete(self.apiclient) + self.cleanup.remove(isolated_network) + + # 4. List subnet for network by network cidr, it should be removed + network_cidr = subnets[0].subnet + subnets = Ipv4SubnetForGuestNetwork.list( + self.apiclient, + subnet=network_cidr + ) + self.assertEqual( + not isinstance(subnets, list) or len(subnets) == 0, + True, + "The subnet should be removed for isolated_network %s" % isolated_network.name + ) + + @attr(tags=['advanced'], required_hardware=False) + def test_05_create_vpc_routed_mode_with_cidrsize(self): + """ Test for Routed VPC with cidrsize""" + """ + # 1. Create VPC with cidrsize + # 2. List subnet for network by vpcid + # 3. Delete the VPC + # 4. List subnet for network by vpcid, it should be removed + """ + self.message("Running test_05_create_vpc_routed_mode_with_cidrsize") + + # 1. Create VPC with cidrsize + del self.services["vpc"]["cidr"] + vpc = VPC.create(self.apiclient, + self.services["vpc"], + vpcofferingid=self.vpc_offering.id, + zoneid=self.zone.id, + cidrsize=26, + start=False + ) + self.cleanup.append(vpc) + + # 2. List subnet for network by networkid + subnets = Ipv4SubnetForGuestNetwork.list( + self.apiclient, + vpcid=vpc.id + ) + self.assertEqual( + isinstance(subnets, list) and len(subnets) == 1 + and subnets[0].vpcid == vpc.id and subnets[0].state == "Allocated", + True, + "The subnet should be created for vpc %s" % vpc.name + ) + + # 3. Delete the VPC + vpc.delete(self.apiclient) + self.cleanup.remove(vpc) + + # 4. List subnet for network by vpc cidr, it should be removed + vpc_cidr = subnets[0].subnet + subnets = Ipv4SubnetForGuestNetwork.list( + self.apiclient, + subnet=vpc_cidr + ) + self.assertEqual( + not isinstance(subnets, list) or len(subnets) == 0, + True, + "The subnet should be removed for vpc %s" % vpc.name + ) + + @attr(tags=['advanced'], required_hardware=False) + def test_06_isolated_network_with_routed_mode(self): + """ Test for Isolated Network with Routed mode""" + """ + # 1. Create Isolated network + # 2. Create VM in the network + """ + self.message("Running test_06_isolated_network_with_routed_mode") + + # 1. Create Isolated network + global test_network + test_network = Network.create( + self.apiclient, + self.services["network"], + networkofferingid=self.network_offering_isolated.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + gateway=NETWORK_CIDR_PREFIX + ".1", + netmask="255.255.255.0" + ) + self._cleanup.append(test_network) + + # 2. Create VM in the network + global test_network_vm + test_network_vm = VirtualMachine.create( + self.regular_user_apiclient, + self.services["virtual_machine"], + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + networkids=test_network.id, + serviceofferingid=self.service_offering.id, + templateid=self.template.id) + self._cleanup.append(test_network_vm) + + @attr(tags=['advanced'], required_hardware=False) + def test_07_vpc_and_tier_with_routed_mode(self): + """ Test for VPC/tier with Routed mode""" + """ + # 1. Create VPC + # 2. Create Network ACL (egress = Deny, ingress = Deny) + # 3. Create VPC tier with Network ACL in the VPC + # 4. Create VM in the VPC tier + """ + self.message("Running test_07_vpc_and_tier_with_routed_mode") + + # 1. Create VPC + self.services["vpc"]["cidr"] = VPC_CIDR_PREFIX + ".0.0/22" + global test_vpc + test_vpc = VPC.create(self.apiclient, + self.services["vpc"], + vpcofferingid=self.vpc_offering.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + account=self.regular_user.name, + start=False + ) + self._cleanup.append(test_vpc) + + # 2. Create Network ACL (egress = Deny, ingress = Deny) + global test_network_acl + test_network_acl = NetworkACLList.create(self.apiclient, + services={}, + name="test-network-acl", + description="test-network-acl", + vpcid=test_vpc.id + ) + + # 3. Create VPC tier with Network ACL in the VPC + global test_vpc_tier + test_vpc_tier = Network.create(self.regular_user_apiclient, + self.services["network"], + networkofferingid=self.vpc_network_offering.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + vpcid=test_vpc.id, + gateway=VPC_CIDR_PREFIX + ".1.1", + netmask="255.255.255.0", + aclid=test_network_acl.id + ) + self._cleanup.append(test_vpc_tier) + + # 4. Create VM in the VPC tier + global test_vpc_vm + test_vpc_vm = VirtualMachine.create( + self.regular_user_apiclient, + self.services["virtual_machine"], + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + networkids=test_vpc_tier.id, + serviceofferingid=self.service_offering.id, + templateid=self.template.id) + self._cleanup.append(test_vpc_vm) + + @attr(tags=['advanced'], required_hardware=False) + def test_08_vpc_and_tier_failed_cases(self): + """ Test for VPC/tier with Routed mode (some failed cases)""" + """ + # 1. create VPC with Routed mode + # 2. create network offering with NATTED mode, create vpc tier, it should fail + # 3. create vpc tier not in the vpc cidr, it should fail + """ + + self.message("Running test_08_vpc_and_tier_failed_cases") + + # 1. Create VPC + self.services["vpc"]["cidr"] = VPC_CIDR_PREFIX + ".8.0/22" + test_vpc_2 = VPC.create(self.apiclient, + self.services["vpc"], + vpcofferingid=self.vpc_offering.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + account=self.regular_user.name, + start=False + ) + self.cleanup.append(test_vpc_2) + + # 2. create network offering with NATTED mode, create vpc tier, it should fail + nw_offering_isolated_vpc = NetworkOffering.create( + self.apiclient, + self.services["nw_offering_isolated_vpc"] + ) + self.cleanup.append(nw_offering_isolated_vpc) + nw_offering_isolated_vpc.update(self.apiclient, state='Enabled') + try: + test_vpc_tier_2 = Network.create(self.regular_user_apiclient, + self.services["network"], + networkofferingid=nw_offering_isolated_vpc.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + vpcid=test_vpc_2.id, + gateway=VPC_CIDR_PREFIX + ".1.1", + netmask="255.255.255.0" + ) + self.cleanup.append(test_vpc_tier_2) + self.fail("Created vpc network successfully, but expected to fail") + except Exception as ex: + self.message("Failed to create vpc network due to %s, which is expected behaviour" % ex) + + # 3. create vpc tier not in the vpc cidr, it should fail + try: + test_vpc_tier_3 = Network.create(self.regular_user_apiclient, + self.services["network"], + networkofferingid=self.vpc_network_offering.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + vpcid=test_vpc_2.id, + gateway=VPC_CIDR_PREFIX + ".31.1", + netmask="255.255.255.0" + ) + self.cleanup.append(test_vpc_tier_3) + self.fail("Created vpc network successfully, but expected to fail") + except Exception as ex: + self.message("Failed to create vpc network due to %s, which is expected behaviour" % ex) + + @attr(tags=['advanced'], required_hardware=False) + def test_09_connectivity_between_network_and_vpc_tier(self): + """ Test for connectivity between VMs in the Isolated Network and VPC/tier""" + """ + # 0. Get static routes of Network/VPC + # 1. Add static routes in VRs manually + + # 2. Test VM2 in VR1-Network (ping/ssh should fail) + # 3. Test VM1 in VR2-VPC (ping/ssh should fail) + + # 4. Create Ingress rules in Network ACL for VPC + # 5. Create Egress rules in Network ACL for VPC + # 6. Test VM2 in VR1-Network (ping/ssh should succeed) + # 7. Test VM1 in VR2-VPC (ping/ssh should fail) + + # 8. Create IPv4 firewalls for Isolated network + # 9. Test VM2 in VR1-Network (ping/ssh should succeed) + # 10. Test VM1 in VR2-VPC (ping/ssh should succeed) + + # 11. Delete Network ACL rules for VPC + # 12. Delete IPv4 firewall rules for Network + # 13. Test VM2 in VR1-Network (ping/ssh should fail) + # 14. Test VM1 in VR2-VPC (ping/ssh should fail) + + """ + self.message("Running test_09_connectivity_between_network_and_vpc_tier") + + # 0. Get static routes of Network/VPC + network_ip4routes = [] + if test_network: + network_ip4routes = Network.list( + self.apiclient, + id=test_network.id, + listall=True + )[0].ip4routes + else: + self.skipTest("test_network is not created") + + vpc_ip4routes = [] + if test_vpc: + vpc_ip4routes = VPC.list( + self.apiclient, + id=test_vpc.id, + listall=True + )[0].ip4routes + else: + self.skipTest("test_vpc is not created") + + network_router = self.get_router(networkid=test_network.id) + vpc_router = self.get_router(vpcid=test_vpc.id) + + # Test VM1 in VR1-Network (wait until ping works) + self.verifyPingFromRouter(network_router, test_network_vm, retries=MAX_RETRIES) + # Test VM2 in VR2-VPC (wait until ping works) + self.verifyPingFromRouter(vpc_router, test_vpc_vm, retries=MAX_RETRIES) + + # 1. Add static routes in VRs manually + if not network_router or not vpc_router: + self.skipTest("network_router (%s) or vpc_router (%s) does not exist" % (network_router, vpc_router)) + for ip4route in network_ip4routes: + self.run_command_in_router(vpc_router, "ip route add %s via %s" % (ip4route.subnet, ip4route.gateway)) + for ip4route in vpc_ip4routes: + self.run_command_in_router(network_router, "ip route add %s via %s" % (ip4route.subnet, ip4route.gateway)) + + # 2. Test VM2 in VR1-Network (ping/ssh should fail) + self.verifyPingFromRouter(network_router, test_vpc_vm, expected=False) + # 3. Test VM1 in VR2-VPC (ping/ssh should fail) + self.verifyPingFromRouter(vpc_router, test_network_vm, expected=False) + + vpc_router_rules = [{"chain": "FORWARD", + "rule": "ip daddr %s jump eth2_ingress_policy" % test_vpc_tier.cidr}, + {"chain": "FORWARD", + "rule": "ip saddr %s jump eth2_egress_policy" % test_vpc_tier.cidr}] + vpc_acl_rules = [] + # 4. Create Ingress rules in Network ACL for VPC + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = test_network.cidr + rule["protocol"] = "icmp" + rule["icmptype"] = -1 + rule["icmpcode"] = -1 + vpc_acl_rules.append(self.createNetworkAclRule(rule)) + vpc_router_rules.append({"chain": "eth2_ingress_policy", + "rule": "ip saddr %s icmp type %s accept" % (test_network.cidr, ICMPv4_ALL_TYPES)}) + self.verifyNftablesRulesInRouter(vpc_router, vpc_router_rules) + + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = test_network.cidr + rule["protocol"] = "tcp" + rule["startport"] = 22 + rule["endport"] = 22 + vpc_acl_rules.append(self.createNetworkAclRule(rule)) + vpc_router_rules.append({"chain": "eth2_ingress_policy", + "rule": "ip saddr %s tcp dport 22 accept" % test_network.cidr}) + self.verifyNftablesRulesInRouter(vpc_router, vpc_router_rules) + + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = network_router.publicip + "/32" + rule["protocol"] = "icmp" + rule["icmptype"] = -1 + rule["icmpcode"] = -1 + vpc_acl_rules.append(self.createNetworkAclRule(rule)) + vpc_router_rules.append({"chain": "eth2_ingress_policy", + "rule": "ip saddr %s icmp type %s accept" % (network_router.publicip, ICMPv4_ALL_TYPES)}) + self.verifyNftablesRulesInRouter(vpc_router, vpc_router_rules) + + # 5. Create Egress rules in Network ACL for VPC + rule = {} + rule["traffictype"] = "Egress" + rule["protocol"] = "icmp" + rule["icmptype"] = -1 + rule["icmpcode"] = -1 + vpc_acl_rules.append(self.createNetworkAclRule(rule)) + vpc_router_rules.append({"chain": "eth2_egress_policy", + "rule": "ip daddr 0.0.0.0/0 icmp type %s accept" % ICMPv4_ALL_TYPES}) + self.verifyNftablesRulesInRouter(vpc_router, vpc_router_rules) + + # 6. Test VM2 in VR1-Network (ping/ssh should succeed) + self.verifyPingFromRouter(network_router, test_vpc_vm, expected=True) + # 7. Test VM1 in VR2-VPC (ping/ssh should fail) + self.verifyPingFromRouter(vpc_router, test_network_vm, expected=False) + + network_router_rules = [{"chain": "FORWARD", + "rule": "ip daddr %s jump fw_chain_ingress" % test_network.cidr}, + {"chain": "FORWARD", + "rule": "ip saddr %s jump fw_chain_egress" % test_network.cidr}] + network_routing_firewall_rules = [] + # 8. Create IPv4 firewalls for Isolated network + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = test_vpc.cidr + rule["protocol"] = "icmp" + rule["icmptype"] = -1 + rule["icmpcode"] = -1 + network_routing_firewall_rules.append(self.createIpv4RoutingFirewallRule(rule)) + network_router_rules.append({"chain": "fw_chain_ingress", + "rule": "ip saddr %s ip daddr 0.0.0.0/0 icmp type %s accept" % (test_vpc.cidr, ICMPv4_ALL_TYPES)}) + self.verifyNftablesRulesInRouter(network_router, network_router_rules) + + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = test_vpc.cidr + rule["protocol"] = "tcp" + rule["startport"] = 22 + rule["endport"] = 22 + network_routing_firewall_rules.append(self.createIpv4RoutingFirewallRule(rule)) + network_router_rules.append({"chain": "fw_chain_ingress", + "rule": "ip saddr %s ip daddr 0.0.0.0/0 tcp dport 22 accept" % test_vpc.cidr}) + self.verifyNftablesRulesInRouter(network_router, network_router_rules) + + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = vpc_router.publicip + "/32" + rule["protocol"] = "icmp" + rule["icmptype"] = -1 + rule["icmpcode"] = -1 + network_routing_firewall_rules.append(self.createIpv4RoutingFirewallRule(rule)) + network_router_rules.append({"chain": "fw_chain_ingress", + "rule": "ip saddr %s ip daddr 0.0.0.0/0 icmp type %s accept" % (vpc_router.publicip, ICMPv4_ALL_TYPES)}) + self.verifyNftablesRulesInRouter(network_router, network_router_rules) + + # 9. Test VM2 in VR1-Network (ping/ssh should succeed) + self.verifyPingFromRouter(network_router, test_vpc_vm, expected=True) + # 10. Test VM1 in VR2-VPC (ping/ssh should succeed) + self.verifyPingFromRouter(vpc_router, test_network_vm, expected=True) + + # 11. Delete Network ACL rules for VPC + for rule in vpc_acl_rules: + rule.delete(self.apiclient) + vpc_router_rules[2] = {"chain": "eth2_ingress_policy", + "rule": "ip saddr %s icmp type %s accept" % (test_network.cidr, ICMPv4_ALL_TYPES), + "exists": False} + vpc_router_rules[3] = {"chain": "eth2_ingress_policy", + "rule": "ip saddr %s tcp dport 22 accept" % test_network.cidr, + "exists": False} + vpc_router_rules[4] = {"chain": "eth2_egress_policy", + "rule": "ip daddr 0.0.0.0/0 icmp type %s accept" % ICMPv4_ALL_TYPES, + "exists": False} + vpc_router_rules[5] = {"chain": "eth2_ingress_policy", + "rule": "ip saddr %s icmp type %s accept" % (network_router.publicip, ICMPv4_ALL_TYPES), + "exists": False} + self.verifyNftablesRulesInRouter(vpc_router, vpc_router_rules) + + # 12. Delete IPv4 firewall rules for Network + for rule in network_routing_firewall_rules: + rule.delete(self.apiclient) + network_router_rules[2] = {"chain": "fw_chain_ingress", + "rule": "ip saddr %s ip daddr 0.0.0.0/0 icmp type %s accept" % (test_vpc.cidr, ICMPv4_ALL_TYPES), + "exists": False} + network_router_rules[3] = {"chain": "fw_chain_ingress", + "rule": "ip saddr %s ip daddr 0.0.0.0/0 tcp dport 22 accept" % test_vpc.cidr, + "exists": False} + network_router_rules[4] = {"chain": "fw_chain_ingress", + "rule": "ip saddr %s ip daddr 0.0.0.0/0 icmp type %s accept" % (vpc_router.publicip, ICMPv4_ALL_TYPES), + "exists": False} + self.verifyNftablesRulesInRouter(network_router, network_router_rules) + + # 13. Test VM2 in VR1-Network (ping/ssh should fail) + self.verifyPingFromRouter(network_router, test_vpc_vm, expected=False) + # 14. Test VM1 in VR2-VPC (ping/ssh should fail) + self.verifyPingFromRouter(vpc_router, test_network_vm, expected=False) + + @attr(tags=['advanced'], required_hardware=False) + def test_10_bgp_peers(self): + """ Test for BGP peers""" + """ + # 1. Create bgppeer + # 2. List bgppeer + # 3. Update bgppeer + # 4. dedicate bgppeer to domain + # 5. released dedicated bgppeer + # 6. dedicate bgppeer to sub-domain/account + # 7. released dedicated bgppeer + # 8. delete bgppeer + """ + self.message("Running test_10_bgp_peers") + # 1. Create bgp peer + bgppeer_1 = BgpPeer.create( + self.apiclient, + zoneid=self.zone.id, + asnumber=ASN_1, + ipaddress=IP4_ADDR_1 + ) + self.cleanup.append(bgppeer_1) + # 2. List bgp peer + bgppeers = BgpPeer.list( + self.apiclient, + id=bgppeer_1.id + ) + self.assertEqual( + isinstance(bgppeers, list), + True, + "List bgppeers for zone should return a valid list" + ) + self.assertEqual( + len(bgppeers) == 1, + True, + "The number of bgp peers (%s) should be equal to 1" % (len(bgppeers)) + ) + self.assertEqual( + bgppeers[0].asnumber == ASN_1 and bgppeers[0].ipaddress == IP4_ADDR_1, + True, + "The asnumber of bgp peer (%s) should be equal to %s, the ip address (%s) should be %s" + % (bgppeers[0].asnumber, ASN_1, bgppeers[0].ipaddress, IP4_ADDR_1) + ) + # 3. Update bgp peer + bgppeer_1.update( + self.apiclient, + asnumber=ASN_2, + ipaddress=IP4_ADDR_2 + ) + bgppeers = BgpPeer.list( + self.apiclient, + id=bgppeer_1.id + ) + self.assertEqual( + isinstance(bgppeers, list) and len(bgppeers) == 1 + and bgppeers[0].asnumber == ASN_2 and bgppeers[0].ipaddress == IP4_ADDR_2, + True, + "The asnumber of bgp peer (%s) should be equal to %s, the ip address (%s) should be %s" + % (bgppeers[0].asnumber, ASN_2, bgppeers[0].ipaddress, IP4_ADDR_2) + ) + # 4. dedicate bgp peer to domain + BgpPeer.dedicate( + self.apiclient, + id=bgppeer_1.id, + domainid=self.domain.id + ) + bgppeers = BgpPeer.list( + self.apiclient, + id=bgppeer_1.id + ) + self.assertEqual( + isinstance(bgppeers, list) and len(bgppeers) == 1 and bgppeers[0].domainid == self.domain.id, + True, + "The bgppeer should be dedicated to domain %s" % self.domain.id + ) + # 5. released dedicated bgp peer + bgppeer_1.release( + self.apiclient + ) + bgppeers = BgpPeer.list( + self.apiclient, + id=bgppeer_1.id + ) + self.assertEqual( + isinstance(bgppeers, list) and len(bgppeers) == 1 and not bgppeers[0].domainid, + True, + "The bgp peer should not be dedicated to domain %s" % self.domain.id + ) + # 6. dedicate bgp peer to sub-domain/account + BgpPeer.dedicate( + self.apiclient, + id=bgppeer_1.id, + domainid=self.sub_domain.id, + account=self.regular_user.name + ) + bgppeers = BgpPeer.list( + self.apiclient, + id=bgppeer_1.id + ) + self.assertEqual( + isinstance(bgppeers, list) and len(bgppeers) == 1 + and bgppeers[0].domainid == self.sub_domain.id and bgppeers[0].account == self.regular_user.name, + True, + "The bgp peer should be dedicated to account %s" % self.regular_user.name + ) + # 7. released dedicated bgp peer + bgppeer_1.release( + self.apiclient + ) + bgppeers = BgpPeer.list( + self.apiclient, + id=bgppeer_1.id + ) + self.assertEqual( + isinstance(bgppeers, list) and len(bgppeers) == 1 and not bgppeers[0].domainid, + True, + "The bgppeer should not be dedicated to account %s" % self.regular_user.name + ) + # 8. delete bgp peer + bgppeer_1.delete( + self.apiclient + ) + self.cleanup.remove(bgppeer_1) + + @attr(tags=['advanced'], required_hardware=False) + def test_11_isolated_network_with_dynamic_routed_mode(self): + """ Test for Isolated Network with Dynamic Routed mode""" + """ + # 1. Create Isolated network with bgp_peer_1 + # 2. Create VM in the network + # 3. Verify frr.conf in network VR + # 4. Update network BGP peers (to bgp_peer_1 and bgp_peer_2) + # 5. Verify frr.conf in network VR + # 6. Reboot VR + # 7. Verify frr.conf in network VR + # 8. Update network BGP peers (to bgppeer_2) + # 9. Verify frr.conf in network VR + # 10. Update network BGP peers (to null) + # 11. Verify frr.conf in network VR + """ + self.message("Running test_11_isolated_network_with_dynamic_routed_mode") + + # 1. Create bgp peers + bgppeer_1 = BgpPeer.create( + self.apiclient, + zoneid=self.zone.id, + asnumber=ASN_1, + ipaddress=IP4_ADDR_1, + password=PASSWORD_1 + ) + self.cleanup.append(bgppeer_1) + + # 1. Create Isolated network with Dynamic routing + test_network_dynamic = Network.create( + self.apiclient, + self.services["network"], + networkofferingid=self.network_offering_dynamic.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + gateway=NETWORK_CIDR_PREFIX_DYNAMIC + ".1", + netmask="255.255.255.0", + bgppeerids=bgppeer_1.id + ) + self.cleanup.append(test_network_dynamic) + + # 2. Create VM in the network + test_network_dynamic_vm = VirtualMachine.create( + self.regular_user_apiclient, + self.services["virtual_machine"], + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + networkids=test_network_dynamic.id, + serviceofferingid=self.service_offering.id, + templateid=self.template.id) + self.cleanup.append(test_network_dynamic_vm) + + network_router = self.get_router(networkid=test_network_dynamic.id) + + # 3. Verify frr.conf in network VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": True}, + {"config": "network %s" % test_network_dynamic.cidr, + "exists": True}] + self.verifyFrrConf(network_router, frr_configs) + + # 4. Update network BGP peers (to bgp_peer_1 and bgp_peer_2) + bgppeer_2 = BgpPeer.create( + self.apiclient, + zoneid=self.zone.id, + asnumber=ASN_2, + ipaddress=IP4_ADDR_2, + password=PASSWORD_2 + ) + self.cleanup.append(bgppeer_2) + + test_network_dynamic.changeBgpPeers( + self.apiclient, + bgppeerids=[bgppeer_1.id, bgppeer_2.id] + ) + + # 5. Verify frr.conf in network VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": True}, + {"config": "neighbor %s remote-as %s" % (bgppeer_2.ipaddress, bgppeer_2.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_2.ipaddress, PASSWORD_2), + "exists": True}, + {"config": "network %s" % test_network_dynamic.cidr, + "exists": True}] + self.verifyFrrConf(network_router, frr_configs) + + # 6. Reboot VR + self.rebootRouter(network_router) + + # 7. Verify frr.conf in network VR + network_router = self.get_router(networkid=test_network_dynamic.id) + self.verifyFrrConf(network_router, frr_configs) + + # 8. Update network BGP peers (to bgppeer_2) + test_network_dynamic.changeBgpPeers( + self.apiclient, + bgppeerids=[bgppeer_2.id] + ) + + # 9. Verify frr.conf in network VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": False}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": False}, + {"config": "neighbor %s remote-as %s" % (bgppeer_2.ipaddress, bgppeer_2.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_2.ipaddress, PASSWORD_2), + "exists": True}, + {"config": "network %s" % test_network_dynamic.cidr, + "exists": True}] + self.verifyFrrConf(network_router, frr_configs) + + # 10. Update network BGP peers (to null) + test_network_dynamic.changeBgpPeers( + self.apiclient, + bgppeerids=[] + ) + + # 11. Verify frr.conf in network VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": True}, + {"config": "neighbor %s remote-as %s" % (bgppeer_2.ipaddress, bgppeer_2.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_2.ipaddress, PASSWORD_2), + "exists": True}, + {"config": "network %s" % test_network_dynamic.cidr, + "exists": True}] + self.verifyFrrConf(network_router, frr_configs) + + @attr(tags=['advanced'], required_hardware=False) + def test_12_vpc_and_tier_with_dynamic_routed_mode(self): + """ Test for VPC/tier with Dynamic Routed mode""" + """ + # 1. Create bgp peers + # 2. Create VPC + # 3. Create Network ACL (egress = Deny, ingress = Deny) + # 4. Create VPC tier with Network ACL in the VPC + # 5. Create VM in the VPC tier + # 6. Verify frr.conf in VPC VR + # 7. Update network BGP peers (to bgp_peer_1 and bgp_peer_2) + # 8. Verify frr.conf in VPC VR + # 9. Create VPC tier-2 with Network ACL in the VPC + # 10. Create VM-2 in the VPC tier-2 + # 11. Verify frr.conf in VPC VR + # 12. Reboot VPC VR + # 13. Verify frr.conf in VPC VR + # 14. Update network BGP peers (to bgppeer_2) + # 15. Verify frr.conf in VPC VR + # 16. Update network BGP peers (to null) + # 17. Verify frr.conf in VPC VR + """ + self.message("Running test_12_vpc_and_tier_with_dynamic_routed_mode") + + # 1. Create bgp peers + bgppeer_1 = BgpPeer.create( + self.apiclient, + zoneid=self.zone.id, + asnumber=ASN_1, + ipaddress=IP4_ADDR_1, + password=PASSWORD_1 + ) + self.cleanup.append(bgppeer_1) + + # 2.1 VPC offering for static routing + vpc_offering_dynamic = VpcOffering.create( + self.apiclient, + VPC_OFFERING_DYNAMIC + ) + self.cleanup.append(vpc_offering_dynamic) + vpc_offering_dynamic.update(self.apiclient, state='Enabled') + + # 2.2 Create VPC + self.services["vpc"]["cidr"] = VPC_CIDR_PREFIX + ".8.0/22" + test_vpc_dynamic = VPC.create(self.apiclient, + self.services["vpc"], + vpcofferingid=vpc_offering_dynamic.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + account=self.regular_user.name, + start=False, + bgppeerids=bgppeer_1.id + ) + self.cleanup.append(test_vpc_dynamic) + + # 3. Create Network ACL (egress = Deny, ingress = Deny) + test_network_acl_dynamic = NetworkACLList.create(self.apiclient, + services={}, + name="test-network-acl-dynamic", + description="test-network-acl-dynamic", + vpcid=test_vpc_dynamic.id + ) + + # 4. Create VPC tier with Network ACL in the VPC + self.services["network"]["name"] = "test_vpc_tier_dynamic_1" + test_vpc_tier_dynamic_1 = Network.create(self.regular_user_apiclient, + self.services["network"], + networkofferingid=self.vpc_network_offering_dynamic.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + vpcid=test_vpc_dynamic.id, + gateway=VPC_CIDR_PREFIX + ".8.1", + netmask="255.255.255.0", + aclid=test_network_acl_dynamic.id + ) + self.cleanup.append(test_vpc_tier_dynamic_1) + + # 5. Create VM in the VPC tier + test_vpc_vm_dynamic_1 = VirtualMachine.create( + self.regular_user_apiclient, + self.services["virtual_machine"], + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + networkids=test_vpc_tier_dynamic_1.id, + serviceofferingid=self.service_offering.id, + templateid=self.template.id) + self.cleanup.append(test_vpc_vm_dynamic_1) + + vpc_router = self.get_router(vpcid=test_vpc_dynamic.id) + + # 6. Verify frr.conf in VPC VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": True}, + {"config": "network %s" % test_vpc_tier_dynamic_1.cidr, + "exists": True}] + self.verifyFrrConf(vpc_router, frr_configs) + + # 7. Update VPC BGP peers (to bgp_peer_1 and bgp_peer_2) + bgppeer_2 = BgpPeer.create( + self.apiclient, + zoneid=self.zone.id, + asnumber=ASN_2, + ipaddress=IP4_ADDR_2, + password=PASSWORD_2 + ) + self.cleanup.append(bgppeer_2) + + test_vpc_dynamic.changeBgpPeers( + self.apiclient, + bgppeerids=[bgppeer_1.id, bgppeer_2.id] + ) + + # 8. Verify frr.conf in VPC VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": True}, + {"config": "neighbor %s remote-as %s" % (bgppeer_2.ipaddress, bgppeer_2.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_2.ipaddress, PASSWORD_2), + "exists": True}, + {"config": "network %s" % test_vpc_tier_dynamic_1.cidr, + "exists": True}] + self.verifyFrrConf(vpc_router, frr_configs) + + # 9. Create VPC tier-2 with Network ACL in the VPC + self.services["network"]["name"] = "test_vpc_tier_dynamic_2" + test_vpc_tier_dynamic_2 = Network.create(self.regular_user_apiclient, + self.services["network"], + networkofferingid=self.vpc_network_offering_dynamic.id, + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + vpcid=test_vpc_dynamic.id, + gateway=VPC_CIDR_PREFIX + ".9.1", + netmask="255.255.255.0", + aclid=test_network_acl_dynamic.id + ) + self.cleanup.append(test_vpc_tier_dynamic_2) + + # 10. Create VM-2 in the VPC tier-2 + test_vpc_vm_dynamic_2 = VirtualMachine.create( + self.regular_user_apiclient, + self.services["virtual_machine"], + zoneid=self.zone.id, + domainid=self.sub_domain.id, + accountid=self.regular_user.name, + networkids=test_vpc_tier_dynamic_2.id, + serviceofferingid=self.service_offering.id, + templateid=self.template.id) + self.cleanup.append(test_vpc_vm_dynamic_2) + + # 11. Verify frr.conf in VPC VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": True}, + {"config": "neighbor %s remote-as %s" % (bgppeer_2.ipaddress, bgppeer_2.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_2.ipaddress, PASSWORD_2), + "exists": True}, + {"config": "network %s" % test_vpc_tier_dynamic_1.cidr, + "exists": True}, + {"config": "network %s" % test_vpc_tier_dynamic_2.cidr, + "exists": True}] + self.verifyFrrConf(vpc_router, frr_configs) + + # 12. Reboot VPC VR + self.rebootRouter(vpc_router) + + # 13. Verify frr.conf in VPC VR + vpc_router = self.get_router(vpcid=test_vpc_dynamic.id) + self.verifyFrrConf(vpc_router, frr_configs) + + # 14. Update VPC BGP peers (to bgppeer_2) + test_vpc_dynamic.changeBgpPeers( + self.apiclient, + bgppeerids=[bgppeer_2.id] + ) + + # 15. Verify frr.conf in VPC VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": False}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": False}, + {"config": "neighbor %s remote-as %s" % (bgppeer_2.ipaddress, bgppeer_2.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_2.ipaddress, PASSWORD_2), + "exists": True}, + {"config": "network %s" % test_vpc_tier_dynamic_1.cidr, + "exists": True}, + {"config": "network %s" % test_vpc_tier_dynamic_2.cidr, + "exists": True}] + self.verifyFrrConf(vpc_router, frr_configs) + + # 16. Update VPC BGP peers (to null) + test_vpc_dynamic.changeBgpPeers( + self.apiclient, + bgppeerids=[] + ) + + # 17. Verify frr.conf in VPC VR + frr_configs = [{"config": "neighbor %s remote-as %s" % (bgppeer_1.ipaddress, bgppeer_1.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_1.ipaddress, PASSWORD_1), + "exists": True}, + {"config": "neighbor %s remote-as %s" % (bgppeer_2.ipaddress, bgppeer_2.asnumber), + "exists": True}, + {"config": "neighbor %s password %s" % (bgppeer_2.ipaddress, PASSWORD_2), + "exists": True}, + {"config": "network %s" % test_vpc_tier_dynamic_1.cidr, + "exists": True}, + {"config": "network %s" % test_vpc_tier_dynamic_2.cidr, + "exists": True}] + self.verifyFrrConf(vpc_router, frr_configs) + + + @attr(tags=['advanced'], required_hardware=False) + def test_13_asn_ranges(self): + """ Test for ASN ranges""" + """ + # 1. Create an ASN range without overlap + # 2. List ASN ranges by zoneid + # 3. List ASN numbers by ASN range id + # 4. Create an ASN range with overlap, it should fail + # 5. Delete ASN range + """ + self.message("Running test_13_asn_ranges") + + # 1. Create an ASN range without overlap + asnrange_2 = ASNRange.create( + self.apiclient, + zoneid=self.zone.id, + startasn=END_ASN+100, + endasn=END_ASN+200 + ) + self.cleanup.append(asnrange_2) + + # 2. List ASN ranges by zoneid + ranges = ASNRange.list( + self.apiclient, + zoneid = self.zone.id + ) + self.assertEqual( + isinstance(ranges, list), + True, + "List ASN ranges by zoneid should return a valid list" + ) + self.assertEqual( + len(ranges) >= 1, + True, + "The number of ASN ranges (%s) should be at least 1" % (len(ranges)) + ) + asnrange_2_new = None + for range in ranges: + if range.startasn == asnrange_2.startasn: + asnrange_2_new = range + break + if asnrange_2_new: + self.assertEqual( + asnrange_2_new.endasn == asnrange_2.endasn, + True, + "The end ASN of ASN range (%s-%s) should be equal to %s" % (asnrange_2_new.startasn, asnrange_2_new.endasn, asnrange_2.endasn) + ) + else: + self.fail("Unable to find ASN range (%s-%s)" % (asnrange_2.startasn, asnrange_2.endasn)) + + # 3. List ASN numbers by ASN range id + asnumbers = ASNRange.listAsNumbers( + self.apiclient, + zoneid = self.zone.id, + asnrangeid = asnrange_2.id + ) + self.assertEqual( + isinstance(asnumbers, list), + True, + "List AS numbers should return a valid list" + ) + self.assertEqual( + len(asnumbers) == asnrange_2.endasn - asnrange_2.startasn + 1, + True, + "The number of asnumbers (%s) should be equal to %s" % (len(asnumbers), (asnrange_2.endasn - asnrange_2.startasn + 1)) + ) + + # 4. Create an ASN range with overlap, it should fail + try: + asnrange_3 = ASNRange.create( + self.apiclient, + zoneid=self.zone.id, + startasn=END_ASN+150, + endasn=END_ASN+250 + ) + self.cleanup.append(asnrange_3) + self.fail("Succeeded to create ASN range (%s-%s) but it should fail" % (asnrange_3.startasn, asnrange_3.endasn)) + except Exception as e: + self.message("Failed to create ASN range but it is expected") + + # 5. Delete ASN range + asnrange_2.delete( + self.apiclient + ) + self.cleanup.remove(asnrange_2) diff --git a/test/integration/smoke/test_network_ipv6.py b/test/integration/smoke/test_network_ipv6.py index 2c369f28300..1e5cec7ef4c 100644 --- a/test/integration/smoke/test_network_ipv6.py +++ b/test/integration/smoke/test_network_ipv6.py @@ -394,7 +394,7 @@ class TestIpv6Network(cloudstackTestCase): cmd, hypervisor=self.routerDetailsMap[router.id]['hypervisor'] ) - self.assertTrue(type(result) == list and len(result) > 0, + self.assertTrue(type(result) == list, "%s on router %s returned invalid result" % (cmd, router.id)) result = '\n'.join(result) return result diff --git a/test/integration/smoke/test_vpc_ipv6.py b/test/integration/smoke/test_vpc_ipv6.py index efec43add7c..bc05334a56e 100644 --- a/test/integration/smoke/test_vpc_ipv6.py +++ b/test/integration/smoke/test_vpc_ipv6.py @@ -520,7 +520,7 @@ class TestIpv6Vpc(cloudstackTestCase): cmd, hypervisor=self.routerDetailsMap[router.id]['hypervisor'] ) - self.assertTrue(type(result) == list and len(result) > 0, + self.assertTrue(type(result) == list, "%s on router %s returned invalid result" % (cmd, router.id)) result = '\n'.join(result) return result diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index 1ec3b2f7dda..ace0dbb33f3 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -51,6 +51,7 @@ known_categories = { 'VirtualMachine': 'Virtual Machine', 'VM': 'Virtual Machine', 'Vnf': 'Virtual Network Functions', + 'GuestSubnet': 'Routing', 'Domain': 'Domain', 'Template': 'Template', 'Iso': 'ISO', @@ -280,7 +281,13 @@ known_categories = { 'SharedFileSystem': 'Shared FileSystem', 'Webhook': 'Webhook', 'Webhooks': 'Webhook', - 'purgeExpungedResources': 'Resource' + 'purgeExpungedResources': 'Resource', + 'BgpPeer': 'BGP Peer', + 'createASNRange': 'AS Number Range', + 'listASNRange': 'AS Number Range', + 'deleteASNRange': 'AS Number Range', + 'listASNumbers': 'AS Number', + 'releaseASNumber': 'AS Number' } diff --git a/tools/appliance/systemvmtemplate/scripts/configure_systemvm_services.sh b/tools/appliance/systemvmtemplate/scripts/configure_systemvm_services.sh index d1a6930d66a..077cabf8d93 100644 --- a/tools/appliance/systemvmtemplate/scripts/configure_systemvm_services.sh +++ b/tools/appliance/systemvmtemplate/scripts/configure_systemvm_services.sh @@ -111,6 +111,7 @@ function configure_services() { systemctl disable haproxy systemctl disable keepalived systemctl disable radvd + systemctl disable frr systemctl disable strongswan-starter systemctl disable x11-common systemctl disable xl2tpd diff --git a/tools/appliance/systemvmtemplate/scripts/install_systemvm_packages.sh b/tools/appliance/systemvmtemplate/scripts/install_systemvm_packages.sh index 63407863110..d391b5c4e19 100644 --- a/tools/appliance/systemvmtemplate/scripts/install_systemvm_packages.sh +++ b/tools/appliance/systemvmtemplate/scripts/install_systemvm_packages.sh @@ -73,6 +73,7 @@ function install_packages() { haproxy \ haveged \ radvd \ + frr \ sharutils genisoimage \ strongswan libcharon-extra-plugins libstrongswan-extra-plugins strongswan-charon strongswan-starter \ virt-what open-vm-tools qemu-guest-agent hyperv-daemons cloud-guest-utils \ diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 9c2c5b12089..e0a57c39924 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -2835,6 +2835,10 @@ class NetworkOffering: cmd.tags = services["tags"] if "internetprotocol" in services: cmd.internetprotocol = services["internetprotocol"] + if "networkmode" in services: + cmd.networkmode = services["networkmode"] + if "routingmode" in services: + cmd.routingmode = services["routingmode"] cmd.details = [{}] if "servicepackageuuid" in services: cmd.details[0]["servicepackageuuid"] = services["servicepackageuuid"] @@ -3553,7 +3557,7 @@ class Network: subdomainaccess=None, zoneid=None, gateway=None, netmask=None, vpcid=None, aclid=None, vlan=None, externalid=None, bypassvlanoverlapcheck=None, associatednetworkid=None, publicmtu=None, privatemtu=None, - sourcenatipaddress=None): + sourcenatipaddress=None, cidrsize=None, **kwargs): """Create Network for account""" cmd = createNetwork.createNetworkCmd() cmd.name = services["name"] @@ -3580,6 +3584,10 @@ class Network: cmd.netmask = netmask elif "netmask" in services: cmd.netmask = services["netmask"] + if cidrsize: + cmd.cidrsize = cidrsize + elif "cidrsize" in services: + cmd.cidrsize = services["cidrsize"] if "startip" in services: cmd.startip = services["startip"] if "endip" in services: @@ -3637,6 +3645,7 @@ class Network: cmd.privatemtu = privatemtu if sourcenatipaddress: cmd.sourcenatipaddress = sourcenatipaddress + [setattr(cmd, k, v) for k, v in list(kwargs.items())] return Network(apiclient.createNetwork(cmd).__dict__) def delete(self, apiclient): @@ -3690,6 +3699,13 @@ class Network: cmd.listall = True return (apiclient.listNetworks(cmd)) + def changeBgpPeers(self, apiclient, bgppeerids): + cmd = changeBgpPeersForNetwork.changeBgpPeersForNetworkCmd() + cmd.networkid = self.id + if bgppeerids is not None: + cmd.bgppeerids = bgppeerids + return (apiclient.changeBgpPeersForNetwork(cmd)) + class NetworkACL: """Manage Network ACL lifecycle""" @@ -5092,6 +5108,10 @@ class VpcOffering: }) if "internetprotocol" in services: cmd.internetprotocol = services["internetprotocol"] + if "networkmode" in services: + cmd.networkmode = services["networkmode"] + if "routingmode" in services: + cmd.routingmode = services["routingmode"] return VpcOffering(apiclient.createVPCOffering(cmd).__dict__) def update(self, apiclient, name=None, displaytext=None, state=None): @@ -5202,6 +5222,13 @@ class VPC: cmd.listall = True return (apiclient.listVPCs(cmd)) + def changeBgpPeers(self, apiclient, bgppeerids): + cmd = changeBgpPeersForVpc.changeBgpPeersForVpcCmd() + cmd.vpcid = self.id + if bgppeerids is not None: + cmd.bgppeerids = bgppeerids + return (apiclient.changeBgpPeersForVpc(cmd)) + class PrivateGateway: """Manage private gateway lifecycle""" @@ -7294,6 +7321,246 @@ class Webhook: [setattr(cmd, k, v) for k, v in list(kwargs.items())] return apiclient.deleteWebhookDelivery(cmd) + +class ZoneIpv4Subnet: + """Manage IPv4 Subnet for Zone""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, zoneid, subnet, **kwargs): + """Create IPv4 Subnet for Zone""" + cmd = createIpv4SubnetForZone.createIpv4SubnetForZoneCmd() + cmd.zoneid = zoneid + cmd.subnet = subnet + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return ZoneIpv4Subnet(apiclient.createIpv4SubnetForZone(cmd).__dict__) + + @classmethod + def list(cls, apiclient, **kwargs): + cmd = listIpv4SubnetsForZone.listIpv4SubnetsForZoneCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.listIpv4SubnetsForZone(cmd) + + def delete(self, apiclient): + """Delete IPv4 Subnet for Zone""" + cmd = deleteIpv4SubnetForZone.deleteIpv4SubnetForZoneCmd() + cmd.id = self.id + apiclient.deleteIpv4SubnetForZone(cmd) + + def update(self, apiclient, **kwargs): + """Update IPv4 Subnet for Zone""" + + cmd = updateIpv4SubnetForZone.updateIpv4SubnetForZoneCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.updateIpv4SubnetForZone(cmd) + + @classmethod + def dedicate(cls, apiclient, id, account=None, domainid=None, projectid=None): + """Dedicate IPv4 Subnet for Zone""" + + cmd = dedicateIpv4SubnetForZone.dedicateIpv4SubnetForZoneCmd() + cmd.id = id + cmd.account = account + cmd.domainid = domainid + cmd.projectid = projectid + return ZoneIpv4Subnet(apiclient.dedicateIpv4SubnetForZone(cmd).__dict__) + + def release(self, apiclient): + """Release IPv4 Subnet for Zone""" + + cmd = releaseIpv4SubnetForZone.releaseIpv4SubnetForZoneCmd() + cmd.id = self.id + return apiclient.releaseIpv4SubnetForZone(cmd) + +class Ipv4SubnetForGuestNetwork: + """Manage IPv4 Subnet for Guest Network""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, parentid, subnet=None, cidrsize=None, **kwargs): + """Create IPv4 Subnet for Guest Network""" + cmd = createIpv4SubnetForGuestNetwork.createIpv4SubnetForGuestNetworkCmd() + cmd.parentid = parentid + if subnet: + cmd.subnet = subnet + if cidrsize: + cmd.cidrsize = cidrsize + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return Ipv4SubnetForGuestNetwork(apiclient.createIpv4SubnetForGuestNetwork(cmd).__dict__) + + @classmethod + def list(cls, apiclient, **kwargs): + cmd = listIpv4SubnetsForGuestNetwork.listIpv4SubnetsForGuestNetworkCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.listIpv4SubnetsForGuestNetwork(cmd) + + def delete(self, apiclient): + """Delete IPv4 Subnet for Guest Network""" + cmd = deleteIpv4SubnetForGuestNetwork.deleteIpv4SubnetForGuestNetworkCmd() + cmd.id = self.id + apiclient.deleteIpv4SubnetForGuestNetwork(cmd) + + + +class RoutingFirewallRule: + """Manage IPv4 Routing Firewall rules""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, networkid=None, protocol=None): + """Create IPv4 Routing Firewall rule""" + cmd = createRoutingFirewallRule.createRoutingFirewallRuleCmd() + + if "networkid" in services: + cmd.networkid = services["networkid"] + elif networkid: + cmd.networkid = networkid + + if "protocol" in services: + cmd.protocol = services["protocol"] + if services["protocol"] == 'ICMP': + cmd.icmptype = -1 + cmd.icmpcode = -1 + elif protocol: + cmd.protocol = protocol + + if "icmptype" in services: + cmd.icmptype = services["icmptype"] + if "icmpcode" in services: + cmd.icmpcode = services["icmpcode"] + + if "startport" in services: + cmd.startport = services["startport"] + if "endport" in services: + cmd.endport = services["endport"] + + if "cidrlist" in services: + cmd.cidrlist = services["cidrlist"] + if "destcidrlist" in services: + cmd.destcidrlist = services["destcidrlist"] + + if "traffictype" in services: + cmd.traffictype = services["traffictype"] + + return RoutingFirewallRule(apiclient.createRoutingFirewallRule(cmd).__dict__) + + @classmethod + def list(cls, apiclient, **kwargs): + cmd = listRoutingFirewallRules.listRoutingFirewallRulesCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.listIpv4SubnetsForGuestNetwork(cmd) + + def delete(self, apiclient): + """Delete IPv4 Routing Firewall rule""" + cmd = deleteRoutingFirewallRule.deleteRoutingFirewallRuleCmd() + cmd.id = self.id + apiclient.deleteRoutingFirewallRule(cmd) + + def update(self, apiclient, **kwargs): + """Update IPv4 Routing Firewall rule""" + cmd = updateRoutingFirewallRule.updateRoutingFirewallRuleCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + apiclient.updateRoutingFirewallRule(cmd) + + +class ASNRange: + """Manage ASN range for Guest Network""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, zoneid, startasn, endasn, **kwargs): + """Create ASN range for Guest Network""" + cmd = createASNRange.createASNRangeCmd() + cmd.zoneid = zoneid + cmd.startasn = startasn + cmd.endasn = endasn + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return ASNRange(apiclient.createASNRange(cmd).__dict__) + + @classmethod + def list(cls, apiclient, **kwargs): + cmd = listASNRanges.listASNRangesCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.listASNRanges(cmd) + + def delete(self, apiclient): + """Delete ASN range for Guest Network""" + cmd = deleteASNRange.deleteASNRangeCmd() + cmd.id = self.id + apiclient.deleteASNRange(cmd) + + @classmethod + def listAsNumbers(cls, apiclient, **kwargs): + """List AS numbers of an ASN range""" + cmd = listASNumbers.listASNumbersCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.listASNumbers(cmd) + + +class BgpPeer: + """Manage BGP Peers for Zone""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, zoneid, asnumber, **kwargs): + """Create BGP Peer""" + cmd = createBgpPeer.createBgpPeerCmd() + cmd.zoneid = zoneid + cmd.asnumber = asnumber + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return BgpPeer(apiclient.createBgpPeer(cmd).__dict__) + + @classmethod + def list(cls, apiclient, **kwargs): + cmd = listBgpPeers.listBgpPeersCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.listBgpPeers(cmd) + + def delete(self, apiclient): + """Delete BGP Peer""" + cmd = deleteBgpPeer.deleteBgpPeerCmd() + cmd.id = self.id + apiclient.deleteBgpPeer(cmd) + + def update(self, apiclient, **kwargs): + """Update BGP Peer""" + + cmd = updateBgpPeer.updateBgpPeerCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.updateBgpPeer(cmd) + + @classmethod + def dedicate(cls, apiclient, id, account=None, domainid=None, projectid=None): + """Dedicate BGP Peer""" + + cmd = dedicateBgpPeer.dedicateBgpPeerCmd() + cmd.id = id + cmd.account = account + cmd.domainid = domainid + cmd.projectid = projectid + return BgpPeer(apiclient.dedicateBgpPeer(cmd).__dict__) + + def release(self, apiclient): + """Release BGP Peer""" + + cmd = releaseBgpPeer.releaseBgpPeerCmd() + cmd.id = self.id + return apiclient.releaseBgpPeer(cmd) + + class SharedFS: def __init__(self, items): diff --git a/tools/marvin/marvin/lib/utils.py b/tools/marvin/marvin/lib/utils.py index d3cbd421c2f..f80eccf1159 100644 --- a/tools/marvin/marvin/lib/utils.py +++ b/tools/marvin/marvin/lib/utils.py @@ -43,11 +43,11 @@ from marvin.codes import ( FAILED) def _configure_ssh_credentials(hypervisor): - ssh_command = "ssh -i ~/.ssh/id_rsa.cloud -ostricthostkeychecking=no " + ssh_command = "ssh -q -i ~/.ssh/id_rsa.cloud -ostricthostkeychecking=no " if (str(hypervisor).lower() == 'vmware' or str(hypervisor).lower() == 'hyperv'): - ssh_command = "ssh -i ~cloud/.ssh/id_rsa -ostricthostkeychecking=no " + ssh_command = "ssh -q -i ~cloud/.ssh/id_rsa -ostricthostkeychecking=no " return ssh_command diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 83e5561dc4d..ca38fd24fc9 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1,7 +1,9 @@ { "alert.service.domainrouter": "Domain router", +"error.dedicate.bgp.peer.failed":"Failed to dedicate BGP peer", "error.dedicate.cluster.failed": "Failed to dedicate cluster.", "error.dedicate.host.failed": "Failed to dedicate host.", +"error.dedicate.ipv4.subnet.failed": "Failed to dedicate IPv4 subnet.", "error.dedicate.pod.failed": "Failed to dedicate pod.", "error.dedicate.zone.failed": "Failed to dedicate zone.", "error.empty.counter.operator.threshold": "Either Counter, Operator or Threshold is empty", @@ -9,8 +11,10 @@ "error.fetching.async.job.result": "Error encountered while fetching async job result.", "error.form.message": "There are errors in the form. Please fix them.", "error.password.not.match": "The password fields do not match", +"error.release.dedicate.bgp.peer": "Failed to release dedicated BGP peer.", "error.release.dedicate.cluster": "Failed to release dedicated cluster.", "error.release.dedicate.host": "Failed to release dedicated host.", +"error.release.dedicate.ipv4.subnet": "Failed to release dedicated IPv4 subnet.", "error.release.dedicate.pod": "Failed to release dedicated pod.", "error.release.dedicate.zone": "Failed to release dedicated zone.", "error.unable.to.add.setting.extraconfig": "It is not allowed to add setting for extraconfig. Please update VirtualMachine with extraconfig parameter.", @@ -55,6 +59,7 @@ "label.action.bulk.delete.isos": "Bulk delete ISOs", "label.action.bulk.delete.load.balancer.rules": "Bulk delete load balancer rules", "label.action.bulk.delete.portforward.rules": "Bulk delete port forward rules", +"label.action.bulk.delete.routing.firewall.rules": "Bulk remove IPv4 Routing firewall rules", "label.action.bulk.delete.snapshots": "Bulk delete snapshots", "label.action.bulk.delete.templates": "Bulk delete Templates", "label.action.bulk.release.public.ip.address": "Bulk release public IP addresses", @@ -91,6 +96,7 @@ "label.action.delete.physical.network": "Delete physical Network", "label.action.delete.pod": "Delete Pod", "label.action.delete.primary.storage": "Delete primary storage", +"label.action.delete.routing.firewall.rule": "Delete IPv4 Routing firewall rule", "label.action.delete.secondary.storage": "Delete secondary storage", "label.action.delete.security.group": "Delete security group", "label.action.delete.snapshot": "Delete Snapshot", @@ -177,6 +183,7 @@ "label.action.disable.2FA.user.auth": "Disable User Two Factor Authentication", "label.action.register.iso": "Register ISO", "label.action.register.template": "Register Template from URL", +"label.action.release.asnumber": "Release AS Number", "label.action.release.ip": "Release IP", "label.action.release.reserved.ip": "Release reserved IP", "label.action.remove.host": "Remove host", @@ -232,6 +239,7 @@ "label.add.acl.list": "Add ACL list", "label.add.affinity.group": "Add new affinity group", "label.add.baremetal.dhcp.device": "Add bare metal DHCP device", +"label.add.bgp.peer": "Add BGP peer", "label.add.bigswitchbcf.device": "Add BigSwitch BCF controller", "label.add.brocadevcs.device": "Add Brocade Vcs Switch", "label.add.by": "Add by", @@ -254,6 +262,7 @@ "label.add.intermediate.certificate": "Add intermediate certificate", "label.add.internal.lb": "Add internal LB", "label.add.ip.range": "Add IP range", +"label.add.ipv4.subnet": "Add IPv4 subnet for Routed networks", "label.add.ip.v6.prefix": "Add IPv6 prefix", "label.add.isolated.network": "Add isolated Network", "label.add.kubernetes.cluster": "Add Kubernetes cluster", @@ -312,6 +321,7 @@ "label.add.tungsten.tag": "Add Tag", "label.add.tungsten.tag.type": "Add Tag Type", "label.add.user": "Add User", +"label.add.upstream.ipv4.routes": "Add upstream IPv4 routes", "label.add.upstream.ipv6.routes": "Add upstream IPv6 routes", "label.add.vm": "Add Instance", "label.add.vms": "Add Instances", @@ -376,6 +386,9 @@ "label.archive.alerts": "Archive alerts", "label.archive.events": "Archive events", "label.as.default": "as default", +"label.asnumber": "AS Number", +"label.asnumbers": "AS Numbers", +"label.asnrange": "AS Range", "label.assign": "Assign", "label.assign.instance.another": "Assign Instance to another Account", "label.assign.vms": "Assign Instances", @@ -428,6 +441,8 @@ "label.based.on.role.id.or.type": "Creates a role based on either role id or type.", "label.basic": "Basic", "label.bcfdeviceid": "ID", +"label.bgp.peers": "BGP Peers", +"label.bgp.peer.set.reservation.desc": "You can make the BGP peer public, or you can dedicate/reserve it for either a Domain or an Account", "label.bigswitch.controller.address": "BigSwitch BCF controller address", "label.bladeid": "Blade ID", "label.blades": "Blades", @@ -466,6 +481,7 @@ "label.certificateid": "Certificate ID", "label.change": "Change", "label.change.affinity": "Change affinity", +"label.change.bgp.peers": "Change BGP peers", "label.change.ip.address": "Change IP address", "label.change.ipaddress": "Change IP address for NIC", "label.change.disk.offering": "Change disk offering", @@ -476,6 +492,7 @@ "label.choose.resource.icon": "Choose icon", "label.choose.saml.identity": "Choose SAML identity provider", "label.cidr": "CIDR", +"label.cidrsize": "CIDR size", "label.cidr.destination.network": "Destination Network CIDR", "label.cidrlist": "CIDR list", "label.cisco.nexus1000v.ip.address": "Nexus 1000v IP address", @@ -530,6 +547,7 @@ "label.confirm.delete.isos": "Please confirm you wish to delete the selected ISOs.", "label.confirm.delete.loadbalancer.rules": "Please confirm you wish to delete the selected load balancing rules.", "label.confirm.delete.portforward.rules": "Please confirm you wish to delete the selected port-forward rules.", +"label.confirm.delete.routing.firewall.rules": "Please confirm you wish to delete the selected IPv4 Routing firewall rules", "label.confirm.delete.snapshot.zones": "Please confirm you wish to delete the Snapshot in the selected zones.", "label.confirm.delete.templates": "Please confirm you wish to delete the selected Templates.", "label.confirm.delete.tungsten.address.group": "Please confirm that you would like to delete this Address Group", @@ -586,6 +604,7 @@ "label.create": "Create", "label.create.instance": "Create cloud server", "label.create.account": "Create Account", +"label.create.asnrange": "Create AS Range", "label.create.backup": "Start backup", "label.create.sharedfs": "Create Shared FileSystem", "label.create.network": "Create new Network", @@ -644,8 +663,10 @@ "label.declare.host.as.degraded": "Declare host as degraded", "label.decline.invitation": "Decline invitation", "label.dedicate": "Dedicate", +"label.dedicate.bgp.peer": "Dedicate BGP peer", "label.dedicate.cluster": "Dedicate cluster", "label.dedicate.host": "Dedicate host", +"label.dedicate.ipv4.subnet": "Dedicate IPv4 subnet", "label.dedicate.pod": "Dedicate pod", "label.dedicate.vlan.vni.range": "Dedicate VLAN/VNI range", "label.dedicate.zone": "Dedicate zone", @@ -661,8 +682,10 @@ "label.delete.acl.list": "Delete ACL list", "label.delete.affinity.group": "Delete affinity group", "label.delete.alerts": "Delete alerts", +"label.delete.asnrange": "Delete AS Range", "label.delete.autoscale.vmgroup": "Delete AutoScaling Group", "label.delete.backup": "Delete backup", +"label.delete.bgp.peer": "Delete BGP peer", "label.delete.bigswitchbcf": "Remove BigSwitch BCF controller", "label.delete.brocadevcs": "Remove Brocade Vcs switch", "label.delete.certificate": "Delete certificate", @@ -678,6 +701,7 @@ "label.delete.icon": "Delete icon", "label.delete.instance.group": "Delete Instance group", "label.delete.internal.lb": "Delete internal LB", +"label.delete.ipv4.subnet": "Delete IPv4 subnet", "label.delete.ip.v6.prefix": "Delete IPv6 prefix", "label.delete.netscaler": "Delete NetScaler", "label.delete.niciranvp": "Remove Nvp controller", @@ -846,6 +870,7 @@ "label.duration.12hours": "12 hours", "label.duration.24hours": "24 hours", "label.duration.7days": "7 days", +"label.dynamic": "Dynamic", "label.dynamicscalingenabled": "Dynamic scaling enabled", "label.dynamicscalingenabled.tooltip": "Instance can dynamically scale only when dynamic scaling is enabled on Template, service offering and global setting.", "label.diskofferingstrictness": "Disk offering strictness", @@ -883,6 +908,7 @@ "label.encrypt": "Encrypt", "label.encryptroot": "Encrypt Root Disk", "label.end": "End", +"label.endasn": "End AS Number", "label.end.date": "End date", "label.end.date.and.time": "End date and time", "label.end.ip": "End IP", @@ -1141,6 +1167,8 @@ "label.iothreadsenabled" : "IOThreads", "label.iothreadsenabled.tooltip" : "Enable iothreads allocation for KVM hypervisor", "label.ip": "IP address", +"label.ip4routes": "IPv6 routes", +"label.ip4routing": "IPv4 routing", "label.ip6firewall": "IPv6 firewall", "label.ip6routes": "IPv6 routes", "label.ip6routing": "IPv6 routing", @@ -1168,11 +1196,14 @@ "label.ipv4.cidr": "IPv4 CIDR", "label.ipv4.dns1": "IPv4 DNS1", "label.ipv4.dns2": "IPv4 DNS2", +"label.ipv4.subnets": "IPv4 Subnets", +"label.ipv4.subnet.set.reservation.desc": "(optional) Please specify a domain or an Account to be associated with this IPv4 subnet.", "label.ipv6.dns1": "IPv6 DNS1", "label.ipv6.dns2": "IPv6 DNS2", "label.ipv6.subnets": "IPv6 Subnets", "label.ip.addresses": "IP Addresses", "label.iqn": "Target IQN", +"label.isallocated": "Allocated", "label.is.base64.encoded": "Base64 encoded", "label.is.in.progress": "is in progress", "label.is.shared": "Is shared", @@ -1476,6 +1507,7 @@ "label.networkkbsread": "Network read", "label.networkkbswrite": "Network write", "label.networklimit": "Network limits", +"label.networkmode": "Network Mode", "label.networkname": "Network name", "label.networkofferingdisplaytext": "Network offering", "label.networkofferingid": "Network offering", @@ -1524,7 +1556,6 @@ "label.not.suitable": "Not suitable", "label.notifications": "Notifications", "label.nsx": "NSX", -"label.nsxmode": "NSX Mode", "label.nsx.provider": "NSX Provider", "label.nsx.provider.name": "NSX provider name", "label.nsx.provider.hostname": "NSX provider hostname", @@ -1618,6 +1649,7 @@ "label.param.value": "Parameter value", "label.parentdomainname": "Parent domain", "label.parentname": "Parent", +"label.parentsubnet": "Parent Subnet", "label.passive": "Passive", "label.password": "Password", "label.password.default": "Default Password", @@ -1810,17 +1842,21 @@ "label.relationaloperator": "Operator", "label.release": "Release", "label.release.account": "Release from Account", +"label.release.dedicated.bgp.peer": "Release dedicated BGP peer", "label.release.dedicated.cluster": "Release dedicated cluster", "label.release.dedicated.host": "Release dedicated host", +"label.release.dedicated.ipv4.subnet": "Release dedicated IPv4 subnet", "label.release.dedicated.pod": "Release dedicated pod", "label.release.dedicated.zone": "Release dedicated zone", "label.releasing.ip": "Releasing IP", "label.remote.instances": "Remote Instances", "label.remove": "Remove", "label.remove.annotation": "Remove comment", +"label.remove.bgp.peer": "Remove BGP peer", "label.remove.egress.rule": "Remove egress rule", "label.remove.interface.route.table": "Remove Tungsten interface route table", "label.remove.ip.range": "Remove IP range", +"label.remove.ipv4.subnet": "Remove IPv4 subnet", "label.remove.ldap": "Remove LDAP", "label.remove.logical.network": "Remove Network from logical router", "label.remove.logical.router": "Remove logical router", @@ -1901,7 +1937,9 @@ "label.routercount": "Total of virtual routers", "label.routerip": "IPv4 address for the VR in this Network.", "label.routeripv6": "IPv6 address for the VR in this Network.", +"label.routing.firewall": "IPv4 Routing Firewall", "label.resourcegroup": "Resource group", +"label.routingmode": "Routing mode", "label.routing.policy": "Routing policy", "label.routing.policy.terms": "Routing policy terms", "label.routing.policy.terms.then": "Routing policy terms then", @@ -2056,6 +2094,7 @@ "label.sourcenattype": "Supported source NAT type", "label.sourceport": "Source port", "label.sourcetype": "Source type", +"label.specifyasnumber": "Specify AS Number", "label.specifyipranges": "Specify IP ranges", "label.specifyvlan": "Specify VLAN", "label.splitconnections": "Split connections", @@ -2090,6 +2129,7 @@ "label.sslverification": "SSL verification", "label.standard.us.keyboard": "Standard (US) keyboard", "label.start": "Start", +"label.startasn": "Start AS Number", "label.start.date": "Start date", "label.start.date.and.time": "Start date and time", "label.start.ip": "Start IP", @@ -2106,6 +2146,7 @@ "label.startquota": "Quota value", "label.state": "State", "label.staticnat": "Static NAT", +"label.static": "Static", "label.static.routes": "Static routes", "label.status": "Status", "label.step.1": "Step 1", @@ -2144,6 +2185,7 @@ "label.strict": "Strict", "label.subdomainaccess": "Subdomain access", "label.submit": "Submit", +"label.subnet": "Subnet", "label.succeeded": "Succeeded", "label.success": "Success", "label.success.migrations": "Successful migrations", @@ -2305,10 +2347,12 @@ "label.up": "Up", "label.updateinsequence": "Update in sequence", "label.update.autoscale.vmgroup": "Update AutoScaling Group", +"label.update.bgp.peer": "Update BGP peer", "label.update.condition": "Update condition", "label.update.sharedfs": "Update Shared FileSystem", "label.update.instance.group": "Update Instance group", "label.update.ip.range": "Update IP range", +"label.update.ipv4.subnet": "Update IPv4 subnet", "label.update.network": "Update Network", "label.update.physical.network": "Update physical Network", "label.update.project.role": "Update project role", @@ -2559,6 +2603,7 @@ "message.action.cancel.maintenance": "Your host has been successfully canceled for maintenance. This process can take up to several minutes.", "message.action.cancel.maintenance.mode": "Please confirm that you want to cancel this maintenance.", "message.action.create.snapshot.from.vmsnapshot": "Please confirm that you want to create Snapshot from Instance Snapshot", +"message.action.delete.asnrange": "Please confirm the AS range that you want to delete", "message.action.delete.autoscale.vmgroup": "Please confirm that you want to delete this autoscaling group.", "message.action.delete.backup.offering": "Please confirm that you want to delete this backup offering?", "message.action.delete.backup.repository": "Please confirm that you want to delete this backup repository?", @@ -2567,6 +2612,7 @@ "message.action.delete.external.firewall": "Please confirm that you would like to remove this external firewall. Warning: If you are planning to add back the same external firewall, you must reset usage data on the device.", "message.action.delete.external.load.balancer": "Please confirm that you would like to remove this external load balancer. Warning: If you are planning to add back the same external load balancer, you must reset usage data on the device.", "message.action.delete.ingress.rule": "Please confirm that you want to delete this ingress rule.", +"message.action.delete.ipv4.subnet": "Please confirm that you want to delete this IPv4 subnet.", "message.action.delete.guest.os": "Please confirm that you want to delete this guest os. System defined entry cannot be deleted.", "message.action.delete.guest.os.hypervisor.mapping": "Please confirm that you want to delete this guest os hypervisor mapping. System defined entry cannot be deleted.", "message.action.delete.instance.group": "Please confirm that you want to delete the Instance group.", @@ -2633,6 +2679,7 @@ "message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.", "message.action.recover.sharedfs": "Please confirm that you would like to recover this Shared FileSystem.", "message.action.recover.volume": "Please confirm that you would like to recover this volume.", +"message.action.release.asnumber": "Please confirm that you want to release this AS Number.", "message.action.release.ip": "Please confirm that you want to release this IP.", "message.action.remove.host": "Please confirm that you want to remove this host.", "message.action.remove.logical.router": "Please confirm that you want to remove Logical Router?", @@ -2674,6 +2721,8 @@ "message.add.host": "Please specify the following parameters to add a new host.", "message.add.host.sshkey": "WARNING: In order to add a host with SSH key, you must ensure your hypervisor host has been configured correctly.", "message.add.iprange.processing": "Adding IP Range...", +"message.add.ipv4.subnet.for.guest.network.failed": "Failed to add IPv4 subnet for guest network", +"message.add.ipv4.subnet.for.guest.network.processing": "Adding IPv4 subnet for guest network...", "message.add.ip.v6.prefix.processing": "Adding IPv6 Prefix...", "message.add.ip.v6.firewall.rule.failed": "Failed to add IPv6 firewall rule", "message.add.ip.v6.firewall.rule.processing": "Adding IPv6 firewall rule...", @@ -2699,6 +2748,9 @@ "message.add.private.gateway.processing": "Adding private gateway...", "message.add.resource.description": "Add infrastructure resources", "message.add.resource.hint": "Add infrastructure resources - pods, clusters, primary/secondary storages.", +"message.add.routing.firewall.rule.failed": "Failed to add IPv4 Routing firewall rule", +"message.add.routing.firewall.rule.processing": "Adding IPv4 Routing firewall rule...", +"message.add.routing.firewall.rule.success": "Added IPv4 Routing firewall rule", "message.add.rule.failed": "Failed to add new rule.", "message.add.rule.processing": "Adding new security-group rule...", "message.add.secondary.ipaddress.processing": "Add secondary IP address...", @@ -2870,6 +2922,7 @@ "message.delete.acl.rule": "Remove ACL rule", "message.delete.acl.rule.failed": "Failed to remove ACL rule.", "message.delete.affinity.group": "Please confirm that you would like to remove this affinity group.", +"message.delete.asn.range": "Successfully deleted ASN Range", "message.delete.backup": "Are you sure you want to delete the backup?", "message.delete.failed": "Delete fail", "message.delete.gateway": "Please confirm you want to delete the gateway.", @@ -2972,6 +3025,8 @@ "message.error.apply.tungsten.tag": "Applying Tag failed", "message.error.binaries.iso.url": "Please enter binaries ISO URL.", "message.error.bucket": "Please enter bucket", +"message.error.cidr": "CIDR is required", +"message.error.cidr.or.cidrsize": "CIDR or cidr size is required", "message.error.cloudian.console": "Single-Sign-On failed for Cloudian management console. Please ask your administrator to fix integration issues.", "message.error.cluster.description": "Please enter Kubernetes cluster description.", "message.error.cluster.name": "Please enter cluster name.", @@ -2980,6 +3035,7 @@ "message.error.current.password": "Please enter current password.", "message.error.custom.disk.size": "Please enter custom disk size.", "message.error.date": "Please select a date.", +"message.error.delete.asnrange": "Deleting AS Range", "message.error.delete.interface.static.route": "Removing interface Static Route failed", "message.error.delete.network.static.route": "Removing Network Static Route failed", "message.error.delete.tungsten.policy.rule": "Deleting Policy rule failed", @@ -2993,6 +3049,7 @@ "message.error.duration.less.than.interval": "The duration in Autoscale policy cannot be less than interval", "message.error.enable.saml": "Unable to find Users IDs to enable SAML single sign on, kindly enable it manually.", "message.error.end.date.and.time": "Please select an end date and time.", +"message.error.endasn": "Please enter end AS Range", "message.error.endip": "Please enter end IP.", "message.error.gateway": "Please enter gateway.", "message.error.host.name": "Please enter host name.", @@ -3031,6 +3088,7 @@ "message.error.nexus1000v.password": "Please enter Nexus 1000v password.", "message.error.nexus1000v.username": "Please enter Nexus 1000v username.", "message.error.number": "Please enter a valid number.", +"message.error.parent.subnet": "Please choose a parent subnet", "message.error.password": "Enter your password", "message.error.path": "Please enter path", "message.error.provide.setting": "Must provide a valid key and value for setting", @@ -3074,6 +3132,7 @@ "message.error.specify.stickiness.method": "Please specify a stickiness method", "message.error.specify.sticky.name": "Please specify a sticky name.", "message.error.sr.namelabel": "Please enter SR Name-Label.", +"message.error.startasn": "Please enter start AS Range", "message.error.start.date.and.time": "Please select the start date and time!", "message.error.startip": "Please enter start IP.", "message.error.storage.tags": "Please enter storage tags.", @@ -3279,6 +3338,9 @@ "message.remove.port.forward.failed": "Removing port forwarding rule failed", "message.remove.router.table.from.interface": "Please confirm that you want to remove Route Table from this NIC", "message.remove.router.table.from.interface.failed": "Removing Router Table from interface failed", +"message.remove.routing.firewall.rule.failed": "Failed to remove IPv4 Routing firewall rule", +"message.remove.routing.firewall.rule.processing": "Removing IPv4 Routing firewall rule...", +"message.remove.routing.firewall.rule.success": "Removed IPv4 Routing firewall rule", "message.remove.rule.failed": "Failed to delete rule", "message.remove.secondary.ipaddress.processing": "Removing secondary IP address...", "message.remove.securitygroup.rule.processing": "Deleting security-group rule...", @@ -3309,6 +3371,7 @@ "message.scaleup.policy.name.continue": "Please input a name to ScaleUp policy to continue", "message.select.a.zone": "A zone typically corresponds to a single datacenter. Multiple zones help make the cloud more reliable by providing physical isolation and redundancy.", "message.select.affinity.groups": "Please select any affinity groups you want this Instance to belong to:", +"message.select.bgp.peers": "Please select / deselect the BGP peers associated to the network or VPC:", "message.select.deselect.desired.options": "Please select / deselect the desired options", "message.select.deselect.to.sort": "Please select / deselect to sort the values", "message.select.destination.image.stores": "Please select Image Store(s) to which data is to be migrated to", @@ -3344,11 +3407,14 @@ "message.step.4.continue": "Please select at least one Network to continue.", "message.step.license.agreements.continue": "Please accept all license agreements to continue.", "message.success.acquire.ip": "Successfully acquired IP", +"message.success.add.bgp.peer": "Successfully added new BGP peer", "message.success.add.egress.rule": "Successfully added new egress rule", "message.success.add.firewall.rule": "Successfully added new firewall rule", "message.success.add.guest.network": "Successfully created guest Network", "message.success.add.interface.static.route": "Successfully added interface Static Route", "message.success.add.iprange": "Successfully added IP range", +"message.success.add.ipv4.subnet": "Successfully added IPv4 subnet", +"message.success.add.ipv4.subnet.for.guest.network": "Successfully added IPv4 subnet for guest network", "message.success.add.ip.v6.prefix": "Successfully added IPv6 Prefix", "message.success.add.kuberversion": "Successfully added Kubernetes version", "message.success.add.logical.router": "Successfully added Logical Router", @@ -3379,6 +3445,7 @@ "message.success.assigned.vms": "Successfully assigned Instances", "message.success.certificate.upload": "Certificate successfully uploaded", "message.success.change.affinity.group": "Successfully changed affinity groups", +"message.success.change.bgp.peers": "Successfully changed BGP peers", "message.success.change.offering": "Successfully changed offering", "message.success.change.password": "Successfully changed password for User", "message.success.clear.webhook.deliveries": "Successfully cleared webhook deliveries", @@ -3389,6 +3456,7 @@ "message.success.config.vm.schedule": "Successfully configured Instance schedule", "message.success.copy.clipboard": "Successfully copied to clipboard", "message.success.create.account": "Successfully created Account", +"message.success.create.asnrange": "Successfully created AS Range", "message.success.create.bucket": "Successfully created bucket", "message.success.create.sharedfs": "Successfully created Shared FileSystem", "message.success.create.internallb": "Successfully created Internal Load Balancer", @@ -3401,11 +3469,16 @@ "message.success.create.user": "Successfully created User", "message.success.create.volume": "Successfully created volume", "message.success.create.webhook": "Successfully created Webhook", +"message.success.dedicate.bgp.peer": "Successfully dedicated BGP peer", +"message.success.dedicate.ipv4.subnet": "Successfully dedicated IPv4 subnet", "message.success.delete": "Successfully deleted", +"message.success.delete.asnrange": "Successfully deleted AS Range", "message.success.delete.acl.rule": "Successfully removed ACL rule", "message.success.delete.backup.schedule": "Successfully deleted configure Instance backup schedule", +"message.success.delete.bgp.peer": "Successfully deleted BGP peer", "message.success.delete.icon": "Successfully deleted icon of", "message.success.delete.interface.static.route": "Successfully removed interface Static Route", +"message.success.delete.ipv4.subnet": "Successfully removed IPv4 subnet", "message.success.delete.network.static.route": "Successfully removed Network Static Route", "message.success.delete.node": "Successfully deleted node", "message.success.delete.snapshot.policy": "Successfully deleted Snapshot policy", @@ -3433,6 +3506,8 @@ "message.success.register.template": "Successfully registered Template", "message.success.register.user.data": "Successfully registered Userdata", "message.success.release.ip": "Successfully released IP", +"message.success.release.dedicated.bgp.peer": "Successfully released dedicated BGP peer", +"message.success.release.dedicated.ipv4.subnet": "Successfully released dedicated IPv4 subnet", "message.success.remove.egress.rule": "Successfully removed egress rule", "message.success.remove.objectstore.objects": "Successfully removed selected object(s)", "message.success.remove.objectstore.directory": "Successfully removed selected directory", @@ -3455,11 +3530,13 @@ "message.success.scale.kubernetes": "Successfully scaled Kubernetes cluster", "message.success.unmanage.instance": "Successfully unmanaged Instance", "message.success.unmanage.volume": "Successfully unmanaged Volume", +"message.success.update.bgp.peer": "Successfully updated BGP peer", "message.success.update.bucket": "Successfully updated bucket", "message.success.update.condition": "Successfully updated condition", "message.success.update.sharedfs": "Successfully updated Shared FileSystem", "message.success.update.ipaddress": "Successfully updated IP address", "message.success.update.iprange": "Successfully updated IP range", +"message.success.update.ipv4.subnet": "Successfully updated IPv4 subnet", "message.success.update.kubeversion": "Successfully updated Kubernetes supported version", "message.success.update.network": "Successfully updated Network", "message.success.update.template": "Successfully updated Template", @@ -3488,6 +3565,7 @@ "message.update.autoscale.vm.profile.failed": "Failed to update autoscale Instance profile", "message.update.condition.failed": "Failed to update condition", "message.update.condition.processing": "Updating condition...", +"message.update.failed": "Update failed", "message.test.webhook.delivery": "Test delivery to the Webhook with an optional payload", "message.two.factor.authorization.failed": "Unable to verify 2FA with provided code, please retry.", "message.two.fa.auth": "Open the two-factor authentication app on your mobile device to view your authentication code.", @@ -3585,6 +3663,7 @@ "message.zone.detail.hint": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.", "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.bgp.peers.null": "Please note, if no BGP peers are selected, the VR will connect to
(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;
(2) all BGP peers the owner can access, otherwise.
", "message.bucket.delete": "Please confirm that you want to delete this Bucket", "migrate.from": "Migrate from", "migrate.to": "Migrate to", diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index 017d304e39b..3f6f37aa08b 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -23,6 +23,11 @@ + + + + + -